packages/deriver/src/Deriver/Resolver/Resolver.php
1<?php
2
3declare(strict_types=1);
4
5namespace Deriver\Deriver\Resolver;
6
7use Deriver\Deriver\Parser\Node\File;
8use Deriver\Exception\ResolutionException;
9use LogicException;
10use PhpParser\ErrorHandler\Collecting;
11use PhpParser\NodeTraverser;
12use PhpParser\NodeVisitor\CloningVisitor;
13use PhpParser\NodeVisitor\NameResolver;
14
15/**
16 * Resolves names and lexical context on a copy of a parsed syntax tree.
17 * @visibility root
18 */
19final class Resolver
20{
21 /**
22 * Prepares declarations and expressions for indexing without changing the parsed input.
23 * @throws ResolutionException If names, scope, or expression constraints are invalid
24 * @throws LogicException If traversal violates the file node contract
25 */
26 public function resolve(File $file): File
27 {
28 $errors = new Collecting();
29 $copy = (new NodeTraverser(new CloningVisitor()))->traverse([$file]);
30 $traverser = new NodeTraverser(new ClassScope($errors), new AssignmentPatterns($errors), new ExpressionValidation($errors), new NameResolver($errors), new MagicContext());
31 $resolved = $traverser->traverse($copy)[0];
32 $error = $errors->getErrors()[0] ?? null;
33 if ($error !== null) {
34 throw new ResolutionException($error->getRawMessage(), $file->source->path, max(1, $error->getStartLine()), $error);
35 }
36 if (!$resolved instanceof File) {
37 throw new LogicException('Name resolution must preserve the file root.');
38 }
39 return $resolved;
40 }
41}
42