Skip to content

Commit 9cb3321

Browse files
authored
Fix/eliminate obsolete usings (#118)
* Made usingResolver in extractor public * Made usingResolver symbols into classes * Made resolveUsingDirective public * Removed obsolete usings * Added caching * Moved using validation to somewhere it actually does something
1 parent 354a027 commit 9cb3321

File tree

3 files changed

+93
-10
lines changed

3 files changed

+93
-10
lines changed

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class CSharpExtractor {
2525
private manifest: DependencyManifest;
2626
public projectMapper: CSharpProjectMapper;
2727
private nsMapper: CSharpNamespaceMapper;
28-
private usingResolver: CSharpUsingResolver;
28+
public usingResolver: CSharpUsingResolver;
2929

3030
constructor(
3131
files: Map<string, { path: string; content: string }>,
@@ -91,17 +91,19 @@ export class CSharpExtractor {
9191
symbol: SymbolNode,
9292
visited: Set<SymbolNode> = new Set<SymbolNode>(),
9393
): SymbolNode[] {
94-
const allDependencies: SymbolNode[] = [];
94+
const allDependencies: Set<SymbolNode> = new Set<SymbolNode>();
9595
if (visited.has(symbol)) {
96-
return allDependencies;
96+
return Array.from(allDependencies);
9797
}
9898
visited.add(symbol);
9999
const dependencies = this.findDependencies(symbol);
100100
for (const dependency of dependencies) {
101-
allDependencies.push(dependency);
102-
allDependencies.push(...this.findAllDependencies(dependency, visited));
101+
allDependencies.add(dependency);
102+
for (const dep of this.findAllDependencies(dependency, visited)) {
103+
allDependencies.add(dep);
104+
}
103105
}
104-
return allDependencies;
106+
return Array.from(allDependencies);
105107
}
106108

107109
/**

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export interface UsingDirective {
5252
/**
5353
* Interface representing an internal symbol resolved from a 'using' directive
5454
*/
55-
export interface InternalSymbol {
55+
export class InternalSymbol {
5656
/** The type of 'using' directive */
5757
usingtype: UsingType;
5858
/** The filepath it is imported in */
@@ -68,7 +68,7 @@ export interface InternalSymbol {
6868
/**
6969
* Interface representing an external symbol resolved from a 'using' directive
7070
*/
71-
export interface ExternalSymbol {
71+
export class ExternalSymbol {
7272
/** The type of 'using' directive */
7373
usingtype: UsingType;
7474
/** The filepath it is imported in */
@@ -101,6 +101,10 @@ export class CSharpUsingResolver {
101101
string,
102102
ResolvedImports
103103
>();
104+
private cachedDirectives: Map<string, UsingDirective[]> = new Map<
105+
string,
106+
UsingDirective[]
107+
>();
104108
public projectmapper: CSharpProjectMapper;
105109
private cachedExternalDeps: Set<string> = new Set<string>();
106110

@@ -118,6 +122,9 @@ export class CSharpUsingResolver {
118122
* @returns An array of UsingDirective objects.
119123
*/
120124
public parseUsingDirectives(filepath: string): UsingDirective[] {
125+
if (this.cachedDirectives.has(filepath)) {
126+
return this.cachedDirectives.get(filepath) as UsingDirective[];
127+
}
121128
const file = this.nsMapper.getFile(filepath);
122129
if (!file) {
123130
return [];
@@ -141,6 +148,8 @@ export class CSharpUsingResolver {
141148
const alias = aliasNode ? aliasNode.text : undefined;
142149
return { node, type, filepath, id, alias };
143150
});
151+
// Cache the using directives for the file
152+
this.cachedDirectives.set(filepath, this.usingDirectives);
144153
return this.usingDirectives;
145154
}
146155

@@ -168,7 +177,7 @@ export class CSharpUsingResolver {
168177
* @param directive - The 'using' directive to resolve.
169178
* @returns An InternalSymbol or ExternalSymbol object.
170179
*/
171-
private resolveUsingDirective(
180+
public resolveUsingDirective(
172181
directive: UsingDirective,
173182
): InternalSymbol | ExternalSymbol {
174183
const { type, filepath, id, alias } = directive;

packages/cli/src/symbolExtractor/csharp/index.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ import {
88
} from "../../languagePlugins/csharp/extractor/index.js";
99
import { DependencyManifest } from "@nanoapi.io/shared";
1010
import { DotNetProject } from "../../languagePlugins/csharp/projectMapper/index.js";
11+
import Parser from "tree-sitter";
12+
import { csharpParser } from "../../helpers/treeSitter/parsers.js";
13+
import { CSharpNamespaceMapper } from "../../languagePlugins/csharp/namespaceMapper/index.js";
14+
import { CSharpProjectMapper } from "../../languagePlugins/csharp/projectMapper/index.js";
15+
import {
16+
CSharpUsingResolver,
17+
InternalSymbol,
18+
ExternalSymbol,
19+
} from "../../languagePlugins/csharp/usingResolver/index.js";
1120

1221
/**
1322
* Extracts C# symbols from the given files.
@@ -27,6 +36,11 @@ export function extractCSharpSymbols(
2736
console.time(`Extracted ${symbolsToExtract.size} symbol(s)`);
2837
const extractor = new CSharpExtractor(files, dependencyManifest);
2938
const extractedFiles: ExtractedFile[] = [];
39+
const parsedFiles = new Map<
40+
string,
41+
{ path: string; rootNode: Parser.SyntaxNode }
42+
>();
43+
const csprojFiles = new Map<string, { path: string; content: string }>();
3044
// Extract symbols from the files
3145
for (const symbolSet of symbolsToExtract.values()) {
3246
for (const symbol of symbolSet.symbols) {
@@ -35,10 +49,65 @@ export function extractCSharpSymbols(
3549
symbol,
3650
);
3751
if (extractedFile) {
52+
// Add the extracted file to the list of extracted files
3853
extractedFiles.push(...extractedFile);
54+
// Add the extracted file to the parsed files map
55+
// This is used to create a representation of the exported project
56+
// It will help us to find which namespaces cannot be used in using directives anymore
57+
for (const file of extractedFile) {
58+
const filePath = file.name;
59+
if (!parsedFiles.has(filePath)) {
60+
parsedFiles.set(filePath, {
61+
path: filePath,
62+
rootNode: csharpParser.parse(extractor.getContent(file)).rootNode,
63+
});
64+
}
65+
// Add the csproj file to the csproj files map
66+
const subproject = file.subproject;
67+
if (!csprojFiles.has(subproject.csprojPath)) {
68+
csprojFiles.set(subproject.csprojPath, {
69+
path: subproject.csprojPath,
70+
content: subproject.csprojContent,
71+
});
72+
const globalUsingsPath = path.join(
73+
subproject.rootFolder,
74+
"GlobalUsings.cs",
75+
);
76+
const globalUsingsContent =
77+
extractor.generateGlobalUsings(subproject);
78+
if (!parsedFiles.has(globalUsingsPath)) {
79+
parsedFiles.set(globalUsingsPath, {
80+
path: globalUsingsPath,
81+
rootNode: csharpParser.parse(globalUsingsContent).rootNode,
82+
});
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
// For each extracted file, check if the using directives are still valid
90+
const nsMapper = new CSharpNamespaceMapper(parsedFiles);
91+
const pjMapper = new CSharpProjectMapper(csprojFiles);
92+
const usingResolver = new CSharpUsingResolver(nsMapper, pjMapper);
93+
for (const extractedFile of extractedFiles) {
94+
const imports = extractedFile.imports;
95+
for (const importDirective of imports) {
96+
const resolvedInNewFile =
97+
usingResolver.resolveUsingDirective(importDirective);
98+
const resolvedInOldFile =
99+
extractor.usingResolver.resolveUsingDirective(importDirective);
100+
if (
101+
resolvedInNewFile instanceof ExternalSymbol &&
102+
resolvedInOldFile instanceof InternalSymbol
103+
) {
104+
extractedFile.imports = extractedFile.imports.filter(
105+
(imp) => imp !== importDirective,
106+
);
39107
}
40108
}
41109
}
110+
// Actually extract the files
42111
const subprojects: DotNetProject[] = [];
43112
const extractedFilesMap: ExtractedFilesMap = new Map();
44113
for (const extractedFile of extractedFiles) {
@@ -62,9 +131,11 @@ export function extractCSharpSymbols(
62131
`${name}.cs`,
63132
);
64133
if (!extractedFilesMap.has(key)) {
134+
const filecontent = extractor.getContent(extractedFile);
135+
// Add the extracted file to the output map
65136
extractedFilesMap.set(key, {
66137
path: key,
67-
content: extractor.getContent(extractedFile),
138+
content: filecontent,
68139
});
69140
}
70141
}
@@ -73,6 +144,7 @@ export function extractCSharpSymbols(
73144
const projectPath = path.join(subproject.name, `${subproject.name}.csproj`);
74145
const globalUsingPath = path.join(subproject.name, "GlobalUsings.cs");
75146
if (!extractedFilesMap.has(projectPath)) {
147+
// Add the project files to the output map
76148
extractedFilesMap.set(projectPath, {
77149
path: projectPath,
78150
content: subproject.csprojContent,

0 commit comments

Comments
 (0)