mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Allow @psalm-assert to propagate member assertions to the caller's context (#2100)
This change injects method call assertions that involve member variables to the caller context by replacing `$this->` with the lhs of the member call. Fixes: #2099
This commit is contained in:
parent
071fae98b2
commit
ae2c6715a7
@ -670,6 +670,7 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
if ($function_storage->assertions && $stmt->name instanceof PhpParser\Node\Name) {
|
||||
self::applyAssertionsToContext(
|
||||
$stmt->name,
|
||||
null,
|
||||
$function_storage->assertions,
|
||||
$stmt->args,
|
||||
$generic_params ?: [],
|
||||
|
@ -39,11 +39,13 @@ use Psalm\Type\Atomic\TNamedObject;
|
||||
use function is_string;
|
||||
use function array_values;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function get_class;
|
||||
use function strtolower;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function array_search;
|
||||
use function array_keys;
|
||||
use function in_array;
|
||||
@ -1306,6 +1308,7 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
if ($method_storage->assertions) {
|
||||
self::applyAssertionsToContext(
|
||||
$stmt->name,
|
||||
ExpressionAnalyzer::getArrayVarId($stmt->var, null, $statements_analyzer),
|
||||
$method_storage->assertions,
|
||||
$args,
|
||||
$class_template_params ?: [],
|
||||
|
@ -905,6 +905,7 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
if ($method_storage->assertions) {
|
||||
self::applyAssertionsToContext(
|
||||
$stmt->name,
|
||||
null,
|
||||
$method_storage->assertions,
|
||||
$stmt->args,
|
||||
$found_generic_params ?: [],
|
||||
|
@ -50,10 +50,12 @@ use function count;
|
||||
use function in_array;
|
||||
use function array_reverse;
|
||||
use function array_filter;
|
||||
use function is_null;
|
||||
use function is_string;
|
||||
use function assert;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function str_replace;
|
||||
use function is_int;
|
||||
use function substr;
|
||||
use function array_merge;
|
||||
@ -3187,6 +3189,7 @@ class CallAnalyzer
|
||||
/**
|
||||
* @param PhpParser\Node\Identifier|PhpParser\Node\Name $expr
|
||||
* @param \Psalm\Storage\Assertion[] $assertions
|
||||
* @param string $thisName
|
||||
* @param array<int, PhpParser\Node\Arg> $args
|
||||
* @param Context $context
|
||||
* @param array<string, array<string, array{Type\Union}>> $template_type_map,
|
||||
@ -3196,6 +3199,7 @@ class CallAnalyzer
|
||||
*/
|
||||
protected static function applyAssertionsToContext(
|
||||
$expr,
|
||||
?string $thisName,
|
||||
array $assertions,
|
||||
array $args,
|
||||
array $template_type_map,
|
||||
@ -3225,6 +3229,8 @@ class CallAnalyzer
|
||||
}
|
||||
} elseif (isset($context->vars_in_scope[$assertion->var_id])) {
|
||||
$assertion_var_id = $assertion->var_id;
|
||||
} elseif (strpos($assertion->var_id, '$this->') === 0 && !is_null($thisName)) {
|
||||
$assertion_var_id = $thisName . str_replace('$this->', '->', $assertion->var_id);
|
||||
}
|
||||
|
||||
if ($assertion_var_id) {
|
||||
|
@ -685,6 +685,32 @@ class AssertTest extends TestCase
|
||||
}
|
||||
}'
|
||||
],
|
||||
'assertPropertyVisibleOutside' => [
|
||||
'<?php
|
||||
class A {
|
||||
public ?int $x = null;
|
||||
|
||||
public function maybeAssignX() : void {
|
||||
if (rand(0, 0) == 0) {
|
||||
$this->x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-assert !null $this->x
|
||||
*/
|
||||
public function assertProperty() : void {
|
||||
if (is_null($this->x)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
$a->maybeAssignX();
|
||||
$a->assertProperty();
|
||||
echo (2 * $a->x);',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user