From b932769213234a3ee78c39c7505ded5fade3568c Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 16 Aug 2021 16:10:01 +0200 Subject: [PATCH] Support @var above class constants --- src/Analyser/MutatingScope.php | 23 ++++-- src/Analyser/NodeScopeResolver.php | 7 ++ src/PhpDoc/PhpDocBlock.php | 39 +++++++++- src/PhpDoc/PhpDocInheritanceResolver.php | 21 +++++ src/PhpDoc/StubPhpDocProvider.php | 42 ++++++++++ .../BetterReflectionProvider.php | 9 +++ src/Reflection/ClassConstantReflection.php | 16 +++- src/Reflection/ClassReflection.php | 43 +++++++++-- .../Runtime/RuntimeReflectionProvider.php | 7 ++ src/Testing/RuleTestCase.php | 2 + src/Testing/TestCase.php | 2 + src/Testing/TypeInferenceTestCase.php | 2 + tests/PHPStan/Analyser/AnalyserTest.php | 2 + .../Analyser/ClassConstantStubFileTest.php | 37 +++++++++ .../Analyser/NodeScopeResolverTest.php | 2 + tests/PHPStan/Analyser/classConstantStub.stub | 14 ++++ .../Analyser/classConstantStubFiles.neon | 3 + .../data/class-constant-stub-files.php | 20 +++++ .../Analyser/data/class-constant-types.php | 76 +++++++++++++++++++ tests/PHPStan/Broker/BrokerTest.php | 2 + .../Reflection/ClassReflectionTest.php | 10 ++- 21 files changed, 365 insertions(+), 14 deletions(-) create mode 100644 tests/PHPStan/Analyser/ClassConstantStubFileTest.php create mode 100644 tests/PHPStan/Analyser/classConstantStub.stub create mode 100644 tests/PHPStan/Analyser/classConstantStubFiles.neon create mode 100644 tests/PHPStan/Analyser/data/class-constant-stub-files.php create mode 100644 tests/PHPStan/Analyser/data/class-constant-types.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 5e2f28465e..05a5cd0338 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -28,6 +28,7 @@ use PhpParser\NodeFinder; use PHPStan\Node\ExecutionEndNode; use PHPStan\Parser\Parser; +use PHPStan\Reflection\ClassConstantReflection; use PHPStan\Reflection\ClassMemberReflection; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ConstantReflection; @@ -1890,7 +1891,8 @@ private function resolveType(Expr $node): Type if (strtolower($constantName) === 'class') { return new GenericClassStringType(new StaticType($this->getClassReflection())); } - return new MixedType(); + + $namesToResolve[] = 'static'; } } if (in_array(strtolower($constantClass), $namesToResolve, true)) { @@ -1926,15 +1928,26 @@ private function resolveType(Expr $node): Type continue; } - $propertyClassReflection = $this->reflectionProvider->getClass($referencedClass); - if (!$propertyClassReflection->hasConstant($constantName)) { + $constantClassReflection = $this->reflectionProvider->getClass($referencedClass); + if (!$constantClassReflection->hasConstant($constantName)) { continue; } - $constantType = $propertyClassReflection->getConstant($constantName)->getValueType(); + $constantReflection = $constantClassReflection->getConstant($constantName); + if ( + $constantReflection instanceof ClassConstantReflection + && $node->class instanceof Name + && strtolower((string) $node->class) === 'static' + && !$constantClassReflection->isFinal() + && !$constantReflection->hasPhpDocType() + ) { + return new MixedType(); + } + + $constantType = $constantReflection->getValueType(); if ( $constantType instanceof ConstantType - && in_array(sprintf('%s::%s', $propertyClassReflection->getName(), $constantName), $this->dynamicConstantNames, true) + && in_array(sprintf('%s::%s', $constantClassReflection->getName(), $constantName), $this->dynamicConstantNames, true) ) { $constantType = $constantType->generalize(GeneralizePrecision::lessSpecific()); } diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 6b9b4a664b..70d9306458 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -83,6 +83,7 @@ use PHPStan\Php\PhpVersion; use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\ResolvedPhpDocBlock; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\MethodReflection; @@ -138,6 +139,8 @@ class NodeScopeResolver private \PHPStan\Type\FileTypeMapper $fileTypeMapper; + private StubPhpDocProvider $stubPhpDocProvider; + private PhpVersion $phpVersion; private \PHPStan\PhpDoc\PhpDocInheritanceResolver $phpDocInheritanceResolver; @@ -189,6 +192,7 @@ public function __construct( ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider, Parser $parser, FileTypeMapper $fileTypeMapper, + StubPhpDocProvider $stubPhpDocProvider, PhpVersion $phpVersion, PhpDocInheritanceResolver $phpDocInheritanceResolver, FileHelper $fileHelper, @@ -208,6 +212,7 @@ public function __construct( $this->classReflectionExtensionRegistryProvider = $classReflectionExtensionRegistryProvider; $this->parser = $parser; $this->fileTypeMapper = $fileTypeMapper; + $this->stubPhpDocProvider = $stubPhpDocProvider; $this->phpVersion = $phpVersion; $this->phpDocInheritanceResolver = $phpDocInheritanceResolver; $this->fileHelper = $fileHelper; @@ -1490,6 +1495,8 @@ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, Scope $scop return new ClassReflection( $this->reflectionProvider, $this->fileTypeMapper, + $this->stubPhpDocProvider, + $this->phpDocInheritanceResolver, $this->phpVersion, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), diff --git a/src/PhpDoc/PhpDocBlock.php b/src/PhpDoc/PhpDocBlock.php index 880e8a92b4..92fa123c71 100644 --- a/src/PhpDoc/PhpDocBlock.php +++ b/src/PhpDoc/PhpDocBlock.php @@ -144,6 +144,43 @@ public static function resolvePhpDocBlockForProperty( ); } + /** + * @param string|null $docComment + * @param \PHPStan\Reflection\ClassReflection $classReflection + * @param string|null $trait + * @param string $constantName + * @param string $file + * @param bool|null $explicit + * @param array $originalPositionalParameterNames + * @param array $newPositionalParameterNames + * @return self + */ + public static function resolvePhpDocBlockForConstant( + ?string $docComment, + ClassReflection $classReflection, + ?string $trait, // unused + string $constantName, + string $file, + ?bool $explicit, + array $originalPositionalParameterNames, // unused + array $newPositionalParameterNames // unused + ): self + { + return self::resolvePhpDocBlockTree( + $docComment, + $classReflection, + null, + $constantName, + $file, + 'hasConstant', + 'getConstant', + __FUNCTION__, + $explicit, + [], + [] + ); + } + /** * @param string|null $docComment * @param \PHPStan\Reflection\ClassReflection $classReflection @@ -336,7 +373,7 @@ private static function resolvePhpDocBlockFromClass( ): ?self { if ($classReflection->getFileNameWithPhpDocs() !== null && $classReflection->$hasMethodName($name)) { - /** @var \PHPStan\Reflection\PropertyReflection|\PHPStan\Reflection\MethodReflection $parentReflection */ + /** @var \PHPStan\Reflection\PropertyReflection|\PHPStan\Reflection\MethodReflection|\PHPStan\Reflection\ConstantReflection $parentReflection */ $parentReflection = $classReflection->$getMethodName($name); if ($parentReflection->isPrivate()) { return null; diff --git a/src/PhpDoc/PhpDocInheritanceResolver.php b/src/PhpDoc/PhpDocInheritanceResolver.php index 67c86af318..9ad0cea135 100644 --- a/src/PhpDoc/PhpDocInheritanceResolver.php +++ b/src/PhpDoc/PhpDocInheritanceResolver.php @@ -39,6 +39,27 @@ public function resolvePhpDocForProperty( return $this->docBlockTreeToResolvedDocBlock($phpDocBlock, $declaringTraitName, null); } + public function resolvePhpDocForConstant( + ?string $docComment, + ClassReflection $classReflection, + string $classReflectionFileName, + string $constantName + ): ResolvedPhpDocBlock + { + $phpDocBlock = PhpDocBlock::resolvePhpDocBlockForConstant( + $docComment, + $classReflection, + null, + $constantName, + $classReflectionFileName, + null, + [], + [] + ); + + return $this->docBlockTreeToResolvedDocBlock($phpDocBlock, null, null); + } + /** * @param string|null $docComment * @param string $fileName diff --git a/src/PhpDoc/StubPhpDocProvider.php b/src/PhpDoc/StubPhpDocProvider.php index 75ca491d10..c4c3619e84 100644 --- a/src/PhpDoc/StubPhpDocProvider.php +++ b/src/PhpDoc/StubPhpDocProvider.php @@ -27,6 +27,9 @@ class StubPhpDocProvider /** @var array> */ private array $propertyMap = []; + /** @var array> */ + private array $constantMap = []; + /** @var array> */ private array $methodMap = []; @@ -46,6 +49,9 @@ class StubPhpDocProvider /** @var array> */ private array $knownPropertiesDocComments = []; + /** @var array> */ + private array $knownConstantsDocComments = []; + /** @var array> */ private array $knownMethodsDocComments = []; @@ -122,6 +128,32 @@ public function findPropertyPhpDoc(string $className, string $propertyName): ?Re return null; } + public function findClassConstantPhpDoc(string $className, string $constantName): ?ResolvedPhpDocBlock + { + if (!$this->isKnownClass($className)) { + return null; + } + + if (array_key_exists($constantName, $this->constantMap[$className])) { + return $this->constantMap[$className][$constantName]; + } + + if (array_key_exists($constantName, $this->knownConstantsDocComments[$className])) { + [$file, $docComment] = $this->knownConstantsDocComments[$className][$constantName]; + $this->constantMap[$className][$constantName] = $this->fileTypeMapper->getResolvedPhpDoc( + $file, + $className, + null, + null, + $docComment + ); + + return $this->constantMap[$className][$constantName]; + } + + return null; + } + /** * @param string $className * @param string $methodName @@ -306,7 +338,9 @@ private function initializeKnownElementNode(string $stubFile, Node $node): void $this->methodMap[$className] = []; $this->propertyMap[$className] = []; + $this->constantMap[$className] = []; $this->knownPropertiesDocComments[$className] = []; + $this->knownConstantsDocComments[$className] = []; $this->knownMethodsDocComments[$className] = []; foreach ($node->stmts as $stmt) { @@ -319,6 +353,14 @@ private function initializeKnownElementNode(string $stubFile, Node $node): void } $this->knownPropertiesDocComments[$className][$property->name->toString()] = [$stubFile, $docComment->getText()]; } + } elseif ($stmt instanceof Node\Stmt\ClassConst) { + foreach ($stmt->consts as $const) { + if ($docComment === null) { + $this->constantMap[$className][$const->name->toString()] = null; + continue; + } + $this->knownConstantsDocComments[$className][$const->name->toString()] = [$stubFile, $docComment->getText()]; + } } elseif ($stmt instanceof Node\Stmt\ClassMethod) { if ($docComment === null) { $this->methodMap[$className][$stmt->name->toString()] = null; diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 35cf41a275..0e6d7a629d 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -21,6 +21,7 @@ use PHPStan\File\FileHelper; use PHPStan\File\RelativePathHelper; use PHPStan\Php\PhpVersion; +use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\PhpDoc\Tag\ParamTag; use PHPStan\Reflection\ClassNameHelper; @@ -52,6 +53,8 @@ class BetterReflectionProvider implements ReflectionProvider private \PHPStan\Type\FileTypeMapper $fileTypeMapper; + private PhpDocInheritanceResolver $phpDocInheritanceResolver; + private PhpVersion $phpVersion; private \PHPStan\Reflection\SignatureMap\NativeFunctionReflectionProvider $nativeFunctionReflectionProvider; @@ -84,6 +87,7 @@ public function __construct( ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider, ClassReflector $classReflector, FileTypeMapper $fileTypeMapper, + PhpDocInheritanceResolver $phpDocInheritanceResolver, PhpVersion $phpVersion, NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, StubPhpDocProvider $stubPhpDocProvider, @@ -101,6 +105,7 @@ public function __construct( $this->classReflectionExtensionRegistryProvider = $classReflectionExtensionRegistryProvider; $this->classReflector = $classReflector; $this->fileTypeMapper = $fileTypeMapper; + $this->phpDocInheritanceResolver = $phpDocInheritanceResolver; $this->phpVersion = $phpVersion; $this->nativeFunctionReflectionProvider = $nativeFunctionReflectionProvider; $this->stubPhpDocProvider = $stubPhpDocProvider; @@ -155,6 +160,8 @@ public function getClass(string $className): ClassReflection $classReflection = new ClassReflection( $this->reflectionProviderProvider->getReflectionProvider(), $this->fileTypeMapper, + $this->stubPhpDocProvider, + $this->phpDocInheritanceResolver, $this->phpVersion, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), @@ -227,6 +234,8 @@ public function getAnonymousClassReflection(\PhpParser\Node\Stmt\Class_ $classNo self::$anonymousClasses[$className] = new ClassReflection( $this->reflectionProviderProvider->getReflectionProvider(), $this->fileTypeMapper, + $this->stubPhpDocProvider, + $this->phpDocInheritanceResolver, $this->phpVersion, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), diff --git a/src/Reflection/ClassConstantReflection.php b/src/Reflection/ClassConstantReflection.php index cdebf0dc6b..c5f261ed83 100644 --- a/src/Reflection/ClassConstantReflection.php +++ b/src/Reflection/ClassConstantReflection.php @@ -13,6 +13,8 @@ class ClassConstantReflection implements ConstantReflection private \ReflectionClassConstant $reflection; + private ?Type $phpDocType; + private ?string $deprecatedDescription; private bool $isDeprecated; @@ -24,6 +26,7 @@ class ClassConstantReflection implements ConstantReflection public function __construct( ClassReflection $declaringClass, \ReflectionClassConstant $reflection, + ?Type $phpDocType, ?string $deprecatedDescription, bool $isDeprecated, bool $isInternal @@ -31,6 +34,7 @@ public function __construct( { $this->declaringClass = $declaringClass; $this->reflection = $reflection; + $this->phpDocType = $phpDocType; $this->deprecatedDescription = $deprecatedDescription; $this->isDeprecated = $isDeprecated; $this->isInternal = $isInternal; @@ -59,11 +63,21 @@ public function getValue() return $this->reflection->getValue(); } + public function hasPhpDocType(): bool + { + return $this->phpDocType !== null; + } + public function getValueType(): Type { if ($this->valueType === null) { - $this->valueType = ConstantTypeHelper::getTypeFromValue($this->getValue()); + if ($this->phpDocType === null) { + $this->valueType = ConstantTypeHelper::getTypeFromValue($this->getValue()); + } else { + $this->valueType = $this->phpDocType; + } } + return $this->valueType; } diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 34534395c2..b17994c62a 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -5,7 +5,9 @@ use Attribute; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass; use PHPStan\Php\PhpVersion; +use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\ResolvedPhpDocBlock; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\PhpDoc\Tag\ExtendsTag; use PHPStan\PhpDoc\Tag\ImplementsTag; use PHPStan\PhpDoc\Tag\MethodTag; @@ -36,6 +38,10 @@ class ClassReflection implements ReflectionWithFilename private \PHPStan\Type\FileTypeMapper $fileTypeMapper; + private StubPhpDocProvider $stubPhpDocProvider; + + private PhpDocInheritanceResolver $phpDocInheritanceResolver; + private PhpVersion $phpVersion; /** @var \PHPStan\Reflection\PropertiesClassReflectionExtension[] */ @@ -122,6 +128,8 @@ class ClassReflection implements ReflectionWithFilename public function __construct( ReflectionProvider $reflectionProvider, FileTypeMapper $fileTypeMapper, + StubPhpDocProvider $stubPhpDocProvider, + PhpDocInheritanceResolver $phpDocInheritanceResolver, PhpVersion $phpVersion, array $propertiesClassReflectionExtensions, array $methodsClassReflectionExtensions, @@ -135,6 +143,8 @@ public function __construct( { $this->reflectionProvider = $reflectionProvider; $this->fileTypeMapper = $fileTypeMapper; + $this->stubPhpDocProvider = $stubPhpDocProvider; + $this->phpDocInheritanceResolver = $phpDocInheritanceResolver; $this->phpVersion = $phpVersion; $this->propertiesClassReflectionExtensions = $propertiesClassReflectionExtensions; $this->methodsClassReflectionExtensions = $methodsClassReflectionExtensions; @@ -777,21 +787,40 @@ public function getConstant(string $name): ConstantReflection $deprecatedDescription = null; $isDeprecated = false; $isInternal = false; - $declaringClass = $reflectionConstant->getDeclaringClass(); + $declaringClass = $this->reflectionProvider->getClass($reflectionConstant->getDeclaringClass()->getName()); $fileName = $declaringClass->getFileName(); - if ($reflectionConstant->getDocComment() !== false && $fileName !== false) { - $docComment = $reflectionConstant->getDocComment(); - $className = $declaringClass->getName(); - $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($fileName, $className, null, null, $docComment); + $phpDocType = null; + $resolvedPhpDoc = $this->stubPhpDocProvider->findClassConstantPhpDoc( + $declaringClass->getName(), + $name + ); + if ($resolvedPhpDoc === null && $fileName !== false) { + $docComment = null; + if ($reflectionConstant->getDocComment() !== false) { + $docComment = $reflectionConstant->getDocComment(); + } + $resolvedPhpDoc = $this->phpDocInheritanceResolver->resolvePhpDocForConstant( + $docComment, + $declaringClass, + $fileName, + $name + ); + } + if ($resolvedPhpDoc !== null) { $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; $isDeprecated = $resolvedPhpDoc->isDeprecated(); $isInternal = $resolvedPhpDoc->isInternal(); + $varTags = $resolvedPhpDoc->getVarTags(); + if (isset($varTags[0]) && count($varTags) === 1) { + $phpDocType = $varTags[0]->getType(); + } } $this->constants[$name] = new ClassConstantReflection( - $this->reflectionProvider->getClass($declaringClass->getName()), + $declaringClass, $reflectionConstant, + $phpDocType, $deprecatedDescription, $isDeprecated, $isInternal @@ -1061,6 +1090,8 @@ public function withTypes(array $types): self return new self( $this->reflectionProvider, $this->fileTypeMapper, + $this->stubPhpDocProvider, + $this->phpDocInheritanceResolver, $this->phpVersion, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, diff --git a/src/Reflection/Runtime/RuntimeReflectionProvider.php b/src/Reflection/Runtime/RuntimeReflectionProvider.php index 7696136a6f..d4f832d1ff 100644 --- a/src/Reflection/Runtime/RuntimeReflectionProvider.php +++ b/src/Reflection/Runtime/RuntimeReflectionProvider.php @@ -7,6 +7,7 @@ use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider; use PHPStan\Php\PhpVersion; +use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\PhpDoc\Tag\ParamTag; use PHPStan\Reflection\ClassNameHelper; @@ -42,6 +43,8 @@ class RuntimeReflectionProvider implements ReflectionProvider private StubPhpDocProvider $stubPhpDocProvider; + private PhpDocInheritanceResolver $phpDocInheritanceResolver; + private PhpStormStubsSourceStubber $phpStormStubsSourceStubber; /** @var \PHPStan\Reflection\FunctionReflection[] */ @@ -61,6 +64,7 @@ public function __construct( ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider, FunctionReflectionFactory $functionReflectionFactory, FileTypeMapper $fileTypeMapper, + PhpDocInheritanceResolver $phpDocInheritanceResolver, PhpVersion $phpVersion, NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, StubPhpDocProvider $stubPhpDocProvider, @@ -71,6 +75,7 @@ public function __construct( $this->classReflectionExtensionRegistryProvider = $classReflectionExtensionRegistryProvider; $this->functionReflectionFactory = $functionReflectionFactory; $this->fileTypeMapper = $fileTypeMapper; + $this->phpDocInheritanceResolver = $phpDocInheritanceResolver; $this->phpVersion = $phpVersion; $this->nativeFunctionReflectionProvider = $nativeFunctionReflectionProvider; $this->stubPhpDocProvider = $stubPhpDocProvider; @@ -154,6 +159,8 @@ private function getClassFromReflection(\ReflectionClass $reflectionClass, strin $classReflection = new ClassReflection( $this->reflectionProviderProvider->getReflectionProvider(), $this->fileTypeMapper, + $this->stubPhpDocProvider, + $this->phpDocInheritanceResolver, $this->phpVersion, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index 01f657065a..8d6d7f6611 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -18,6 +18,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\PhpDocNodeResolver; use PHPStan\PhpDoc\PhpDocStringResolver; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; use PHPStan\Rules\Registry; use PHPStan\Rules\Rule; @@ -77,6 +78,7 @@ private function getAnalyser(): Analyser $this->getClassReflectionExtensionRegistryProvider(), $this->getParser(), $fileTypeMapper, + self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), $phpDocInheritanceResolver, $fileHelper, diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index ec3ccb90e1..bac8ae96cf 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -238,6 +238,7 @@ private function createRuntimeReflectionProvider( $classReflectionExtensionRegistryProvider, $functionReflectionFactory, $fileTypeMapper, + self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(NativeFunctionReflectionProvider::class), self::getContainer()->getByType(StubPhpDocProvider::class), @@ -393,6 +394,7 @@ private function createStaticReflectionProvider( $classReflectionExtensionRegistryProvider, $classReflector, $fileTypeMapper, + self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(NativeFunctionReflectionProvider::class), self::getContainer()->getByType(StubPhpDocProvider::class), diff --git a/src/Testing/TypeInferenceTestCase.php b/src/Testing/TypeInferenceTestCase.php index 6e5fdea242..06774f7fb8 100644 --- a/src/Testing/TypeInferenceTestCase.php +++ b/src/Testing/TypeInferenceTestCase.php @@ -19,6 +19,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\PhpDocNodeResolver; use PHPStan\PhpDoc\PhpDocStringResolver; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; use PHPStan\TrinaryLogic; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -69,6 +70,7 @@ public function processFile( $this->getClassReflectionExtensionRegistryProvider(), $this->getParser(), $fileTypeMapper, + self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), $phpDocInheritanceResolver, $fileHelper, diff --git a/tests/PHPStan/Analyser/AnalyserTest.php b/tests/PHPStan/Analyser/AnalyserTest.php index 2547c6985e..a98abc07f5 100644 --- a/tests/PHPStan/Analyser/AnalyserTest.php +++ b/tests/PHPStan/Analyser/AnalyserTest.php @@ -17,6 +17,7 @@ use PHPStan\PhpDoc\PhpDocNodeResolver; use PHPStan\PhpDoc\PhpDocStringResolver; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; use PHPStan\Rules\AlwaysFailRule; use PHPStan\Rules\Registry; @@ -507,6 +508,7 @@ private function createAnalyser(bool $reportUnmatchedIgnoredErrors): \PHPStan\An $this->getClassReflectionExtensionRegistryProvider(), $this->getParser(), $fileTypeMapper, + self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), $phpDocInheritanceResolver, $fileHelper, diff --git a/tests/PHPStan/Analyser/ClassConstantStubFileTest.php b/tests/PHPStan/Analyser/ClassConstantStubFileTest.php new file mode 100644 index 0000000000..02081f8433 --- /dev/null +++ b/tests/PHPStan/Analyser/ClassConstantStubFileTest.php @@ -0,0 +1,37 @@ +gatherAssertTypes(__DIR__ . '/data/class-constant-stub-files.php'); + } + + /** + * @dataProvider dataFileAsserts + * @param string $assertType + * @param string $file + * @param mixed ...$args + */ + public function testFileAsserts( + string $assertType, + string $file, + ...$args + ): void + { + $this->assertFileAsserts($assertType, $file, ...$args); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/classConstantStubFiles.neon', + ]; + } + +} diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 38df6cf224..00dc0ffa27 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -461,6 +461,8 @@ public function dataFileAsserts(): iterable } else { yield from $this->gatherAssertTypes(__DIR__ . '/data/mb_substitute_character.php'); } + + yield from $this->gatherAssertTypes(__DIR__ . '/data/class-constant-types.php'); } /** diff --git a/tests/PHPStan/Analyser/classConstantStub.stub b/tests/PHPStan/Analyser/classConstantStub.stub new file mode 100644 index 0000000000..b88ee8b705 --- /dev/null +++ b/tests/PHPStan/Analyser/classConstantStub.stub @@ -0,0 +1,14 @@ +createMock(FunctionReflectionFactory::class), new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper), + self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(NativeFunctionReflectionProvider::class), self::getContainer()->getByType(StubPhpDocProvider::class), diff --git a/tests/PHPStan/Reflection/ClassReflectionTest.php b/tests/PHPStan/Reflection/ClassReflectionTest.php index 2a74d56c8d..48cce715eb 100644 --- a/tests/PHPStan/Reflection/ClassReflectionTest.php +++ b/tests/PHPStan/Reflection/ClassReflectionTest.php @@ -8,6 +8,8 @@ use Attributes\IsNotAttribute; use PHPStan\Broker\Broker; use PHPStan\Php\PhpVersion; +use PHPStan\PhpDoc\PhpDocInheritanceResolver; +use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Type\FileTypeMapper; use WrongClassConstantFile\SecuredRouter; @@ -32,7 +34,9 @@ public function testHasTraitUse(string $className, bool $has): void { $broker = $this->createMock(Broker::class); $fileTypeMapper = $this->createMock(FileTypeMapper::class); - $classReflection = new ClassReflection($broker, $fileTypeMapper, new PhpVersion(PHP_VERSION_ID), [], [], $className, new \ReflectionClass($className), null, null, null); + $stubPhpDocProvider = $this->createMock(StubPhpDocProvider::class); + $phpDocInheritanceResolver = $this->createMock(PhpDocInheritanceResolver::class); + $classReflection = new ClassReflection($broker, $fileTypeMapper, $stubPhpDocProvider, $phpDocInheritanceResolver, new PhpVersion(PHP_VERSION_ID), [], [], $className, new \ReflectionClass($className), null, null, null); $this->assertSame($has, $classReflection->hasTraitUse(\HasTraitUse\FooTrait::class)); } @@ -95,10 +99,14 @@ public function testClassHierarchyDistances( { $broker = $this->createReflectionProvider(); $fileTypeMapper = $this->createMock(FileTypeMapper::class); + $stubPhpDocProvider = $this->createMock(StubPhpDocProvider::class); + $phpDocInheritanceResolver = $this->createMock(PhpDocInheritanceResolver::class); $classReflection = new ClassReflection( $broker, $fileTypeMapper, + $stubPhpDocProvider, + $phpDocInheritanceResolver, new PhpVersion(PHP_VERSION_ID), [], [],