Skip to content

Commit f48e4bc

Browse files
authored
Merge pull request #2580 from asger-semmle/typescript-unbounded-recursion
Approved by max-schaefer
2 parents 0c0073f + f31d47c commit f48e4bc

File tree

8 files changed

+61
-8
lines changed

8 files changed

+61
-8
lines changed

javascript/extractor/lib/typescript/src/ast_extractor.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ function forEachNode(ast: ts.Node, callback: (node: ts.Node) => void) {
6767
visit(ast);
6868
}
6969

70+
function tryGetTypeOfNode(typeChecker: ts.TypeChecker, node: AugmentedNode): ts.Type | null {
71+
try {
72+
return typeChecker.getTypeAtLocation(node);
73+
} catch (e) {
74+
let sourceFile = node.getSourceFile();
75+
let { line, character } = sourceFile.getLineAndCharacterOfPosition(node.pos);
76+
console.warn(`Could not compute type of ${ts.SyntaxKind[node.kind]} at ${sourceFile.fileName}:${line+1}:${character+1}`);
77+
return null;
78+
}
79+
}
80+
7081
export function augmentAst(ast: AugmentedSourceFile, code: string, project: Project | null) {
7182
ast.$lineStarts = ast.getLineStarts();
7283

@@ -196,7 +207,7 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
196207
let contextualType = isContextuallyTypedNode(node)
197208
? typeChecker.getContextualType(node)
198209
: null;
199-
let type = contextualType || typeChecker.getTypeAtLocation(node);
210+
let type = contextualType || tryGetTypeOfNode(typeChecker, node);
200211
if (type != null) {
201212
let parent = node.parent;
202213
let unfoldAlias = ts.isTypeAliasDeclaration(parent) && node === parent.type;

javascript/extractor/lib/typescript/src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ function parseSingleFile(filename: string): {ast: ts.SourceFile, code: string} {
243243
}
244244

245245
function handleOpenProjectCommand(command: OpenProjectCommand) {
246+
Error.stackTraceLimit = Infinity;
246247
let tsConfigFilename = String(command.tsConfig);
247248
let tsConfig = ts.readConfigFile(tsConfigFilename, ts.sys.readFile);
248249
let basePath = pathlib.dirname(tsConfigFilename);

javascript/extractor/lib/typescript/src/type_table.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,16 @@ export class TypeTable {
766766
return hash;
767767
}
768768

769+
/** Returns the type of `symbol` or `null` if it could not be computed. */
770+
private tryGetTypeOfSymbol(symbol: ts.Symbol) {
771+
try {
772+
return this.typeChecker.getTypeOfSymbolAtLocation(symbol, this.arbitraryAstNode)
773+
} catch (e) {
774+
console.warn(`Could not compute type of '${this.typeChecker.symbolToString(symbol)}'`);
775+
return null;
776+
}
777+
}
778+
769779
/**
770780
* Returns a type string consisting of all the members of the given type.
771781
*
@@ -775,7 +785,7 @@ export class TypeTable {
775785
private makeStructuralTypeVector(tag: string, type: ts.ObjectType): string | null {
776786
let hash = tag;
777787
for (let property of type.getProperties()) {
778-
let propertyType = this.typeChecker.getTypeOfSymbolAtLocation(property, this.arbitraryAstNode);
788+
let propertyType = this.tryGetTypeOfSymbol(property);
779789
if (propertyType == null) return null;
780790
let propertyTypeId = this.getId(propertyType, false);
781791
if (propertyTypeId == null) return null;
@@ -882,7 +892,7 @@ export class TypeTable {
882892
let props = this.tryGetProperties(type);
883893
if (props == null) return;
884894
for (let symbol of props) {
885-
let propertyType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, this.arbitraryAstNode);
895+
let propertyType = this.tryGetTypeOfSymbol(symbol);
886896
if (propertyType == null) continue;
887897
let propertyTypeId = this.getId(propertyType, false);
888898
if (propertyTypeId == null) continue;
@@ -938,7 +948,7 @@ export class TypeTable {
938948
}
939949
if (parameters.length === 0) return null;
940950
let restParameter = parameters[parameters.length - 1];
941-
let restParameterType = this.typeChecker.getTypeOfSymbolAtLocation(restParameter, this.arbitraryAstNode);
951+
let restParameterType = this.tryGetTypeOfSymbol(restParameter);
942952
if (restParameterType == null) return null;
943953
let restParameterTypeId = this.getId(restParameterType, false);
944954
if (restParameterTypeId == null) return null;
@@ -961,7 +971,7 @@ export class TypeTable {
961971
}
962972
for (let paramIndex = 0; paramIndex < parameters.length; ++paramIndex) {
963973
let parameter = parameters[paramIndex];
964-
let parameterType = this.typeChecker.getTypeOfSymbolAtLocation(parameter, this.arbitraryAstNode);
974+
let parameterType = this.tryGetTypeOfSymbol(parameter);
965975
if (parameterType == null) {
966976
return null;
967977
}
@@ -1178,7 +1188,7 @@ export class TypeTable {
11781188
stack.push(id);
11791189

11801190
for (let symbol of type.getProperties()) {
1181-
let propertyType: ts.Type = typeTable.typeChecker.getTypeOfSymbolAtLocation(symbol, typeTable.arbitraryAstNode);
1191+
let propertyType = this.tryGetTypeOfSymbol(symbol);
11821192
if (propertyType == null) continue;
11831193
traverseType(propertyType);
11841194
}
@@ -1255,7 +1265,7 @@ export class TypeTable {
12551265
if (objectFlags & ts.ObjectFlags.Anonymous) {
12561266
// Anonymous interface type like `{ x: number }`.
12571267
for (let symbol of type.getProperties()) {
1258-
let propertyType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, this.arbitraryAstNode);
1268+
let propertyType = this.tryGetTypeOfSymbol(symbol);
12591269
if (propertyType == null) continue;
12601270
callback(propertyType);
12611271
}
@@ -1280,7 +1290,7 @@ export class TypeTable {
12801290
private forEachChildTypeOfSignature(signature: ts.Signature, callback: (type: ts.Type) => void): void {
12811291
callback(signature.getReturnType());
12821292
for (let parameter of signature.getParameters()) {
1283-
let paramType = this.typeChecker.getTypeOfSymbolAtLocation(parameter, this.arbitraryAstNode);
1293+
let paramType = this.tryGetTypeOfSymbol(parameter);
12841294
if (paramType == null) continue;
12851295
callback(paramType);
12861296
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { A } from "@blah/foo";
2+
3+
var x: A;

javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportSelf/node_modules/@blah/foo/index.d.ts

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
| bar.ts:1:10:1:10 | A | any |
2+
| bar.ts:1:10:1:10 | A | any |
3+
| bar.ts:1:19:1:29 | "@blah/foo" | any |
4+
| bar.ts:3:5:3:5 | x | any |
5+
| node_modules/@blah/foo/index.d.ts:1:8:1:15 | FooArray | typeof FooArray in library-tests/TypeScript/RegressionTests/ImportSelf/node_modules/@blah/foo/index.d.ts |
6+
| node_modules/@blah/foo/index.d.ts:1:20:1:20 | A | any |
7+
| node_modules/@blah/foo/index.d.ts:1:20:1:20 | A | any |
8+
| node_modules/@blah/foo/index.d.ts:1:29:1:39 | '@blah/foo' | any |
9+
| node_modules/@blah/foo/index.d.ts:3:11:3:18 | FooArray | typeof FooArray in library-tests/TypeScript/RegressionTests/ImportSelf/node_modules/@blah/foo/index.d.ts |
10+
| node_modules/@blah/foo/index.d.ts:4:12:4:15 | blah | () => void |
11+
| node_modules/@blah/foo/index.d.ts:8:10:8:10 | A | any |
12+
| node_modules/@blah/foo/index.d.ts:8:10:8:10 | A | any |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import javascript
2+
3+
// We're mainly testing extraction succeeds, so just test that some types are extracted.
4+
5+
from Expr e select e, e.getType()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["."]
3+
}

0 commit comments

Comments
 (0)