mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 12:24:49 +01:00
Make the string concatenation of two known values into a known value (#717)
* Make the string concatenation of two known values into a known value address review comments * Add vim temporary files to .gitignore
This commit is contained in:
parent
6e259bed03
commit
9a9f6d1856
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,4 +4,6 @@ phpunit.xml
|
||||
composer.lock
|
||||
.php_cs.cache
|
||||
.php_cs
|
||||
.*.swp
|
||||
.*.swo
|
||||
/build/logs/
|
||||
|
@ -29,11 +29,13 @@ use Psalm\Type\Atomic\TFalse;
|
||||
use Psalm\Type\Atomic\TFloat;
|
||||
use Psalm\Type\Atomic\TGenericParam;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TLiteralString;
|
||||
use Psalm\Type\Atomic\TMixed;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
use Psalm\Type\Atomic\TNull;
|
||||
use Psalm\Type\Atomic\TNumeric;
|
||||
use Psalm\Type\Reconciler;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
class BinaryOpChecker
|
||||
{
|
||||
@ -1364,5 +1366,14 @@ class BinaryOpChecker
|
||||
}
|
||||
}
|
||||
}
|
||||
// When concatenating two known string literals (with only one possibility),
|
||||
// put the concatenated string into $result_type
|
||||
if ($left_type && $right_type && $left_type->isSingleStringLiteral() && $right_type->isSingleStringLiteral()) {
|
||||
$literal = $left_type->getSingleStringLiteral() . $right_type->getSingleStringLiteral();
|
||||
if (strlen($literal) <= 10000) {
|
||||
// Limit these to 10000 bytes to avoid extremely large union types from repeated concatenations, etc
|
||||
$result_type = new Union([new TLiteralString([$literal => true])]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use Psalm\CodeLocation;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TLiteralString;
|
||||
|
||||
class Union
|
||||
{
|
||||
@ -678,6 +679,42 @@ class Union
|
||||
return $type->type_params[count($type->type_params) - 1]->isSingle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true if this is a string literal with only one possible value
|
||||
* TODO: Is there a better place for this?
|
||||
*/
|
||||
public function isSingleStringLiteral()
|
||||
{
|
||||
if (count($this->types) !== 1) {
|
||||
return false;
|
||||
}
|
||||
$string_type = $this->types['string'] ?? null;
|
||||
if (!($string_type instanceof TLiteralString)) {
|
||||
return false;
|
||||
}
|
||||
return count($string_type->getValues()) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the only string literal represented by this union type
|
||||
* @throws \InvalidArgumentException if isSingleStringLiteral is false
|
||||
*/
|
||||
public function getSingleStringLiteral()
|
||||
{
|
||||
if (count($this->types) !== 1) {
|
||||
throw new \InvalidArgumentException("Not a string literal");
|
||||
}
|
||||
$string_type = $this->types['string'] ?? null;
|
||||
if (!($string_type instanceof TLiteralString)) {
|
||||
throw new \InvalidArgumentException("Not a string literal");
|
||||
}
|
||||
$values = $string_type->getValues();
|
||||
if (count($values) !== 1) {
|
||||
throw new \InvalidArgumentException("Not a string literal");
|
||||
}
|
||||
return (string)key($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StatementsSource $source
|
||||
* @param CodeLocation $code_location
|
||||
|
@ -831,6 +831,13 @@ class TypeAlgebraTest extends TestCase
|
||||
echo $a === false ? "a" : "b";
|
||||
}',
|
||||
],
|
||||
'stringConcatenationTrackedValid' => [
|
||||
'<?php
|
||||
$x = "a";
|
||||
$x = "_" . $x;
|
||||
$array = [$x => 2];
|
||||
echo $array["_a"];',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1012,6 +1019,14 @@ class TypeAlgebraTest extends TestCase
|
||||
if ($a !== $b) {}',
|
||||
'error_message' => 'RedundantCondition',
|
||||
],
|
||||
'stringConcatenationTrackedInvalid' => [
|
||||
'<?php
|
||||
$x = "a";
|
||||
$x = "_" . $x;
|
||||
$array = [$x => 2];
|
||||
echo $array["other"];',
|
||||
'error_message' => 'InvalidArrayOffset',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user