From c2195b8946016fd47aa297da64e14aadd325bf42 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Tue, 1 Oct 2024 18:57:43 +0200 Subject: [PATCH 1/3] Introduce Scope::getMaybeDefinedVariables Fixes phpstan/phpstan#11772 --- src/Analyser/MutatingScope.php | 21 +++++++++++++++++++++ src/Analyser/Scope.php | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 086a09c3c2..d8cc9faf06 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -574,6 +574,27 @@ public function getDefinedVariables(): array return $variables; } + /** + * @api + * @return array + */ + public function getMaybeDefinedVariables(): array + { + $variables = []; + foreach ($this->expressionTypes as $exprString => $holder) { + if (!$holder->getExpr() instanceof Variable) { + continue; + } + if (!$holder->getCertainty()->maybe()) { + continue; + } + + $variables[] = substr($exprString, 1); + } + + return $variables; + } + private function isGlobalVariable(string $variableName): bool { return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true); diff --git a/src/Analyser/Scope.php b/src/Analyser/Scope.php index 0c1682209d..b1fe0cff97 100644 --- a/src/Analyser/Scope.php +++ b/src/Analyser/Scope.php @@ -67,6 +67,11 @@ public function canAnyVariableExist(): bool; */ public function getDefinedVariables(): array; + /** + * @return array + */ + public function getMaybeDefinedVariables(): array; + public function hasConstant(Name $name): bool; public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection; From 8b2edf09bdbd3de43a10878a8ce6cdfd26064cd1 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Wed, 2 Oct 2024 08:35:16 +0200 Subject: [PATCH 2/3] Add `@covers` to ScopeTest By doing this, when you are inside MutatingScope you can press "Go to test" in PHPStorm and it navigates you right to this test. --- tests/PHPStan/Analyser/ScopeTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/PHPStan/Analyser/ScopeTest.php b/tests/PHPStan/Analyser/ScopeTest.php index fe0644cd30..532286ffe7 100644 --- a/tests/PHPStan/Analyser/ScopeTest.php +++ b/tests/PHPStan/Analyser/ScopeTest.php @@ -15,6 +15,9 @@ use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; +/** + * @covers \PHPStan\Analyser\MutatingScope + */ class ScopeTest extends PHPStanTestCase { From e69c8cfe7662cab90cdb9235db68f22b302b9a5c Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Wed, 2 Oct 2024 08:39:09 +0200 Subject: [PATCH 3/3] Add tests for getDefinedVariables and getMaybeDefinedVariables --- tests/PHPStan/Analyser/ScopeTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/PHPStan/Analyser/ScopeTest.php b/tests/PHPStan/Analyser/ScopeTest.php index 532286ffe7..cdad83a96f 100644 --- a/tests/PHPStan/Analyser/ScopeTest.php +++ b/tests/PHPStan/Analyser/ScopeTest.php @@ -5,12 +5,14 @@ use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Name\FullyQualified; use PHPStan\Testing\PHPStanTestCase; +use PHPStan\TrinaryLogic; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\IntegerRangeType; use PHPStan\Type\ObjectType; +use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; @@ -251,4 +253,26 @@ public function testGetConstantType(): void $this->assertSame('int<1, max>', $type->describe(VerbosityLevel::precise())); } + public function testDefinedVariables(): void + { + /** @var ScopeFactory $scopeFactory */ + $scopeFactory = self::getContainer()->getByType(ScopeFactory::class); + $scope = $scopeFactory->create(ScopeContext::create('file.php')) + ->assignVariable('a', new ConstantStringType('a'), new StringType(), TrinaryLogic::createYes()) + ->assignVariable('b', new ConstantStringType('b'), new StringType(), TrinaryLogic::createMaybe()); + + $this->assertSame(['a'], $scope->getDefinedVariables()); + } + + public function testMaybeDefinedVariables(): void + { + /** @var ScopeFactory $scopeFactory */ + $scopeFactory = self::getContainer()->getByType(ScopeFactory::class); + $scope = $scopeFactory->create(ScopeContext::create('file.php')) + ->assignVariable('a', new ConstantStringType('a'), new StringType(), TrinaryLogic::createYes()) + ->assignVariable('b', new ConstantStringType('b'), new StringType(), TrinaryLogic::createMaybe()); + + $this->assertSame(['b'], $scope->getMaybeDefinedVariables()); + } + }