Repository navigation
Do not report undefined members of built-in classes when PHP_VERSION_ID narrowing excludes the analysed PHP version - #6945
Conversation
…_ID` narrowing excludes the analysed PHP version - Add `BuiltinClassMemberAvailabilityHelper`: the stubs of built-in classes are filtered for the single analysed `PhpVersion`, so when `Scope::getPhpVersion()` (narrowed by a `PHP_VERSION_ID` check) excludes that version, a member missing from the stubs may exist in the guarded branch. The helper answers this for built-in classes and classes extending them. - Use it before reporting `staticMethod.notFound` (StaticMethodCallCheck), `method.notFound` (MethodCallCheck), `classConstant.notFound` (ClassConstantRule), `property.notFound` (AccessPropertiesCheck) and `staticProperty.notFound` (AccessStaticPropertiesCheck). - Members of user classes are still reported in such branches. - Not covered: unknown built-in classes, functions and global constants. These are reported from many different rules and would need a version-independent stub lookup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VincentLanglet
left a comment
There was a problem hiding this comment.
This fix just ignore the error but loose also the type inference. Shouldn't we load the stubs for those PHP_VERSION_ID conditions ? Is it too complex ?
|
I didn't change any code or push commits for this review. The reviewer asked a design question, and the answer depends on a decision that should be made first. Does this PR lose type inference? No, there was none to lose. I analysed the issue snippet with Could we load the stubs for the narrowed versions? Possibly, but it is a much bigger change than this fix:
My suggestion is to merge this PR as the narrow false-positive fix and do version-specific stub loading as a separate feature. If the reviewer would rather have the full approach in this PR, I can start on it, but it will be a large change. |
Summary
if (PHP_VERSION_ID >= 80400) { ReflectionMethod::createFromMethodName($func); }reported Call to an undefined static method when the analysed PHP version is older than 8.3. Built-in class stubs are filtered for the single analysed PHP version, but the scope inside the guard is narrowed to PHP versions where the method does exist. With this change, PHPStan stops reporting a missing member of a built-in class when the scope's PHP version range excludes the analysed version.Changes
src/Rules/BuiltinClassMemberAvailabilityHelper.php. It returns true when every class in the type is built-in (or extends a built-in class) andScope::getPhpVersion()does not include the analysedPhpVersion.src/Rules/Methods/StaticMethodCallCheck.php(the reported case)src/Rules/Methods/MethodCallCheck.php(instance methods, e.g.ReflectionProperty::isVirtual())src/Rules/Classes/ClassConstantRule.php(e.g.ReflectionProperty::IS_READONLY)src/Rules/Properties/AccessPropertiesCheck.php(e.g.DOMNode::$isConnected)src/Rules/Properties/AccessStaticPropertiesCheck.phpnew Random\Randomizer()behind a guard), functions and global constants. Functions already work because the functionMap is version-independent. Classes and constants are reported from ~20 rules, and handling them would need a version-independent stub lookup, so they are left for a separate change.Root cause
Reflection for built-in classes is built once from phpstorm-stubs filtered by the configured
PhpVersion. The rules then trusted that reflection even in branches wherePHP_VERSION_IDnarrowing has ruled the analysed version out. In those branches the reflection says nothing about which members really exist.Test
tests/PHPStan/Rules/Methods/data/bug-15109.php(used byCallStaticMethodsRuleTestandCallMethodsRuleTest). It contains the issue's snippet verbatim, plus calls guarded byPHP_VERSION_ID >= 90000andPHP_VERSION_ID < 50000onReflectionMethodand on a subclass. These are no longer reported. The same calls without a guard, and calls on user classes inside the guards, are still reported.tests/PHPStan/Rules/Classes/data/bug-15109.php(ClassConstantRuleTest) covers class constants.tests/PHPStan/Rules/Properties/data/bug-15109.php(AccessPropertiesRuleTest,AccessStaticPropertiesRuleTest) covers instance and static properties.Fixes phpstan/phpstan#15109
🤖 Generated with Claude Code