Skip to content

Commit 8dfefd3

Browse files
authored
Invocation resolver (#169)
1 parent acc0d4a commit 8dfefd3

File tree

6 files changed

+128
-0
lines changed

6 files changed

+128
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { describe, test } from "@std/testing/bdd";
2+
import { expect } from "@std/expect";
3+
import { PHPInvocationResolver } from "./index.ts";
4+
import { PHPIncluseResolver } from "../incluseResolver/index.ts";
5+
import { PHPRegistree } from "../registree/index.ts";
6+
import { getPHPFilesMap } from "../testFiles/index.ts";
7+
import { INVOCATIONS } from "../testFiles/constants.ts";
8+
9+
describe("PHP Invocation resolver", () => {
10+
const files = getPHPFilesMap();
11+
const registree = new PHPRegistree(files);
12+
const incluseres = new PHPIncluseResolver(registree);
13+
const resolver = new PHPInvocationResolver(incluseres);
14+
15+
test("resolves invocations", () => {
16+
const invocations = resolver.getInvocationsForFile(INVOCATIONS);
17+
expect(invocations.unresolved.size >= 1).toBe(true);
18+
expect(invocations.unresolved).toContainEqual("doesnotexistlmao");
19+
const resolved = Array.from(invocations.resolved.keys());
20+
expect(resolved).toContainEqual("array");
21+
expect(resolved).toContainEqual("my_function");
22+
expect(resolved).toContainEqual("a");
23+
expect(resolved).toContainEqual("MyClass");
24+
expect(resolved).toContainEqual("All\\My\\Fellas\\f");
25+
});
26+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { Invocations } from "./types.ts";
2+
import { PHP_INVOCATION_QUERY } from "./queries.ts";
3+
import type { PHPIncluseResolver } from "../incluseResolver/index.ts";
4+
import type Parser from "tree-sitter";
5+
import type { ExportedSymbol } from "../exportResolver/types.ts";
6+
import { SymbolNode } from "../registree/types.ts";
7+
8+
export class PHPInvocationResolver {
9+
incluseResolver: PHPIncluseResolver;
10+
11+
constructor(incluseResolver: PHPIncluseResolver) {
12+
this.incluseResolver = incluseResolver;
13+
}
14+
15+
getInvocationsForNode(
16+
node: Parser.SyntaxNode,
17+
filepath: string,
18+
symbolname: string | undefined = undefined,
19+
): Invocations {
20+
const currentfile = this.incluseResolver.registree.registry.files.get(
21+
filepath,
22+
)!;
23+
const availableSymbols = this.incluseResolver
24+
.resolveImports(currentfile)?.resolved;
25+
const localSymbols = currentfile.symbols;
26+
const unresolved = new Set<string>();
27+
const resolved = new Map<string, ExportedSymbol[]>();
28+
const captures = PHP_INVOCATION_QUERY.captures(node);
29+
for (const capture of captures) {
30+
const name = capture.node.text;
31+
// if the symbol name is the same as the one we are looking at, skip it
32+
if (symbolname && name === symbolname) {
33+
continue;
34+
}
35+
if (availableSymbols && availableSymbols.has(name)) {
36+
const availableSymbol = availableSymbols.get(name);
37+
if (!availableSymbol) {
38+
unresolved.add(name);
39+
continue;
40+
}
41+
resolved.set(name, availableSymbol);
42+
} else if (localSymbols && localSymbols.has(name)) {
43+
const localSymbol = localSymbols.get(name);
44+
if (!localSymbol) {
45+
unresolved.add(name);
46+
continue;
47+
}
48+
resolved.set(name, localSymbol);
49+
} else if (capture.name === "qualified") {
50+
const qualSymbol = this.incluseResolver.registree.tree.findNode(name);
51+
if (qualSymbol && qualSymbol instanceof SymbolNode) {
52+
resolved.set(name, qualSymbol.symbols);
53+
} else {
54+
unresolved.add(name);
55+
}
56+
} else {
57+
unresolved.add(name);
58+
}
59+
}
60+
return {
61+
resolved,
62+
unresolved,
63+
};
64+
}
65+
66+
getInvocationsForFile(filepath: string): Invocations {
67+
const file = this.incluseResolver.registree.registry.files.get(filepath);
68+
if (!file) {
69+
throw new Error(`File not found: ${filepath}`);
70+
}
71+
return this.getInvocationsForNode(file.rootNode, file.path);
72+
}
73+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Parser from "tree-sitter";
2+
import { phpParser } from "../../../helpers/treeSitter/parsers.ts";
3+
4+
export const PHP_INVOCATION_QUERY = new Parser.Query(
5+
phpParser.getLanguage(),
6+
`
7+
(name) @name
8+
(qualified_name) @qualified
9+
`,
10+
);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { ExportedSymbol } from "../exportResolver/types.ts";
2+
3+
export interface Invocations {
4+
resolved: Map<string, ExportedSymbol[]>;
5+
unresolved: Set<string>;
6+
}

src/languagePlugins/php/testFiles/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export const LEARN_PHP = join(phpFilesFolder, "learnphp.php");
55
export const NESTED = join(phpFilesFolder, "nested.php");
66
export const INCLUDE = join(phpFilesFolder, "include.php");
77
export const USE = join(phpFilesFolder, "use.php");
8+
export const INVOCATIONS = join(phpFilesFolder, "invocations.php");
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
namespace NewMexico\Albuquerque;
3+
require 'learnphp.php';
4+
5+
$one = $array[0];
6+
echo my_function();
7+
$ahundred = $a;
8+
$walter = new MyClass('Walter');
9+
10+
$error = $doesnotexistlmao;
11+
12+
class Product implements All\My\Fellas\f {}

0 commit comments

Comments
 (0)