Skip to content

Commit 872659e

Browse files
committed
Class resolution through imports to solve ambiguities
1 parent fe50e9e commit 872659e

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

packages/cli/src/languagePlugins/csharp/invocationResolver/index.ts

+27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SymbolNode,
77
} from "../namespaceMapper";
88
import { csharpParser } from "../../../helpers/treeSitter/parsers";
9+
import { CSharpUsingResolver, ResolvedImports } from "../usingResolver";
910

1011
export interface Invocations {
1112
resolvedSymbols: SymbolNode[];
@@ -15,9 +16,16 @@ export interface Invocations {
1516
export class CSharpInvocationResolver {
1617
parser: Parser = csharpParser;
1718
private nsMapper: CSharpNamespaceMapper;
19+
private usingResolver: CSharpUsingResolver;
20+
private resolvedImports: ResolvedImports;
1821

1922
constructor(nsMapper: CSharpNamespaceMapper) {
2023
this.nsMapper = nsMapper;
24+
this.usingResolver = new CSharpUsingResolver(nsMapper);
25+
this.resolvedImports = {
26+
internal: [],
27+
external: [],
28+
};
2129
}
2230

2331
// Retrieves variable names from the given syntax node.
@@ -65,6 +73,15 @@ export class CSharpInvocationResolver {
6573
// Process each captured class name
6674
catches.forEach((ctc) => {
6775
const classname = ctc.node.text;
76+
// Try to find the class in the resolved imports
77+
const ucls = this.usingResolver.findClassInImports(
78+
this.resolvedImports,
79+
classname,
80+
);
81+
if (ucls) {
82+
invocations.resolvedSymbols.push(ucls);
83+
return;
84+
}
6885
// Try to find the class in the namespace tree
6986
const cls = this.nsMapper.findClassInTree(namespaceTree, classname);
7087
if (cls) {
@@ -108,6 +125,15 @@ export class CSharpInvocationResolver {
108125
if (variablenames.includes(classname)) {
109126
return;
110127
}
128+
// Try to find the class in the resolved imports
129+
const ucls = this.usingResolver.findClassInImports(
130+
this.resolvedImports,
131+
classname,
132+
);
133+
if (ucls) {
134+
invocations.resolvedSymbols.push(ucls);
135+
return;
136+
}
111137
// Try to find the class in the namespace tree
112138
const cls = this.nsMapper.findClassInTree(namespaceTree, classname);
113139
if (cls) {
@@ -122,6 +148,7 @@ export class CSharpInvocationResolver {
122148

123149
// Gets the classes used in a file.
124150
getInvocationsFromFile(filepath: string): Invocations {
151+
this.resolvedImports = this.usingResolver.resolveUsingDirectives(filepath);
125152
const file = this.nsMapper.getFile(filepath) as File;
126153
const invocations: Invocations = {
127154
resolvedSymbols: [],

packages/cli/src/languagePlugins/csharp/testFiles/csharpFiles/Program.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
using HalfNamespace;
33
using OuterNamespace;
44
using OuterNamespace.InnerNamespace;
5+
using BeefBurger;
56

67
namespace Tests
78
{
89
class Program
910
{
1011
static void Main(string[] args)
1112
{
12-
BeefBurger.Bun beefBun = new BeefBurger.Bun();
13+
Bun beefBun = new Bun();
1314
ChickenBurger.Bun chickenBun = new ChickenBurger.Bun();
1415
MyClass myClass = new MyClass();
1516
myClass.MyMethod();

packages/cli/src/languagePlugins/csharp/usingResolver/index.test.ts

+25
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,29 @@ describe("UsingResolver", () => {
9090
],
9191
});
9292
});
93+
94+
test("Class resolution", () => {
95+
const imports = resolver.resolveUsingDirectives("Usage.cs");
96+
const user = resolver.findClassInImports(imports, "User");
97+
expect(user).toMatchObject({
98+
name: "User",
99+
type: "class",
100+
namespace: "MyApp.Models",
101+
filepath: "Models.cs",
102+
});
103+
const gordon = resolver.findClassInImports(imports, "Gordon");
104+
expect(gordon).toMatchObject({
105+
name: "Gordon",
106+
type: "class",
107+
namespace: "HalfNamespace",
108+
filepath: "SemiNamespaced.cs",
109+
});
110+
const guy = resolver.findClassInImports(imports, "Guy");
111+
expect(guy).toMatchObject({
112+
name: "User",
113+
type: "class",
114+
namespace: "MyApp.Models",
115+
filepath: "Models.cs",
116+
});
117+
});
93118
});

packages/cli/src/languagePlugins/csharp/usingResolver/index.ts

+41
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,45 @@ export class CSharpUsingResolver {
130130
});
131131
return { internal, external };
132132
}
133+
134+
public findClassInImports(
135+
imports: ResolvedImports,
136+
className: string,
137+
): SymbolNode | null {
138+
// Handle qualified class names with aliases
139+
const parts = className.split(".");
140+
for (let i = 0; i < parts.length; i++) {
141+
const aliasMatch = imports.internal.find(
142+
(symbol) => symbol.alias === parts[i],
143+
);
144+
if (aliasMatch && aliasMatch.symbol) {
145+
parts[i] = aliasMatch.symbol.name;
146+
}
147+
}
148+
const reconstructedClassName = parts.join(".");
149+
// Check if the class is directly imported
150+
const found = imports.internal.find(
151+
(symbol) =>
152+
"symbol" in symbol &&
153+
(symbol.symbol?.name === reconstructedClassName ||
154+
symbol.alias === reconstructedClassName),
155+
);
156+
if (found) {
157+
return found.symbol ?? null;
158+
}
159+
// Check if the class is imported through a namespace
160+
for (const symbol of imports.internal) {
161+
if ("namespace" in symbol && symbol.namespace) {
162+
const nsFound = this.nsMapper.findClassInTree(
163+
symbol.namespace,
164+
reconstructedClassName,
165+
);
166+
if (nsFound) {
167+
return nsFound;
168+
}
169+
}
170+
}
171+
172+
return null;
173+
}
133174
}

0 commit comments

Comments
 (0)