From 8d745d89be498ed9c47809ae664a8167a5c3f7c3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 31 Aug 2026 18:38:35 +0800 Subject: [PATCH] fix(trait): preserve nested namespace context, fix gh-38 --- src/Translator.php | 8 ++- .../trait/nested-trait-import-context.phpt | 49 +++++++++++++++++++ .../trait/nested-trait-namespace-context.phpt | 28 +++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/compiler/trait/nested-trait-import-context.phpt create mode 100644 tests/compiler/trait/nested-trait-namespace-context.phpt diff --git a/src/Translator.php b/src/Translator.php index 3f383552..8f18e177 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -3107,8 +3107,12 @@ CODE; /** @var Node\Stmt\Trait_ $traitAst */ $traitAst = $this->cloneAstNode($traitDef->trait); // Recursively flatten traits used by this trait before copying - // its members into the final class. - $this->composeTraitAst($traitAst, new Node\Name($traitFullName)); + // its members into the final class. The nested TraitUse nodes + // must be resolved in the lexical context of the trait being + // expanded, not in the context of its eventual consumer. + $this->withTraitNameContext($traitFullName, function () use ($traitAst, $traitFullName): void { + $this->composeTraitAst($traitAst, new Node\Name($traitFullName)); + }); $traitStmts = $traitAst->stmts; $aliasStmts = []; foreach ($traitStmts as $k1 => $traitStmt) { diff --git a/tests/compiler/trait/nested-trait-import-context.phpt b/tests/compiler/trait/nested-trait-import-context.phpt new file mode 100644 index 00000000..b49b76cb --- /dev/null +++ b/tests/compiler/trait/nested-trait-import-context.phpt @@ -0,0 +1,49 @@ +--TEST-- +Nested trait uses retain import aliases through multiple composition levels +--FILE-- +importedHello() . ']'; + } + } + + trait OuterWrapper { + use InnerWrapper; + } +} + +namespace Issue38\Consumer { + use Issue38\Template\OuterWrapper as ImportedWrapper; + + class Example { + use ImportedWrapper; + } +} + +namespace { + function main(): void { + $example = new Issue38\Consumer\Example(); + echo $example->hello(), "\n"; + echo $example->wrapped(), "\n"; + } +} +?> +--EXPECT-- +imported +[imported] diff --git a/tests/compiler/trait/nested-trait-namespace-context.phpt b/tests/compiler/trait/nested-trait-namespace-context.phpt new file mode 100644 index 00000000..3f627766 --- /dev/null +++ b/tests/compiler/trait/nested-trait-namespace-context.phpt @@ -0,0 +1,28 @@ +--TEST-- +Nested trait uses resolve unqualified names in the declaring namespace +--FILE-- +hello(), "\n"; + } +} +?> +--EXPECT-- +hi