Skip to content

Commit

Permalink
Read native class constant type from php-8-stubs
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Nov 8, 2023
1 parent 88e1140 commit 714bb44
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 32 deletions.
3 changes: 3 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
use PHPStan\Reflection\Php\PhpMethodReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
Expand Down Expand Up @@ -214,6 +215,7 @@ public function __construct(
private readonly FileTypeMapper $fileTypeMapper,
private readonly StubPhpDocProvider $stubPhpDocProvider,
private readonly PhpVersion $phpVersion,
private readonly SignatureMapProvider $signatureMapProvider,
private readonly PhpDocInheritanceResolver $phpDocInheritanceResolver,
private readonly FileHelper $fileHelper,
private readonly TypeSpecifier $typeSpecifier,
Expand Down Expand Up @@ -1696,6 +1698,7 @@ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, string $cla
$this->stubPhpDocProvider,
$this->phpDocInheritanceResolver,
$this->phpVersion,
$this->signatureMapProvider,
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getAllowedSubTypesClassReflectionExtensions(),
Expand Down
4 changes: 4 additions & 0 deletions src/Reflection/BetterReflection/BetterReflectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use PHPStan\Reflection\Php\PhpFunctionReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\SignatureMap\NativeFunctionReflectionProvider;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\Generic\TemplateTypeMap;
Expand Down Expand Up @@ -82,6 +83,7 @@ public function __construct(
private AnonymousClassNameHelper $anonymousClassNameHelper,
private FileHelper $fileHelper,
private PhpStormStubsSourceStubber $phpstormStubsSourceStubber,
private SignatureMapProvider $signatureMapProvider,
)
{
}
Expand Down Expand Up @@ -133,6 +135,7 @@ public function getClass(string $className): ClassReflection
$this->stubPhpDocProvider,
$this->phpDocInheritanceResolver,
$this->phpVersion,
$this->signatureMapProvider,
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getAllowedSubTypesClassReflectionExtensions(),
Expand Down Expand Up @@ -209,6 +212,7 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $
$this->stubPhpDocProvider,
$this->phpDocInheritanceResolver,
$this->phpVersion,
$this->signatureMapProvider,
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getAllowedSubTypesClassReflectionExtensions(),
Expand Down
38 changes: 13 additions & 25 deletions src/Reflection/ClassConstantReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
use PhpParser\Node\Expr;
use PHPStan\BetterReflection\NodeCompiler\Exception\UnableToCompileNode;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionIntersectionType;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionNamedType;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionUnionType;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Type;
use PHPStan\Type\TypehintHelper;
Expand All @@ -18,13 +15,11 @@ class ClassConstantReflection implements ConstantReflection

private ?Type $valueType = null;

private ?Type $finalNativeType = null;

public function __construct(
private InitializerExprTypeResolver $initializerExprTypeResolver,
private ClassReflection $declaringClass,
private ReflectionClassConstant $reflection,
private ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null $nativeType,
private ?Type $nativeType,
private ?Type $phpDocType,
private ?string $deprecatedDescription,
private bool $isDeprecated,
Expand Down Expand Up @@ -78,30 +73,23 @@ public function hasNativeType(): bool

public function getNativeType(): ?Type
{
if ($this->nativeType === null) {
return null;
}

if ($this->finalNativeType !== null) {
return $this->finalNativeType;
}

return $this->finalNativeType = TypehintHelper::decideTypeFromReflection(
$this->nativeType,
null,
$this->declaringClass,
);
return $this->nativeType;
}

public function getValueType(): Type
{
if ($this->valueType === null) {
if ($this->phpDocType !== null || $this->nativeType !== null) {
return $this->valueType = TypehintHelper::decideTypeFromReflection(
$this->nativeType,
$this->phpDocType,
$this->declaringClass,
);
if ($this->phpDocType !== null) {
if ($this->nativeType !== null) {
return $this->valueType = TypehintHelper::decideType(
$this->nativeType,
$this->phpDocType,
);
}

return $this->phpDocType;
} elseif ($this->nativeType !== null) {
return $this->nativeType;
}

$this->valueType = $this->initializerExprTypeResolver->getType($this->getValueExpr(), InitializerExprContext::fromClassReflection($this->declaringClass));
Expand Down
13 changes: 12 additions & 1 deletion src/Reflection/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use PHPStan\PhpDoc\Tag\TypeAliasTag;
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\CircularTypeAliasDefinitionException;
use PHPStan\Type\Constant\ConstantIntegerType;
Expand Down Expand Up @@ -138,6 +139,7 @@ public function __construct(
private StubPhpDocProvider $stubPhpDocProvider,
private PhpDocInheritanceResolver $phpDocInheritanceResolver,
private PhpVersion $phpVersion,
private SignatureMapProvider $signatureMapProvider,
private array $propertiesClassReflectionExtensions,
private array $methodsClassReflectionExtensions,
private array $allowedSubTypesClassReflectionExtensions,
Expand Down Expand Up @@ -983,11 +985,18 @@ public function getConstant(string $name): ConstantReflection
$phpDocType = $varTags[0]->getType();
}

$nativeType = null;
if ($reflectionConstant->getType() !== null) {
$nativeType = TypehintHelper::decideTypeFromReflection($reflectionConstant->getType(), null, $this);
} elseif ($this->signatureMapProvider->hasClassConstantMetadata($declaringClass->getName(), $name)) {
$nativeType = $this->signatureMapProvider->getClassConstantMetadata($declaringClass->getName(), $name)['nativeType'];
}

$this->constants[$name] = new ClassConstantReflection(
$this->initializerExprTypeResolver,
$declaringClass,
$reflectionConstant,
$reflectionConstant->getType(),
$nativeType,
$phpDocType,
$deprecatedDescription,
$isDeprecated,
Expand Down Expand Up @@ -1394,6 +1403,7 @@ public function withTypes(array $types): self
$this->stubPhpDocProvider,
$this->phpDocInheritanceResolver,
$this->phpVersion,
$this->signatureMapProvider,
$this->propertiesClassReflectionExtensions,
$this->methodsClassReflectionExtensions,
$this->allowedSubTypesClassReflectionExtensions,
Expand All @@ -1419,6 +1429,7 @@ public function withVariances(array $variances): self
$this->stubPhpDocProvider,
$this->phpDocInheritanceResolver,
$this->phpVersion,
$this->signatureMapProvider,
$this->propertiesClassReflectionExtensions,
$this->methodsClassReflectionExtensions,
$this->allowedSubTypesClassReflectionExtensions,
Expand Down
10 changes: 10 additions & 0 deletions src/Reflection/SignatureMap/FunctionSignatureMapProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,14 @@ private function computeSignatureMap(array $signatureMap, array $delta): array
return $signatureMap;
}

public function hasClassConstantMetadata(string $className, string $constantName): bool
{
return false;
}

public function getClassConstantMetadata(string $className, string $constantName): array
{
throw new ShouldNotHappenException();
}

}
90 changes: 84 additions & 6 deletions src/Reflection/SignatureMap/Php8SignatureMapProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace PHPStan\Reflection\SignatureMap;

use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod;
Expand Down Expand Up @@ -38,6 +39,9 @@ class Php8SignatureMapProvider implements SignatureMapProvider
/** @var array<string, array<string, array{ClassMethod, string}>> */
private array $methodNodes = [];

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

private Php8StubsMap $map;

public function __construct(
Expand Down Expand Up @@ -70,7 +74,6 @@ public function hasMethodSignature(string $className, string $methodName): bool

/**
* @return array{ClassMethod, string}|null
* @throws ShouldNotHappenException
*/
private function findMethodNode(string $className, string $methodName): ?array
{
Expand Down Expand Up @@ -98,7 +101,7 @@ private function findMethodNode(string $className, string $methodName): ?array
}

if ($stmt->name->toLowerString() === $lowerMethodName) {
if (!$this->isForCurrentVersion($stmt)) {
if (!$this->isForCurrentVersion($stmt->attrGroups)) {
continue;
}
return $this->methodNodes[$lowerClassName][$lowerMethodName] = [$stmt, $stubFile];
Expand All @@ -108,9 +111,12 @@ private function findMethodNode(string $className, string $methodName): ?array
return null;
}

private function isForCurrentVersion(FunctionLike $functionLike): bool
/**
* @param AttributeGroup[] $attrGroups
*/
private function isForCurrentVersion(array $attrGroups): bool
{
foreach ($functionLike->getAttrGroups() as $attrGroup) {
foreach ($attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() === 'Until') {
$arg = $attr->args[0]->value;
Expand Down Expand Up @@ -190,7 +196,7 @@ public function getFunctionSignatures(string $functionName, ?string $className,
throw new ShouldNotHappenException(sprintf('Function %s stub not found in %s.', $functionName, $stubFile));
}
foreach ($functions[$lowerName] as $functionNode) {
if (!$this->isForCurrentVersion($functionNode->getNode())) {
if (!$this->isForCurrentVersion($functionNode->getNode()->getAttrGroups())) {
continue;
}

Expand Down Expand Up @@ -359,4 +365,76 @@ private function getSignature(
);
}

public function hasClassConstantMetadata(string $className, string $constantName): bool
{
$lowerClassName = strtolower($className);
if (!array_key_exists($lowerClassName, $this->map->classes)) {
return false;
}

return $this->findConstantType($className, $constantName) !== null;
}

public function getClassConstantMetadata(string $className, string $constantName): array
{
$lowerClassName = strtolower($className);
if (!array_key_exists($lowerClassName, $this->map->classes)) {
throw new ShouldNotHappenException();
}

$type = $this->findConstantType($className, $constantName);
if ($type === null) {
throw new ShouldNotHappenException();
}

return [
'nativeType' => $type,
];
}

private function findConstantType(string $className, string $constantName): ?Type
{
$lowerClassName = strtolower($className);
$lowerConstantName = strtolower($constantName);
if (isset($this->constantTypes[$lowerClassName][$lowerConstantName])) {
return $this->constantTypes[$lowerClassName][$lowerConstantName];
}

$stubFile = self::DIRECTORY . '/' . $this->map->classes[$lowerClassName];
$nodes = $this->fileNodesFetcher->fetchNodes($stubFile);
$classes = $nodes->getClassNodes();
if (count($classes) !== 1) {
throw new ShouldNotHappenException(sprintf('Class %s stub not found in %s.', $className, $stubFile));
}

$class = $classes[$lowerClassName];
if (count($class) !== 1) {
throw new ShouldNotHappenException(sprintf('Class %s stub not found in %s.', $className, $stubFile));
}

foreach ($class[0]->getNode()->stmts as $stmt) {
if (!$stmt instanceof ClassConst) {
continue;
}

foreach ($stmt->consts as $const) {
if ($const->name->toString() !== $constantName) {
continue;
}

if (!$this->isForCurrentVersion($stmt->attrGroups)) {
continue;
}

if ($stmt->type === null) {
return null;
}

return $this->constantTypes[$lowerClassName][$lowerConstantName] = ParserNodeTypeToPHPStanType::resolve($stmt->type, null);
}
}

return null;
}

}
8 changes: 8 additions & 0 deletions src/Reflection/SignatureMap/SignatureMapProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection\SignatureMap;

use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod;
use PHPStan\Type\Type;
use ReflectionFunctionAbstract;

interface SignatureMapProvider
Expand Down Expand Up @@ -32,4 +33,11 @@ public function getMethodMetadata(string $className, string $methodName): array;
*/
public function getFunctionMetadata(string $functionName): array;

public function hasClassConstantMetadata(string $className, string $constantName): bool;

/**
* @return array{nativeType: Type}
*/
public function getClassConstantMetadata(string $className, string $constantName): array;

}
2 changes: 2 additions & 0 deletions src/Testing/RuleTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
use PHPStan\PhpDoc\StubPhpDocProvider;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\Rules\DirectRegistry as DirectRuleRegistry;
use PHPStan\Rules\Properties\DirectReadWritePropertiesExtensionProvider;
use PHPStan\Rules\Properties\ReadWritePropertiesExtension;
Expand Down Expand Up @@ -87,6 +88,7 @@ private function getAnalyser(): Analyser
self::getContainer()->getByType(FileTypeMapper::class),
self::getContainer()->getByType(StubPhpDocProvider::class),
self::getContainer()->getByType(PhpVersion::class),
self::getContainer()->getByType(SignatureMapProvider::class),
self::getContainer()->getByType(PhpDocInheritanceResolver::class),
self::getContainer()->getByType(FileHelper::class),
$typeSpecifier,
Expand Down
2 changes: 2 additions & 0 deletions src/Testing/TypeInferenceTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
use PHPStan\PhpDoc\StubPhpDocProvider;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ConstantScalarType;
Expand Down Expand Up @@ -54,6 +55,7 @@ public static function processFile(
self::getContainer()->getByType(FileTypeMapper::class),
self::getContainer()->getByType(StubPhpDocProvider::class),
self::getContainer()->getByType(PhpVersion::class),
self::getContainer()->getByType(SignatureMapProvider::class),
self::getContainer()->getByType(PhpDocInheritanceResolver::class),
self::getContainer()->getByType(FileHelper::class),
$typeSpecifier,
Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Analyser/AnalyserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
use PHPStan\PhpDoc\StubPhpDocProvider;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\Rules\AlwaysFailRule;
use PHPStan\Rules\DirectRegistry as DirectRuleRegistry;
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
Expand Down Expand Up @@ -631,6 +632,7 @@ private function createAnalyser(bool $reportUnmatchedIgnoredErrors): Analyser
$fileTypeMapper,
self::getContainer()->getByType(StubPhpDocProvider::class),
self::getContainer()->getByType(PhpVersion::class),
self::getContainer()->getByType(SignatureMapProvider::class),
$phpDocInheritanceResolver,
$fileHelper,
$typeSpecifier,
Expand Down
Loading

0 comments on commit 714bb44

Please sign in to comment.