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