Skip to content

Commit

Permalink
Support @var above class constants
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 17, 2021
1 parent f7250db commit b932769
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 14 deletions.
23 changes: 18 additions & 5 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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());
}
Expand Down
7 changes: 7 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -138,6 +139,8 @@ class NodeScopeResolver

private \PHPStan\Type\FileTypeMapper $fileTypeMapper;

private StubPhpDocProvider $stubPhpDocProvider;

private PhpVersion $phpVersion;

private \PHPStan\PhpDoc\PhpDocInheritanceResolver $phpDocInheritanceResolver;
Expand Down Expand Up @@ -189,6 +192,7 @@ public function __construct(
ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider,
Parser $parser,
FileTypeMapper $fileTypeMapper,
StubPhpDocProvider $stubPhpDocProvider,
PhpVersion $phpVersion,
PhpDocInheritanceResolver $phpDocInheritanceResolver,
FileHelper $fileHelper,
Expand All @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down
39 changes: 38 additions & 1 deletion src/PhpDoc/PhpDocBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, string> $originalPositionalParameterNames
* @param array<int, string> $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
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 21 additions & 0 deletions src/PhpDoc/PhpDocInheritanceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions src/PhpDoc/StubPhpDocProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class StubPhpDocProvider
/** @var array<string, array<string, ResolvedPhpDocBlock|null>> */
private array $propertyMap = [];

/** @var array<string, array<string, ResolvedPhpDocBlock|null>> */
private array $constantMap = [];

/** @var array<string, array<string, null>> */
private array $methodMap = [];

Expand All @@ -46,6 +49,9 @@ class StubPhpDocProvider
/** @var array<string, array<string, array{string, string}>> */
private array $knownPropertiesDocComments = [];

/** @var array<string, array<string, array{string, string}>> */
private array $knownConstantsDocComments = [];

/** @var array<string, array<string, array{string, string}>> */
private array $knownMethodsDocComments = [];

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions src/Reflection/BetterReflection/BetterReflectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -84,6 +87,7 @@ public function __construct(
ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider,
ClassReflector $classReflector,
FileTypeMapper $fileTypeMapper,
PhpDocInheritanceResolver $phpDocInheritanceResolver,
PhpVersion $phpVersion,
NativeFunctionReflectionProvider $nativeFunctionReflectionProvider,
StubPhpDocProvider $stubPhpDocProvider,
Expand All @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
16 changes: 15 additions & 1 deletion src/Reflection/ClassConstantReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class ClassConstantReflection implements ConstantReflection

private \ReflectionClassConstant $reflection;

private ?Type $phpDocType;

private ?string $deprecatedDescription;

private bool $isDeprecated;
Expand All @@ -24,13 +26,15 @@ class ClassConstantReflection implements ConstantReflection
public function __construct(
ClassReflection $declaringClass,
\ReflectionClassConstant $reflection,
?Type $phpDocType,
?string $deprecatedDescription,
bool $isDeprecated,
bool $isInternal
)
{
$this->declaringClass = $declaringClass;
$this->reflection = $reflection;
$this->phpDocType = $phpDocType;
$this->deprecatedDescription = $deprecatedDescription;
$this->isDeprecated = $isDeprecated;
$this->isInternal = $isInternal;
Expand Down Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit b932769

Please sign in to comment.