55namespace TypeLang \Parser ;
66
77use TypeLang \Parser \Traverser \TypeMapVisitor ;
8+ use TypeLang \Parser \TypeResolver \PhpUseStatementsReader ;
89use TypeLang \Parser \TypeResolver \PhpUseStatementsTransformer ;
910use TypeLang \Type \TypeNode ;
1011
@@ -41,7 +42,6 @@ public function __construct(
4142 * echo $ast->name->toString();
4243 * ```
4344 *
44- * @api
4545 * @param non-empty-string $name
4646 */
4747 public function withTypeImport (string $ name ): self
@@ -73,7 +73,6 @@ public function withTypeImport(string $name): self
7373 * echo $ast->name->toString();
7474 * ```
7575 *
76- * @api
7776 * @param non-empty-string $name
7877 * @param non-empty-string $alias
7978 */
@@ -82,6 +81,93 @@ public function withTypeImportAs(string $name, string $alias): self
8281 return new self ([...$ this ->imports , $ alias => $ name ]);
8382 }
8483
84+ /**
85+ * Registers every `use` import declared in the source file of the given
86+ * class, so that type names are resolved exactly as they would be inside
87+ * that class.
88+ *
89+ * For a class whose file declares:
90+ * ```
91+ * namespace App;
92+ *
93+ * use TypeLang\Parser\Node;
94+ * use TypeLang\Parser\Exception as Error;
95+ *
96+ * final class Example {}
97+ * ```
98+ *
99+ * You can reuse its imports like this:
100+ * ```
101+ * $ast = new TypeParser()
102+ * ->parse(<<<'PHP'
103+ * Error\SemanticException
104+ * PHP);
105+ *
106+ * $ast = new TypeResolver()
107+ * ->withTypeImportsOf(new \ReflectionClass(App\Example::class))
108+ * ->resolve($ast);
109+ *
110+ * // Expected Output:
111+ * // > TypeLang\Parser\Exception\SemanticException
112+ * echo $ast->name->toString();
113+ * ```
114+ *
115+ * @param \ReflectionClass<object> $class
116+ */
117+ public function withTypeImportsFromClass (\ReflectionClass $ class ): self
118+ {
119+ $ statements = new PhpUseStatementsReader ()
120+ ->getClassUseStatements ($ class );
121+
122+ return new self ([...$ this ->imports , ...$ statements ]);
123+ }
124+
125+ /**
126+ * Registers every `use` import available in the source file of the given
127+ * function or method, so that type names are resolved exactly as they
128+ * would be inside that function's body.
129+ *
130+ * For a function whose file declares:
131+ * ```
132+ * namespace App;
133+ *
134+ * use TypeLang\Parser\Node;
135+ * use TypeLang\Parser\Exception as Error;
136+ *
137+ * function example(): void {}
138+ * ```
139+ *
140+ * You can reuse its imports like this:
141+ * ```
142+ * $ast = new TypeParser()
143+ * ->parse(<<<'PHP'
144+ * Error\SemanticException
145+ * PHP);
146+ *
147+ * $ast = new TypeResolver()
148+ * ->withTypeImportsFromFunction(new \ReflectionFunction('App\example'))
149+ * ->resolve($ast);
150+ *
151+ * // Expected Output:
152+ * // > TypeLang\Parser\Exception\SemanticException
153+ * echo $ast->name->toString();
154+ * ```
155+ */
156+ public function withTypeImportsFromFunction (\ReflectionFunctionAbstract $ function ): self
157+ {
158+ if ($ function instanceof \ReflectionMethod) {
159+ // For methods, we find the class and search by it. This will
160+ // allow us to traverse fewer PHP tokens (optimize), since the
161+ // class declaration is "higher".
162+ return $ this ->withTypeImportsFromClass ($ function ->getDeclaringClass ());
163+ }
164+
165+ $ statements = new PhpUseStatementsReader ()
166+ ->getFunctionUseStatements ($ function );
167+
168+ return new self ([...$ this ->imports , ...$ statements ]);
169+ }
170+
85171 private function createTraverser (): TraverserInterface
86172 {
87173 $ transformer = new PhpUseStatementsTransformer ($ this ->imports );
@@ -112,8 +198,6 @@ private function createTraverser(): TraverserInterface
112198 * // > TypeLang\Parser\Exception\SemanticException
113199 * // > }
114200 * ```
115- *
116- * @api
117201 */
118202 public function resolve (TypeNode $ type ): TypeNode
119203 {
0 commit comments