-
Notifications
You must be signed in to change notification settings - Fork 574
Mark class_exists, interface_exists, trait_exists, and enum_exists as having no side effects in function metadata
#5607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2.1.x
Are you sure you want to change the base?
Changes from all commits
910e8bc
978114e
50a2756
a06c890
3567b00
327da33
0d8b316
60bcc54
36db3d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,9 @@ | |
| use PHPStan\Analyser\TypeSpecifierAwareExtension; | ||
| use PHPStan\Analyser\TypeSpecifierContext; | ||
| use PHPStan\DependencyInjection\AutowiredService; | ||
| use PHPStan\Node\Expr\AlwaysRememberedExpr; | ||
| use PHPStan\Reflection\FunctionReflection; | ||
| use PHPStan\Type\BooleanType; | ||
| use PHPStan\Type\ClassStringType; | ||
| use PHPStan\Type\Constant\ConstantBooleanType; | ||
| use PHPStan\Type\Constant\ConstantStringType; | ||
|
|
@@ -47,10 +49,11 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n | |
| $args = $node->getArgs(); | ||
| $argType = $scope->getType($args[0]->value); | ||
| if ($argType instanceof ConstantStringType) { | ||
| $funcCall = new FuncCall(new FullyQualified('class_exists'), [ | ||
| new Arg(new String_(ltrim($argType->getValue(), '\\'))), | ||
| ]); | ||
| return $this->typeSpecifier->create( | ||
| new FuncCall(new FullyQualified('class_exists'), [ | ||
| new Arg(new String_(ltrim($argType->getValue(), '\\'))), | ||
| ]), | ||
| new AlwaysRememberedExpr($funcCall, new BooleanType(), new BooleanType()), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I always wondered what the use-case is for
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I processed this review but have nothing to report. |
||
| new ConstantBooleanType(true), | ||
| $context, | ||
| $scope, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace PHPStan\Rules\Classes; | ||
|
|
||
| use PHPStan\Rules\Rule; | ||
| use PHPStan\Testing\RuleTestCase; | ||
| use function array_merge; | ||
|
|
||
| /** | ||
| * @extends RuleTestCase<InstantiationRule> | ||
| */ | ||
| class InstantiationDoNotRememberPossiblyImpureValuesRuleTest extends RuleTestCase | ||
| { | ||
|
|
||
| protected function getRule(): Rule | ||
| { | ||
| return self::getContainer()->getByType(InstantiationRule::class); | ||
| } | ||
|
|
||
| public function testBug8579(): void | ||
| { | ||
| $this->analyse([__DIR__ . '/data/bug-8579.php'], []); | ||
| } | ||
|
|
||
| public static function getAdditionalConfigFiles(): array | ||
| { | ||
| return array_merge( | ||
| parent::getAdditionalConfigFiles(), | ||
| [ | ||
| __DIR__ . '/doNotRememberPossiblyImpureValues.neon', | ||
| ], | ||
| ); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php | ||
|
|
||
| if (!class_exists('NonexistentClassBug8579')) throw new \Exception('nonexistentclass'); | ||
| $x = new \NonexistentClassBug8579(); | ||
|
|
||
| if (!interface_exists('NonexistentInterfaceBug8579')) throw new \Exception('nonexistentinterface'); | ||
| $x = new \NonexistentInterfaceBug8579(); | ||
|
|
||
| if (!trait_exists('NonexistentTraitBug8579')) throw new \Exception('nonexistenttrait'); | ||
| $x = new \NonexistentTraitBug8579(); | ||
|
|
||
| if (!enum_exists('NonexistentEnumBug8579')) throw new \Exception('nonexistentenum'); | ||
| $x = new \NonexistentEnumBug8579(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| parameters: | ||
| rememberPossiblyImpureFunctionValues: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
per PR title I expect a tests (and fixes) also for
interface_exists,trait_exists, andenum_existsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed. Here's what was done to address staabm's review:
1. Added
interface_exists/trait_exists/enum_existstests (tests/PHPStan/Rules/Classes/data/bug-8579.php): Added instantiation guards using all four*_existsfunctions, not justclass_exists. TheInstantiationDoNotRememberPossiblyImpureValuesRuleTestnow covers all four functions withrememberPossiblyImpureFunctionValues: false.2. Fixed nsrt assertions to actually test the fix (
tests/PHPStan/Analyser/data/do-not-remember-possibly-impure-function-values.php): The previous assertions used unqualifiedclass_exists(...)which produced a different expression key (class_exists(...)) than what the extension stores (\class_exists(...)viaFullyQualified), so they always gotboolregardless of the fix. Changed to\class_exists(...)(fully qualified) so the key matches. The truthy branch now correctly asserts'true'— verified this fails before the fix and passes after.All 12000 tests pass and
make phpstanreports no errors.