mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Assert smart things when offset is variable
This commit is contained in:
parent
7f8a3d798e
commit
ce93c4ef2d
@ -361,6 +361,23 @@ class AssertionFinder
|
||||
|
||||
if ($var_name) {
|
||||
$if_types[$var_name] = 'isset';
|
||||
} else {
|
||||
// look for any variables we *can* use for an isset assertion
|
||||
$array_root = $isset_var;
|
||||
|
||||
while ($array_root instanceof PhpParser\Node\Expr\ArrayDimFetch && !$var_name) {
|
||||
$array_root = $array_root->var;
|
||||
|
||||
$var_name = ExpressionChecker::getArrayVarId(
|
||||
$array_root,
|
||||
$this_class_name,
|
||||
$source
|
||||
);
|
||||
}
|
||||
|
||||
if ($var_name) {
|
||||
$if_types[$var_name] = '^isset';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,6 +530,10 @@ class TypeChecker
|
||||
$result_var_types = null;
|
||||
|
||||
if ($existing_var_type === null) {
|
||||
if ($new_var_type === '^isset') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($new_var_type === 'isset') {
|
||||
return Type::getMixed();
|
||||
}
|
||||
@ -611,6 +615,18 @@ class TypeChecker
|
||||
return $existing_var_type;
|
||||
}
|
||||
|
||||
if ($new_var_type === '^isset' || $new_var_type === 'isset') {
|
||||
$existing_var_type->removeType('null');
|
||||
|
||||
if (empty($existing_var_type->types)) {
|
||||
// @todo - I think there's a better way to handle this, but for the moment
|
||||
// mixed will have to do.
|
||||
return Type::getMixed();
|
||||
}
|
||||
|
||||
return $existing_var_type;
|
||||
}
|
||||
|
||||
if ($new_var_type[0] === '^') {
|
||||
$new_var_type = substr($new_var_type, 1);
|
||||
}
|
||||
@ -657,18 +673,6 @@ class TypeChecker
|
||||
}
|
||||
}
|
||||
|
||||
if ($new_var_type === 'isset') {
|
||||
$existing_var_type->removeType('null');
|
||||
|
||||
if (empty($existing_var_type->types)) {
|
||||
// @todo - I think there's a better way to handle this, but for the moment
|
||||
// mixed will have to do.
|
||||
return Type::getMixed();
|
||||
}
|
||||
|
||||
return $existing_var_type;
|
||||
}
|
||||
|
||||
$new_type = Type::parseString($new_var_type);
|
||||
|
||||
if ($existing_var_type->isMixed()) {
|
||||
|
@ -931,4 +931,66 @@ class PropertyTypeTest extends PHPUnit_Framework_TestCase
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPropertyArrayIssetAssertionWithVariableOffset()
|
||||
{
|
||||
$this->project_checker->registerFile(
|
||||
getcwd() . '/somefile.php',
|
||||
'<?php
|
||||
function bar(string $s) : void { }
|
||||
|
||||
class A {
|
||||
/** @var array<string, string> */
|
||||
public $a = [];
|
||||
|
||||
private function foo() : void {
|
||||
$b = "hello";
|
||||
|
||||
if (!isset($this->a[$b])) {
|
||||
return;
|
||||
}
|
||||
|
||||
bar($this->a[$b]);
|
||||
}
|
||||
}'
|
||||
);
|
||||
|
||||
$file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testStaticPropertyArrayIssetAssertionWithVariableOffset()
|
||||
{
|
||||
$this->project_checker->registerFile(
|
||||
getcwd() . '/somefile.php',
|
||||
'<?php
|
||||
function bar(string $s) : void { }
|
||||
|
||||
class A {
|
||||
/** @var array<string, string> */
|
||||
public static $a = [];
|
||||
}
|
||||
|
||||
function foo() : void {
|
||||
$b = "hello";
|
||||
|
||||
if (!isset(A::$a[$b])) {
|
||||
return;
|
||||
}
|
||||
|
||||
bar(A::$a[$b]);
|
||||
}'
|
||||
);
|
||||
|
||||
$file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
}
|
||||
|
@ -736,4 +736,25 @@ class TypeReconciliationTest extends PHPUnit_Framework_TestCase
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testUpdateMultipleIssetVarsWithVariableOffset()
|
||||
{
|
||||
$stmts = self::$parser->parse('<?php
|
||||
/** @return void **/
|
||||
function foo(string $s) {}
|
||||
|
||||
$a = rand(0, 1) ? ["hello"] : null;
|
||||
$b = 0;
|
||||
if (isset($a[$b])) {
|
||||
foo($a[$b]);
|
||||
}
|
||||
');
|
||||
|
||||
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user