mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
TS: Resolve imports using TypeScript symbols
This commit is contained in:
@@ -7,7 +7,9 @@
|
||||
* Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
when the import refers to a file generated by TypeScript.
|
||||
|
||||
- The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
|
||||
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
|
||||
@@ -251,8 +251,13 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isNamedNodeWithSymbol(node)) {
|
||||
let symbol = typeChecker.getSymbolAtLocation(node.name);
|
||||
let symbolNode =
|
||||
isNamedNodeWithSymbol(node) ? node.name :
|
||||
ts.isImportDeclaration(node) ? node.moduleSpecifier :
|
||||
ts.isExternalModuleReference(node) ? node.expression :
|
||||
null;
|
||||
if (symbolNode != null) {
|
||||
let symbol = typeChecker.getSymbolAtLocation(symbolNode);
|
||||
if (symbol != null) {
|
||||
node.$symbol = typeTable.getSymbolId(symbol);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.semmle.js.ast;
|
||||
|
||||
import com.semmle.ts.ast.INodeWithSymbol;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -14,13 +15,15 @@ import java.util.List;
|
||||
* import "m";
|
||||
* </pre>
|
||||
*/
|
||||
public class ImportDeclaration extends Statement {
|
||||
public class ImportDeclaration extends Statement implements INodeWithSymbol {
|
||||
/** List of import specifiers detailing how declarations are imported; may be empty. */
|
||||
private final List<ImportSpecifier> specifiers;
|
||||
|
||||
/** The module from which declarations are imported. */
|
||||
private final Literal source;
|
||||
|
||||
private int symbol = -1;
|
||||
|
||||
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source) {
|
||||
super("ImportDeclaration", loc);
|
||||
this.specifiers = specifiers;
|
||||
@@ -39,4 +42,14 @@ public class ImportDeclaration extends Statement {
|
||||
public <C, R> R accept(Visitor<C, R> v, C c) {
|
||||
return v.visit(this, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSymbol(int symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1555,6 +1555,7 @@ public class ASTExtractor {
|
||||
Label lbl = super.visit(nd, c);
|
||||
visit(nd.getSource(), lbl, -1);
|
||||
visitAll(nd.getSpecifiers(), lbl);
|
||||
emitNodeSymbol(nd, lbl);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
@@ -1705,6 +1706,7 @@ public class ASTExtractor {
|
||||
public Label visit(ExternalModuleReference nd, Context c) {
|
||||
Label key = super.visit(nd, c);
|
||||
visit(nd.getExpression(), key, 0);
|
||||
emitNodeSymbol(nd, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -2061,12 +2063,14 @@ public class ASTExtractor {
|
||||
|
||||
@Override
|
||||
public Label visit(AssignmentPattern nd, Context c) {
|
||||
additionalErrors.add(new ParseError("Unexpected assignment pattern.", nd.getLoc().getStart()));
|
||||
additionalErrors.add(
|
||||
new ParseError("Unexpected assignment pattern.", nd.getLoc().getStart()));
|
||||
return super.visit(nd, c);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ParseError> extract(Node root, Platform platform, SourceType sourceType, int toplevelKind) {
|
||||
public List<ParseError> extract(
|
||||
Node root, Platform platform, SourceType sourceType, int toplevelKind) {
|
||||
lexicalExtractor.getMetrics().startPhase(ExtractionPhase.ASTExtractor_extract);
|
||||
trapwriter.addTuple("toplevels", toplevelLabel, toplevelKind);
|
||||
locationManager.emitNodeLocation(root, toplevelLabel);
|
||||
|
||||
@@ -1202,7 +1202,9 @@ public class TypeScriptASTConverter {
|
||||
|
||||
private Node convertExternalModuleReference(JsonObject node, SourceLocation loc)
|
||||
throws ParseError {
|
||||
return new ExternalModuleReference(loc, convertChild(node, "expression"));
|
||||
ExternalModuleReference moduleRef = new ExternalModuleReference(loc, convertChild(node, "expression"));
|
||||
attachSymbolInformation(moduleRef, node);
|
||||
return moduleRef;
|
||||
}
|
||||
|
||||
private Node convertFalseKeyword(SourceLocation loc) {
|
||||
@@ -1366,7 +1368,9 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ImportDeclaration(loc, specifiers, src);
|
||||
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src);
|
||||
attachSymbolInformation(importDecl, node);
|
||||
return importDecl;
|
||||
}
|
||||
|
||||
private Node convertImportEqualsDeclaration(JsonObject node, SourceLocation loc)
|
||||
|
||||
@@ -4,8 +4,9 @@ import com.semmle.js.ast.Expression;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
public class ExternalModuleReference extends Expression {
|
||||
public class ExternalModuleReference extends Expression implements INodeWithSymbol {
|
||||
private final Expression expression;
|
||||
private int symbol = -1;
|
||||
|
||||
public ExternalModuleReference(SourceLocation loc, Expression expression) {
|
||||
super("ExternalModuleReference", loc);
|
||||
@@ -20,4 +21,14 @@ public class ExternalModuleReference extends Expression {
|
||||
public <C, R> R accept(Visitor<C, R> v, C c) {
|
||||
return v.visit(this, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSymbol(int symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,16 @@ abstract class Import extends ASTNode {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the imported module, as determined by the TypeScript compiler, if any.
|
||||
*/
|
||||
private Module resolveFromTypeScriptSymbol() {
|
||||
exists(CanonicalName symbol |
|
||||
ast_node_symbol(this, symbol) and
|
||||
ast_node_symbol(result, symbol)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the module this import refers to.
|
||||
*
|
||||
@@ -162,7 +172,8 @@ abstract class Import extends ASTNode {
|
||||
else (
|
||||
result = resolveAsProvidedModule() or
|
||||
result = resolveImportedPath() or
|
||||
result = resolveFromTypeRoot()
|
||||
result = resolveFromTypeRoot() or
|
||||
result = resolveFromTypeScriptSymbol()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -688,7 +688,7 @@ case @symbol.kind of
|
||||
;
|
||||
|
||||
@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype;
|
||||
@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr;
|
||||
@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr | @importdeclaration | @externalmodulereference;
|
||||
|
||||
ast_node_symbol(
|
||||
unique int node: @ast_node_with_symbol ref,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
symbols
|
||||
| src/lib/foo.ts:1:1:4:0 | <toplevel> | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
| src/lib/foo.ts:1:8:3:1 | functio ... 123;\\n} | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
| test/test_foo.ts:1:1:1:28 | import ... @/foo"; | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
| test/test_foo.ts:1:1:7:0 | <toplevel> | library-tests/TypeScript/PathMapping/test/test_foo.ts |
|
||||
| test/test_foo.ts:2:17:2:32 | require("@/foo") | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
| test/test_foo.ts:4:1:4:5 | foo() | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
| test/test_foo.ts:6:1:6:12 | foolib.foo() | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
|
||||
#select
|
||||
| test/test_foo.ts:1:1:1:28 | import ... @/foo"; | src/lib/foo.ts:1:1:4:0 | <toplevel> |
|
||||
| test/test_foo.ts:2:17:2:32 | require("@/foo") | src/lib/foo.ts:1:1:4:0 | <toplevel> |
|
||||
@@ -0,0 +1,8 @@
|
||||
import javascript
|
||||
|
||||
query predicate symbols(ASTNode astNode, CanonicalName symbol) {
|
||||
ast_node_symbol(astNode, symbol)
|
||||
}
|
||||
|
||||
from Import imprt
|
||||
select imprt, imprt.getImportedModule()
|
||||
@@ -0,0 +1,3 @@
|
||||
export function foo() {
|
||||
return 123;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { foo } from "@/foo";
|
||||
import foolib = require("@/foo");
|
||||
|
||||
foo();
|
||||
|
||||
foolib.foo();
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"include": ["."],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/lib/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user