fix(parser): resolve trait member constants with correct lexical namespace, Fix GH-113

master
韩天峰 3 weeks ago
parent d245ff87a0
commit 1ac462ae9f
  1. 32
      phpunit/code/interface_const_trait_import.php
  2. 17
      phpunit/src/InterfaceConstantTest.php
  3. 8
      src/gen_stub.php
  4. 74
      tests/compiler/const/interface-const-trait-import.phpt

@ -0,0 +1,32 @@
<?php
namespace Imported {
interface Output
{
public const VERBOSITY = 32;
}
}
namespace TraitSource {
use Imported\Output as Level;
trait HasLevel
{
protected int $verbosity = Level::VERBOSITY;
}
}
namespace Consumer {
interface Level
{
public const VERBOSITY = 999;
}
class Command
{
use \TraitSource\HasLevel;
}
}
namespace {
function main(): void {}
}

@ -1,5 +1,6 @@
<?php
use TypePhp\CompilerTest;
use TypePhp\Exception\TestError;
/**
@ -87,4 +88,20 @@ class InterfaceConstantTest extends BaseTest
'interface_const_trait_mismatch.php',
);
}
public function testTraitImportedInterfaceConstantUsesLexicalNameInArginfo(): void
{
global $translator;
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$file = TYPEPHP_ROOT_PATH . '/phpunit/code/interface_const_trait_import.php';
$compiler->addFiles([$file]);
$compiler->prepareFile($file);
$compiler->convertFile($file);
$arginfo = file_get_contents($compiler->getArgInfoHeaderFile($file));
self::assertIsString($arginfo);
self::assertStringContainsString('ZVAL_LONG(&property_verbosity_default_value, 32);', $arginfo);
self::assertStringNotContainsString('ZVAL_LONG(&property_verbosity_default_value, 999);', $arginfo);
}
}

@ -69,6 +69,14 @@ function reportFilePutContents(string $filename, string $content): void {
function getClassConstFetchClassName(Expr\ClassConstFetch $expr): string
{
$className = $expr->class->toString();
if ($expr->class instanceof PhpParser\Node\Name && !$expr->class->isSpecialClassName()) {
// Trait members are copied into the using class after NameResolver has
// visited it. Their resolvedName still records the trait's own imports.
$resolvedName = $expr->class->getAttribute('resolvedName');
if ($resolvedName instanceof PhpParser\Node\Name) {
return '\\' . ltrim($resolvedName->toString(), '\\');
}
}
if ($expr->class instanceof PhpParser\Node\Name\FullyQualified) {
return '\\' . $className;
}

@ -0,0 +1,74 @@
--TEST--
Imported interface constants in trait defaults keep the trait's lexical namespace
--FILE--
<?php
namespace Source {
interface OutputInterface
{
public const VERBOSITY_NORMAL = 32;
public const VERBOSITY_VERBOSE = 64;
}
}
namespace Traits {
use Source\OutputInterface as Output;
trait HasOutput
{
protected int $verbosity = Output::VERBOSITY_NORMAL;
protected array $levels = ['verbose' => Output::VERBOSITY_VERBOSE];
public function verbosity(): int { return $this->verbosity; }
public function levels(): array { return $this->levels; }
}
}
namespace Wrapper {
use Traits\HasOutput;
trait WrappedOutput
{
use HasOutput;
}
}
namespace Local {
use Source\OutputInterface;
trait HasLocalOutput
{
protected int $verbosity = OutputInterface::VERBOSITY_NORMAL;
public function verbosity(): int { return $this->verbosity; }
}
class Command
{
use HasLocalOutput;
}
}
namespace Consumer {
class Command
{
use \Wrapper\WrappedOutput;
}
}
namespace {
function main(): void
{
$command = new Consumer\Command();
var_dump($command->verbosity());
var_dump($command->levels());
var_dump((new Local\Command())->verbosity());
}
}
?>
--EXPECT--
int(32)
array(1) {
["verbose"]=>
int(64)
}
int(32)
Loading…
Cancel
Save