diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index 1c8bcb37f..a12c5114d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -1249,9 +1249,16 @@ class AssertionFinder $if_types[$var_id] = [[$assertion->rule[0][0]]]; } } elseif (\is_string($assertion->var_id) - && $expr instanceof PhpParser\Node\Expr\MethodCall + && ( + $expr instanceof PhpParser\Node\Expr\MethodCall + || $expr instanceof PhpParser\Node\Expr\StaticCall + ) ) { - $if_types[$assertion->var_id] = [[$assertion->rule[0][0]]]; + $var_id = $assertion->var_id; + if (strpos($var_id, 'self::') === 0) { + $var_id = $this_class_name . '::' . substr($var_id, 6); + } + $if_types[$var_id] = [[$assertion->rule[0][0]]]; } if ($if_types) { @@ -1315,9 +1322,16 @@ class AssertionFinder } } } elseif (\is_string($assertion->var_id) - && $expr instanceof PhpParser\Node\Expr\MethodCall + && ( + $expr instanceof PhpParser\Node\Expr\MethodCall + || $expr instanceof PhpParser\Node\Expr\StaticCall + ) ) { - $if_types[$assertion->var_id] = [['!' . $assertion->rule[0][0]]]; + $var_id = $assertion->var_id; + if (strpos($var_id, 'self::') === 0) { + $var_id = $this_class_name . '::' . substr($var_id, 6); + } + $if_types[$var_id] = [['!' . $assertion->rule[0][0]]]; } if ($if_types) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index b179fba4c..73ee268d9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -670,6 +670,11 @@ class CallAnalyzer $assertion_var_id = $thisName; } elseif (strpos($assertion->var_id, '$this->') === 0 && $thisName !== null) { $assertion_var_id = $thisName . str_replace('$this->', '->', $assertion->var_id); + } elseif (strpos($assertion->var_id, 'self::') === 0 && $context->self) { + $assertion_var_id = $context->self . str_replace('self::', '::', $assertion->var_id); + } elseif (strpos($assertion->var_id, '::$') !== false) { + // allow assertions to bring external static props into scope + $assertion_var_id = $assertion->var_id; } elseif (isset($context->vars_in_scope[$assertion->var_id])) { $assertion_var_id = $assertion->var_id; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index 0e5d68603..a3610ce1e 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -388,7 +388,7 @@ class FunctionLikeDocblockParser foreach ($parsed_docblock->tags['psalm-assert'] as $assertion) { $line_parts = CommentAnalyzer::splitDocLine($assertion); - if (count($line_parts) < 2 || $line_parts[1][0] !== '$') { + if (count($line_parts) < 2 || strpos($line_parts[1], '$') === false) { throw new IncorrectDocblockException('Misplaced variable'); } @@ -396,7 +396,7 @@ class FunctionLikeDocblockParser $info->assertions[] = [ 'type' => $line_parts[0], - 'param_name' => substr($line_parts[1], 1), + 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1], ]; } } @@ -405,13 +405,13 @@ class FunctionLikeDocblockParser foreach ($parsed_docblock->tags['psalm-assert-if-true'] as $assertion) { $line_parts = CommentAnalyzer::splitDocLine($assertion); - if (count($line_parts) < 2 || $line_parts[1][0] !== '$') { + if (count($line_parts) < 2 || strpos($line_parts[1], '$') === false) { throw new IncorrectDocblockException('Misplaced variable'); } $info->if_true_assertions[] = [ 'type' => $line_parts[0], - 'param_name' => substr($line_parts[1], 1), + 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1], ]; } } @@ -420,13 +420,13 @@ class FunctionLikeDocblockParser foreach ($parsed_docblock->tags['psalm-assert-if-false'] as $assertion) { $line_parts = CommentAnalyzer::splitDocLine($assertion); - if (count($line_parts) < 2 || $line_parts[1][0] !== '$') { + if (count($line_parts) < 2 || strpos($line_parts[1], '$') === false) { throw new IncorrectDocblockException('Misplaced variable'); } $info->if_false_assertions[] = [ 'type' => $line_parts[0], - 'param_name' => substr($line_parts[1], 1), + 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1], ]; } } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index 50a3fe69a..8559a9353 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -1,14 +1,8 @@ assertions[] = new \Psalm\Storage\Assertion( - '$' . $assertion['param_name'], + (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'], [$assertion_type_parts] ); } @@ -1143,7 +1145,7 @@ class FunctionLikeDocblockScanner } $storage->if_true_assertions[] = new \Psalm\Storage\Assertion( - '$' . $assertion['param_name'], + (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'], [$assertion_type_parts] ); } @@ -1182,7 +1184,7 @@ class FunctionLikeDocblockScanner } $storage->if_false_assertions[] = new \Psalm\Storage\Assertion( - '$' . $assertion['param_name'], + (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'], [$assertion_type_parts] ); } diff --git a/tests/AssertAnnotationTest.php b/tests/AssertAnnotationTest.php index 9e0b1feed..9e5b92e7c 100644 --- a/tests/AssertAnnotationTest.php +++ b/tests/AssertAnnotationTest.php @@ -1,4 +1,5 @@ [ + '' + ], + 'assertIfTrueStaticSelf' => [ + '' + ], + 'assertIfFalseStaticSelf' => [ + '' + ], + 'assertStaticByInheritedMethod' => [ + '' + ], + 'assertInheritedStatic' => [ + '' + ], + 'assertStaticOnUnrelatedClass' => [ + '' + ], ]; } @@ -1480,7 +1605,8 @@ class AssertAnnotationTest extends TestCase if ($bar) {} }', - 'error_message' => 'RedundantConditionGivenDocblockType - src' . DIRECTORY_SEPARATOR . 'somefile.php:19:29', + 'error_message' => 'RedundantConditionGivenDocblockType - src' + . DIRECTORY_SEPARATOR . 'somefile.php:19:29', ], 'assertOneOfStrings' => [ '