Skip to content

Commit d20a95d

Browse files
authored
Feature/java invocation resolver (#151)
* Fixed wildcard imports breaking queries * Invocation resolver * Absolute foolproof safety
1 parent a744281 commit d20a95d

7 files changed

Lines changed: 121 additions & 2 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, test } from "@std/testing/bdd";
2+
import { expect } from "@std/expect";
3+
import { getJavaFilesMap } from "../testFiles/index.ts";
4+
import { JavaInvocationResolver } from "./index.ts";
5+
import { JavaPackageMapper } from "../packageMapper/index.ts";
6+
import { JavaImportResolver } from "../importResolver/index.ts";
7+
import { APP, WORMKILLER } from "../testFiles/constants.ts";
8+
9+
describe("Java Invocation Resolver", () => {
10+
const files = getJavaFilesMap();
11+
const mapper = new JavaPackageMapper(files);
12+
const impResolver = new JavaImportResolver(mapper);
13+
const resolver = new JavaInvocationResolver(impResolver);
14+
const invocations = resolver.invocations;
15+
16+
test("resolves Wormkiller.java", () => {
17+
const killerinv = invocations.get(WORMKILLER)!;
18+
expect(killerinv.unresolved.has("Sandworm")).toBe(true);
19+
expect(killerinv.unresolved.has("Pebble.Sandworm")).toBe(true);
20+
const resolved = Array.from(killerinv.resolved.keys());
21+
expect(resolved).toContain("Steak");
22+
expect(resolved).toContain("Pebble");
23+
});
24+
25+
test("resolves App.java", () => {
26+
const appinv = invocations.get(APP)!;
27+
console.log(impResolver.imports.get(APP)!);
28+
expect(appinv.unresolved.has("System.out")).toBe(false);
29+
expect(appinv.unresolved.has("System")).toBe(true);
30+
const resolved = Array.from(appinv.resolved.keys());
31+
expect(resolved).toContain("restaurantCount");
32+
});
33+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { JavaImportResolver } from "../importResolver/index.ts";
2+
import type { JavaImports } from "../importResolver/types.ts";
3+
import type { JavaPackageMapper } from "../packageMapper/index.ts";
4+
import { ConcreteNode } from "../packageMapper/types.ts";
5+
import type { JavaFile } from "../packageResolver/types.ts";
6+
import { JAVA_INVOCATION_QUERY, JAVA_VARIABLES_QUERY } from "./queries.ts";
7+
import type { Invocations } from "./types.ts";
8+
9+
export class JavaInvocationResolver {
10+
mapper: JavaPackageMapper;
11+
imports: Map<string, JavaImports>;
12+
invocations: Map<string, Invocations>;
13+
14+
constructor(resolver: JavaImportResolver) {
15+
this.mapper = resolver.mapper;
16+
this.imports = resolver.imports;
17+
this.invocations = new Map();
18+
for (const [key, f] of this.mapper.files) {
19+
this.invocations.set(key, this.getInvocations(f));
20+
}
21+
}
22+
23+
getInvocations(file: JavaFile): Invocations {
24+
const variables = new Set(
25+
JAVA_VARIABLES_QUERY.captures(file.symbol.node).map((c) => c.node.text),
26+
);
27+
const captures = JAVA_INVOCATION_QUERY.captures(file.symbol.node).map((c) =>
28+
c.node.text
29+
);
30+
const resolvedImports = this.imports.get(file.path)!.resolved;
31+
const resolved = new Map<string, ConcreteNode>();
32+
const unresolved = new Set<string>();
33+
for (const c of captures) {
34+
// Check variables
35+
if (variables.has(c)) {
36+
continue;
37+
}
38+
// Check imports
39+
if (resolvedImports.has(c)) {
40+
resolved.set(c, resolvedImports.get(c)!);
41+
} else {
42+
// Check project
43+
const cnode = this.mapper.tree.getNode(c);
44+
if (cnode && cnode instanceof ConcreteNode) {
45+
resolved.set(c, cnode);
46+
} else {
47+
unresolved.add(c);
48+
}
49+
}
50+
}
51+
return { resolved, unresolved };
52+
}
53+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Parser from "npm:tree-sitter";
2+
import { javaParser } from "../../../helpers/treeSitter/parsers.ts";
3+
4+
export const JAVA_INVOCATION_QUERY = new Parser.Query(
5+
javaParser.getLanguage(),
6+
`
7+
[
8+
(scoped_type_identifier)
9+
(type_identifier)
10+
(identifier)
11+
] @any
12+
`,
13+
);
14+
15+
export const JAVA_VARIABLES_QUERY = new Parser.Query(
16+
javaParser.getLanguage(),
17+
`
18+
(variable_declarator name: (_) @var)
19+
(formal_parameter name: (_) @var)
20+
`,
21+
);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { ConcreteNode } from "../packageMapper/types.ts";
2+
3+
export interface Invocations {
4+
resolved: Map<string, ConcreteNode>;
5+
unresolved: Set<string>;
6+
}

packages/cli/src/languagePlugins/java/packageResolver/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export class JavaPackageResolver {
1414
const packagename = captures.find((c) => c.name === "package")?.node.text ??
1515
"";
1616
const imports = captures.filter((c) => c.name === "import").map((c) =>
17-
c.node.text
17+
// Unholy string manipulation because wildcard imports break tree-sitter queries.
18+
c.node.text.split(" ").findLast((s) => s.length > 0 && s !== ";")!
19+
.replace(";", "")
1820
);
1921
const declaration = captures.find((c) =>
2022
!["package", "import"].includes(c.name)

packages/cli/src/languagePlugins/java/packageResolver/queries.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const JAVA_PROGRAM_QUERY = new Parser.Query(
55
javaParser.getLanguage(),
66
`(program [
77
(package_declaration (_) @package)
8-
(import_declaration (_) @import)
8+
(import_declaration) @import
99
(class_declaration) @class
1010
(interface_declaration) @interface
1111
(enum_declaration) @enum
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package io.nanoapi.testfiles;
22

3+
import static io.nanoapi.testfiles.food.Burger.*;
4+
35
/**
46
* Hello world!
57
*/
68
public class App {
9+
710
public static void main(String[] args) {
11+
restaurantCount++;
812
System.out.println("Hello World!");
913
}
1014
}

0 commit comments

Comments
 (0)