Skip to content

Conversation

herndlm
Copy link
Contributor

@herndlm herndlm commented Nov 9, 2024

Added a similar method on Mutatingscope as foreach handling has it already. This should keep expressions in the scope we can safely keep while still removing the initial expressions when polluteScopeWithLoopInitialAssignments is set to false.

Before, it was merging $finalScope with $scope to get rid of initial expressions which has the downside of also getting rid of expressions that only exist in $finalScope because e.g. the loop is iterating at least once and assigns a variable inside.

Closes phpstan/phpstan#9550

Btw a similar thing seems to be going on with foreach and polluteScopeWithAlwaysIterableForeach set to false.

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I'd say this is a new feature so one rule regression test isn't enough. We should have a new NodeScopeResolverTest file that covers all the branches with assertType, assertNativeType and assertVariableCertainty calls.

@herndlm
Copy link
Contributor Author

herndlm commented Nov 10, 2024

Hi, I'd say this is a new feature so one rule regression test isn't enough. We should have a new NodeScopeResolverTest file that covers all the branches with assertType, assertNativeType and assertVariableCertainty calls.

Added more tests that should cover all the new code and also duplicated them to see how it behaves with the default when polluteScopeWithLoopInitialAssignments is true. Was that what you were thinking of?

With my change reverted, tests fail like this
There were 5 failures:

1) PHPStan\Rules\Variables\DefinedVariableRuleTest::testBug9550
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'
+'30: Variable $vStr might not be defined.
 '

/home/martin/PhpstormProjects/phpstan-src/src/Testing/RuleTestCase.php:164
/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php:1077

2) PHPStan\Analyser\ForLoopNoScopePollutionTest::testFileAsserts with data set "/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php:37" ('type', '/home/martin/PhpstormProjects...on.php', 'int', 'mixed', 37)
Expected type int, got type mixed in /home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php on line 37.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'int'
+'mixed'

/home/martin/PhpstormProjects/phpstan-src/src/Testing/TypeInferenceTestCase.php:127
/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/ForLoopNoScopePollutionTest.php:26

3) PHPStan\Analyser\ForLoopNoScopePollutionTest::testFileAsserts with data set "/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php:32" ('type', '/home/martin/PhpstormProjects...on.php', 'int<0, 1>', 'int', 32)
Expected type int<0, 1>, got type int in /home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php on line 32.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'int<0, 1>'
+'int'

/home/martin/PhpstormProjects/phpstan-src/src/Testing/TypeInferenceTestCase.php:127
/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/ForLoopNoScopePollutionTest.php:26

4) PHPStan\Analyser\ForLoopNoScopePollutionTest::testFileAsserts with data set "/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php:36" ('type', '/home/martin/PhpstormProjects...on.php', 'int<0, 1>', 'int', 36)
Expected type int<0, 1>, got type int in /home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php on line 36.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'int<0, 1>'
+'int'

/home/martin/PhpstormProjects/phpstan-src/src/Testing/TypeInferenceTestCase.php:127
/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/ForLoopNoScopePollutionTest.php:26

5) PHPStan\Analyser\ForLoopNoScopePollutionTest::testFileAsserts with data set "/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php:42" ('variableCertainty', '/home/martin/PhpstormProjects...on.php', PHPStan\TrinaryLogic Object (...), PHPStan\TrinaryLogic Object (...), 'variable $c', 42)
Expected Yes, actual certainty of variable $c is Maybe in /home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php on line 42.
Failed asserting that false is true.

/home/martin/PhpstormProjects/phpstan-src/src/Testing/TypeInferenceTestCase.php:136
/home/martin/PhpstormProjects/phpstan-src/tests/PHPStan/Analyser/ForLoopNoScopePollutionTest.php:26

@ondrejmirtes ondrejmirtes merged commit c55aa05 into phpstan:1.12.x Nov 10, 2024
452 checks passed
@ondrejmirtes
Copy link
Member

Thank you!

@herndlm
Copy link
Contributor Author

herndlm commented Nov 10, 2024

ah I just noticed a new line too much after the namespace of the test files :P

also wanted to add the diff between scope pollution on and off:

--- tests/PHPStan/Analyser/nsrt/for-loop.php	2024-11-10 13:55:39.808980414 +0100
+++ tests/PHPStan/Analyser/data/for-loop-no-scope-pollution.php	2024-11-10 13:55:39.685646935 +0100
@@ -1,6 +1,6 @@
 <?php declare(strict_types=1);
 
-namespace ForLoop;
+namespace ForLoopNoScopePollution;
 
 use PHPStan\TrinaryLogic;
 use function PHPStan\Testing\assertNativeType;
@@ -22,7 +22,7 @@
 
 		assertType('int<10, max>', $i);
 		assertNativeType('int<10, max>', $i);
-		assertVariableCertainty(TrinaryLogic::createYes(), $i);
+		assertVariableCertainty(TrinaryLogic::createMaybe(), $i);
 
 		assertType('int<min, 10>', $j);
 		assertNativeType('int<min, 10>', $j);
@@ -53,7 +53,7 @@
 
 		assertType('int<0, max>', $i);
 		assertNativeType('int<0, max>', $i);
-		assertVariableCertainty(TrinaryLogic::createYes(), $i);
+		assertVariableCertainty(TrinaryLogic::createMaybe(), $i);
 
 		assertType('int<min, 10>', $j);
 		assertNativeType('int<min, 10>', $j);
@@ -82,12 +82,12 @@
 			$c = rand(0, 1);
 		}
 
-		assertType('0', $i);
-		assertNativeType('0', $i);
-		assertVariableCertainty(TrinaryLogic::createYes(), $i);
+		assertType('*ERROR*', $i);
+		assertNativeType('*ERROR*', $i);
+		assertVariableCertainty(TrinaryLogic::createNo(), $i);
 
-		assertType('10', $j);
-		assertNativeType('10', $j);
+		assertType('0', $j);
+		assertNativeType('0', $j);
 		assertVariableCertainty(TrinaryLogic::createYes(), $j);
 
 		assertType('int', $a);

@herndlm
Copy link
Contributor Author

herndlm commented Nov 10, 2024

fyi, got the equivalent improvement for foreach almost ready and try to open the PR somewhen today.

@ondrejmirtes
Copy link
Member

Keep them coming :)


assertType('int<0, 1>', $c);
assertNativeType('int', $c);
assertVariableCertainty(TrinaryLogic::createYes(), $c);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this should be maybe :(

@ondrejmirtes
Copy link
Member

Reverted for now.

@mvorisek
Copy link
Contributor

mvorisek commented Nov 10, 2024

As reverted, can you reopen phpstan/phpstan#9550?

@ondrejmirtes
Copy link
Member

I realized the issue - right now this behaviour is connected to polluteScopeWithLoopInitialAssignments but it might be a better idea to have its own $polluteScopeWithAlwaysIterableFor setting?

@herndlm
Copy link
Contributor Author

herndlm commented Nov 10, 2024

Sounds like a good idea I guess. So you mean this could be brought back but only under that new flag? And if so - what about foreach?

The behaviour change of phpstan/phpstan#8270 confused me too a bit hmm.

@ondrejmirtes
Copy link
Member

Yeah, I guess so, let's try it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants