Skip to content

Commit a311899

Browse files
authored
JS: Populate declaring type on standalone function declarations (#7222)
Standalone function declarations/expressions previously got Type.unknownType as their declaring type, making JavaType.Method.getDeclaringType() return "<unknown>". Derive the module FQN from the source file path instead, matching how Go's type_mapper already handles package-level functions. Fixes moderneinc/customer-requests#2137 (JS portion)
1 parent c6071dd commit a311899

2 files changed

Lines changed: 56 additions & 1 deletion

File tree

rewrite-javascript/rewrite/src/javascript/type-mapping.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,24 @@ export class JavaScriptTypeMapping {
944944
}
945945

946946
methodName = node.name ? node.name.getText() : "<anonymous>";
947-
declaringType = Type.unknownType as Type.FullyQualified;
947+
948+
// Derive declaring type from source file module path (like Go's type_mapper.go).
949+
// Use the same relativization as getFullyQualifiedName() so that declarations
950+
// and invocations produce matching FQNs.
951+
let moduleFqn: string;
952+
const fileName = node.getSourceFile().fileName;
953+
if (this.sourceRoot && path.isAbsolute(fileName)) {
954+
moduleFqn = path.relative(this.sourceRoot, fileName);
955+
} else {
956+
moduleFqn = fileName;
957+
}
958+
// Strip file extension to get the module name
959+
moduleFqn = moduleFqn.replace(/\.[^/.]+$/, '');
960+
declaringType = {
961+
kind: Type.Kind.Class,
962+
flags: 0,
963+
fullyQualifiedName: moduleFqn
964+
} as Type.FullyQualified;
948965

949966
// Get type parameters from node
950967
if (node.typeParameters) {

rewrite-javascript/rewrite/test/javascript/type-mapping.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,44 @@ describe('JavaScript type mapping', () => {
18951895
);
18961896
});
18971897

1898+
test('standalone function declaration has module-derived declaringType', async () => {
1899+
const spec = new RecipeSpec();
1900+
spec.recipe = new class extends Recipe {
1901+
name = 'Check function declaringType';
1902+
displayName = 'Check function declaringType';
1903+
description = 'Checks that standalone function declarations get a module-derived declaringType';
1904+
1905+
async editor(): Promise<JavaScriptVisitor<ExecutionContext>> {
1906+
return new class extends JavaScriptVisitor<ExecutionContext> {
1907+
async visitMethodDeclaration(methodDecl: J.MethodDeclaration, p: ExecutionContext): Promise<J.MethodDeclaration> {
1908+
const visited = await super.visitMethodDeclaration(methodDecl, p) as J.MethodDeclaration;
1909+
if (visited.methodType?.declaringType) {
1910+
const dt = visited.methodType.declaringType as Type.FullyQualified;
1911+
if (Type.isClass(dt)) {
1912+
const fqn = dt.fullyQualifiedName;
1913+
return foundSearchResult(visited, fqn);
1914+
}
1915+
}
1916+
return visited;
1917+
}
1918+
};
1919+
}
1920+
};
1921+
1922+
const src = typescript(
1923+
`
1924+
function helper(): void {}
1925+
`,
1926+
//@formatter:off
1927+
`
1928+
/*~~(main)~~>*/function helper(): void {}
1929+
`
1930+
//@formatter:on
1931+
);
1932+
src.path = 'main.ts';
1933+
await spec.rewriteRun(src);
1934+
});
1935+
18981936
test('FindMissingTypes produces no results on a complex class', async () => {
18991937
const findings: string[] = [];
19001938

0 commit comments

Comments
 (0)