1
0
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:
Matthew Brown 2017-01-31 01:35:44 -05:00
parent 7f8a3d798e
commit ce93c4ef2d
4 changed files with 116 additions and 12 deletions

View File

@ -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';
}
}
}

View File

@ -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()) {

View File

@ -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);
}
}

View File

@ -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();
}
}