Skip to content

Commit

Permalink
ConstExprNodeResolver - support ConstFetchNode for class constants
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 26, 2024
1 parent 524913e commit 3e51899
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 8 deletions.
80 changes: 73 additions & 7 deletions src/PhpDoc/ConstExprNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\PhpDoc;

use PHPStan\Analyser\NameScope;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
Expand All @@ -10,22 +11,35 @@
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\Reflection\InitializerExprContext;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use function strtolower;

final class ConstExprNodeResolver
{

public function resolve(ConstExprNode $node): Type
public function __construct(
private ReflectionProvider\ReflectionProviderProvider $reflectionProviderProvider,
private InitializerExprTypeResolver $initializerExprTypeResolver,
)
{
}

public function resolve(ConstExprNode $node, NameScope $nameScope): Type
{
if ($node instanceof ConstExprArrayNode) {
return $this->resolveArrayNode($node);
return $this->resolveArrayNode($node, $nameScope);
}

if ($node instanceof ConstExprFalseNode) {
Expand All @@ -52,22 +66,74 @@ public function resolve(ConstExprNode $node): Type
return new ConstantStringType($node->value);
}

return new MixedType();
if ($node instanceof ConstFetchNode) {
if ($nameScope->getClassName() !== null) {
switch (strtolower($node->className)) {
case 'static':
case 'self':
$className = $nameScope->getClassName();
break;

case 'parent':
if ($this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
$classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
if ($classReflection->getParentClass() === null) {
return new ErrorType();

}

$className = $classReflection->getParentClass()->getName();
}
break;
}
}
if (!isset($className)) {
$className = $nameScope->resolveStringName($node->className);
}
if (!$this->getReflectionProvider()->hasClass($className)) {
return new ErrorType();
}
$classReflection = $this->getReflectionProvider()->getClass($className);
if (!$classReflection->hasConstant($node->name)) {
return new ErrorType();
}
if ($classReflection->isEnum() && $classReflection->hasEnumCase($node->name)) {
return new EnumCaseObjectType($classReflection->getName(), $node->name);
}

$reflectionConstant = $classReflection->getNativeReflection()->getReflectionConstant($node->name);
if ($reflectionConstant === false) {
return new ErrorType();
}
$declaringClass = $reflectionConstant->getDeclaringClass();

return $this->initializerExprTypeResolver->getType(
$reflectionConstant->getValueExpression(),
InitializerExprContext::fromClass($declaringClass->getName(), $declaringClass->getFileName() ?: null),
);
}

return new ErrorType();
}

private function resolveArrayNode(ConstExprArrayNode $node): Type
private function resolveArrayNode(ConstExprArrayNode $node, NameScope $nameScope): Type
{
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
foreach ($node->items as $item) {
if ($item->key === null) {
$key = null;
} else {
$key = $this->resolve($item->key);
$key = $this->resolve($item->key, $nameScope);
}
$arrayBuilder->setOffsetValueType($key, $this->resolve($item->value));
$arrayBuilder->setOffsetValueType($key, $this->resolve($item->value, $nameScope));
}

return $arrayBuilder->getArray();
}

private function getReflectionProvider(): ReflectionProvider
{
return $this->reflectionProviderProvider->getReflectionProvider();
}

}
2 changes: 1 addition & 1 deletion src/PhpDoc/PhpDocNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public function resolveMethodTags(PhpDocNode $phpDocNode, NameScope $nameScope):
}
$defaultValue = null;
if ($parameterNode->defaultValue !== null) {
$defaultValue = $this->constExprNodeResolver->resolve($parameterNode->defaultValue);
$defaultValue = $this->constExprNodeResolver->resolve($parameterNode->defaultValue, $nameScope);
}

$parameters[$parameterName] = new MethodTagParameter(
Expand Down
1 change: 1 addition & 0 deletions src/PhpDoc/TypeNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ private function resolveConstTypeNode(ConstTypeNode $typeNode, NameScope $nameSc

$className = $classReflection->getParentClass()->getName();
}
break;
}
}

Expand Down

0 comments on commit 3e51899

Please sign in to comment.