fix: preserve imported enum cases in attribute arguments

master^2
韩天峰 5 hours ago
parent 8b1c8e7d26
commit f6394934c5
  1. 47
      src/Transform/RuntimeAttributeFactoryLowering.php
  2. 87
      tests/compiler/attribute/imported-enum-defaults.phpt

@ -78,14 +78,36 @@ final class RuntimeAttributeFactoryLowering extends NodeVisitorAbstract
return null;
}
if (!$node instanceof Node\Attribute) {
return null;
return null;
}
public function leaveNode(Node $node): null
{
if ($node instanceof Node\Attribute) {
$this->lowerAttribute($node);
} elseif ($node instanceof Stmt\ClassLike) {
array_pop($this->classStack);
} elseif ($node instanceof Stmt\Namespace_) {
$factories = array_pop($this->namespaceFactories);
if ($factories !== []) {
array_push($node->stmts, ...$factories);
}
$this->namespace = '';
}
if (CompileTimeAttributeRegistry::get($node->name->toString()) !== null) {
return null;
return null;
}
private function lowerAttribute(Node\Attribute $attribute): void
{
if (CompileTimeAttributeRegistry::get($attribute->name->toString()) !== null) {
return;
}
foreach ($node->args as $argument) {
// Attribute children have now passed through NameResolver. Processing
// on enterNode() resolves imported enum names relative to the current
// namespace (for example `use A\\Status; Status::Active`) and misses
// the enum case, causing gen_stub to persist its backing scalar.
foreach ($attribute->args as $argument) {
if (!$this->requiresFactory($argument->value)) {
continue;
}
@ -102,21 +124,6 @@ final class RuntimeAttributeFactoryLowering extends NodeVisitorAbstract
$this->globalFactories[] = $factory['node'];
}
}
return null;
}
public function leaveNode(Node $node): null
{
if ($node instanceof Stmt\ClassLike) {
array_pop($this->classStack);
} elseif ($node instanceof Stmt\Namespace_) {
$factories = array_pop($this->namespaceFactories);
if ($factories !== []) {
array_push($node->stmts, ...$factories);
}
$this->namespace = '';
}
return null;
}
public function afterTraverse(array $nodes): ?array

@ -0,0 +1,87 @@
--TEST--
Imported enum cases are preserved in property and parameter defaults
--FILE--
<?php
namespace EnumDefaults\Domain {
enum IdType: string
{
case Auto = 'auto';
case Assigned = 'assigned';
}
}
namespace EnumDefaults\Metadata {
use Attribute;
use EnumDefaults\Domain\IdType;
#[Attribute(Attribute::TARGET_PROPERTY)]
class TableId
{
public function __construct(public readonly IdType $type = IdType::Auto)
{
}
}
}
namespace EnumDefaults\Model {
use EnumDefaults\Domain\IdType;
use EnumDefaults\Metadata\TableId;
class Record
{
#[TableId(IdType::Assigned)]
public IdType $explicit = IdType::Assigned;
#[TableId]
public IdType $default = IdType::Auto;
public function __construct(public readonly IdType $promoted = IdType::Auto)
{
}
public function select(IdType $type = IdType::Auto): IdType
{
return $type;
}
}
}
namespace {
use EnumDefaults\Domain\IdType;
use EnumDefaults\Metadata\TableId;
use EnumDefaults\Model\Record;
function main(): void
{
$record = new Record();
var_dump($record->explicit === IdType::Assigned);
var_dump($record->default === IdType::Auto);
var_dump($record->promoted === IdType::Auto);
var_dump($record->select() === IdType::Auto);
$reflection = new ReflectionClass(Record::class);
$explicit = $reflection->getProperty('explicit')->getAttributes(TableId::class)[0];
var_dump($explicit->getArguments()[0] === IdType::Assigned);
var_dump($explicit->newInstance()->type === IdType::Assigned);
$default = $reflection->getProperty('default')->getAttributes(TableId::class)[0];
var_dump($default->getArguments());
var_dump($default->newInstance()->type === IdType::Auto);
$parameter = $reflection->getMethod('select')->getParameters()[0];
var_dump($parameter->getDefaultValue() === IdType::Auto);
}
}
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
array(0) {
}
bool(true)
bool(true)
Loading…
Cancel
Save