JavaScript: Recognize imports from TypeScript type annotations

This commit is contained in:
Ellen Arteca
2019-06-20 05:45:30 -04:00
committed by Max Schaefer
parent a3d5d2c8e4
commit 99c32f08fb
12 changed files with 156 additions and 5 deletions

View File

@@ -542,13 +542,13 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation {
* without type information.
*/
override Type getType() { ast_node_type(this, result) }
override Stmt getEnclosingStmt() { result = ExprOrType.super.getEnclosingStmt() }
override Function getEnclosingFunction() { result = ExprOrType.super.getEnclosingFunction() }
override StmtContainer getContainer() { result = ExprOrType.super.getContainer() }
override TopLevel getTopLevel() { result = ExprOrType.super.getTopLevel() }
}
@@ -1474,6 +1474,44 @@ class ExternalModuleScope extends @externalmodulescope, Scope {
override string toString() { result = "external module scope" }
}
/**
* A reference to a global variable for which there is a
* TypeScript type annotation suggesting that it contains
* the namespace object of a module.
*
* For example:
* ```
* import * as net_outer from "net"
* declare global {
* var net: typeof net_outer
* }
*
* var s = net.createServer(); // this reference to net is an import
* ```
*/
class TSGlobalDeclImport extends DataFlow::ModuleImportNode::Range {
string path;
TSGlobalDeclImport() {
exists(
GlobalVariable gv, VariableDeclarator vd, TypeofTypeExpr tt, LocalVarTypeAccess pkg,
BulkImportDeclaration i
|
// gv is declared with type "typeof pkg"
vd.getBindingPattern() = gv.getADeclaration() and
tt = vd.getTypeAnnotation() and
pkg = tt.getExpressionName() and
// then, check pkg is imported as "import * as pkg from path"
i.getLocal().getVariable() = pkg.getVariable() and
path = i.getImportedPath().getValue() and
// finally, "this" needs to be a reference to gv
this = DataFlow::exprNode(gv.getAnAccess())
)
}
override string getPath() { result = path }
}
/**
* A TypeScript comment of one of the two forms:
* ```

View File

@@ -0,0 +1,19 @@
| amd1.js:1:25:1:26 | fs | fs |
| amd2.js:2:12:2:24 | require('fs') | fs |
| client1.ts:4:28:4:29 | F1 | framework1 |
| client1.ts:6:9:6:11 | net | net |
| client2.ts:4:28:4:29 | F2 | framework2 |
| client2_lazy.ts:4:28:4:29 | F2 | framework2 |
| declare-module-client2.ts:3:1:3:22 | import ... 'foo'; | foo |
| declare-module-client.ts:3:1:3:22 | import ... 'foo'; | foo |
| destructuringES6.js:1:1:1:41 | import ... ctron'; | electron |
| destructuringRequire.js:1:27:1:45 | require('electron') | electron |
| instanceThroughDefaultImport.js:1:1:1:82 | import ... tance'; | myDefaultImportedModuleInstance |
| instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName | myDefaultImportedModuleInstance |
| instanceThroughNamespaceImport.js:1:8:1:49 | myNamespaceImportedModuleInstanceName | myNamespaceImportedModuleInstance |
| instanceThroughRequire.js:1:36:1:70 | require ... tance') | myRequiredModuleInstance |
| moduleUses.js:1:11:1:24 | require('mod') | mod |
| process2.js:1:1:1:13 | require('fs') | fs |
| process2.js:2:10:2:16 | process | process |
| process.js:1:10:1:27 | require('process') | process |
| process.js:2:10:2:23 | global.process | process |

View File

@@ -0,0 +1,5 @@
import javascript
// this will catch the new import style
from DataFlow::ModuleImportNode m
select m, m.getPath()

View File

@@ -0,0 +1,6 @@
// <reference path="framework1.d.ts" />
//import * as F from 'framework1';
var a : F1.Component = new F1.Component();
var b : Util.DefaultComponent = new Util.DefaultComponent();
var s = net.createServer();

View File

@@ -0,0 +1,5 @@
// <reference types="framework2" />
//import * as F from 'framework2';
var a : F2.Component = new F2.Component();
var b : Util2.DefaultComponent = new Util2.DefaultComponent();

View File

@@ -0,0 +1,5 @@
// TypeScript finds the "framework2/index.d.ts" file even without the reference comment.
//import * as F from 'framework2';
var a : F2.Component = new F2.Component();
var b : Util2.DefaultComponent = new Util2.DefaultComponent();

View File

@@ -0,0 +1,5 @@
/// <reference path="declare-module.d.ts"/>
import {C} from 'foo';
var x: C;

View File

@@ -0,0 +1,5 @@
/// <reference path="./declare-module.d.ts"/>
import {C} from 'foo';
var x: C;

View File

@@ -0,0 +1,3 @@
declare module "foo" {
class C {}
}

View File

@@ -0,0 +1,10 @@
// <reference types="framework2" />
import * as F1_outer from 'framework1';
import * as F2_outer from 'framework2';
import * as net_outer from "net";
declare global {
var F1: typeof F1_outer
var F2: typeof F2_outer
var net: typeof net_outer
}

View File

@@ -0,0 +1,13 @@
declare namespace __Framework1 {
class Component {}
}
declare module "framework1" {
export = __Framework1
}
declare namespace Util {
import Framework1 = __Framework1;
class DefaultComponent extends Framework1.Component {}
}

View File

@@ -1,6 +1,10 @@
test_ModuleImportNode
| amd1.js:1:25:1:26 | fs | fs | amd1.js:2:3:2:4 | fs | fs |
| amd2.js:2:12:2:24 | require('fs') | fs | amd2.js:3:3:3:4 | fs | fs |
| client1.ts:4:28:4:29 | F1 | framework1 | client1.ts:4:28:4:29 | F1 | F1 |
| client1.ts:6:9:6:11 | net | net | client1.ts:6:9:6:11 | net | net |
| client2.ts:4:28:4:29 | F2 | framework2 | client2.ts:4:28:4:29 | F2 | F2 |
| client2_lazy.ts:4:28:4:29 | F2 | framework2 | client2_lazy.ts:4:28:4:29 | F2 | F2 |
| instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName | myDefaultImportedModuleInstance | instanceThroughDefaultImport.js:2:1:2:35 | myDefau ... nceName | myDefaultImportedModuleInstanceName |
| instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName | myDefaultImportedModuleInstance | instanceThroughDefaultImport.js:4:5:4:39 | myDefau ... nceName | myDefaultImportedModuleInstanceName |
| instanceThroughNamespaceImport.js:1:8:1:49 | myNamespaceImportedModuleInstanceName | myNamespaceImportedModuleInstance | instanceThroughNamespaceImport.js:2:1:2:37 | myNames ... nceName | myNamespaceImportedModuleInstanceName |
@@ -15,12 +19,20 @@ test_ModuleImportNode
| moduleUses.js:1:11:1:24 | require('mod') | mod | moduleUses.js:15:5:15:7 | mod | mod |
| process2.js:2:10:2:16 | process | process | process2.js:2:10:2:16 | process | process |
test_ModuleImportNode_getAConstructorInvocation
| client1.ts:4:28:4:29 | F1 | client1.ts:4:24:4:41 | new F1.Component() |
| client2.ts:4:28:4:29 | F2 | client2.ts:4:24:4:41 | new F2.Component() |
| client2_lazy.ts:4:28:4:29 | F2 | client2_lazy.ts:4:24:4:41 | new F2.Component() |
| destructuringES6.js:1:1:1:41 | import ... ctron'; | destructuringES6.js:2:1:2:19 | new BrowserWindow() |
| destructuringRequire.js:1:27:1:45 | require('electron') | destructuringRequire.js:2:1:2:19 | new BrowserWindow() |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:9:1:9:7 | new K() |
test_moduleImport
| electron | destructuringES6.js:1:1:1:41 | import ... ctron'; |
| electron | destructuringRequire.js:1:27:1:45 | require('electron') |
| foo | declare-module-client2.ts:3:1:3:22 | import ... 'foo'; |
| foo | declare-module-client.ts:3:1:3:22 | import ... 'foo'; |
| framework1 | client1.ts:4:28:4:29 | F1 |
| framework2 | client2.ts:4:28:4:29 | F2 |
| framework2 | client2_lazy.ts:4:28:4:29 | F2 |
| fs | amd1.js:1:25:1:26 | fs |
| fs | amd2.js:2:12:2:24 | require('fs') |
| fs | process2.js:1:1:1:13 | require('fs') |
@@ -29,12 +41,16 @@ test_moduleImport
| myDefaultImportedModuleInstance | instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName |
| myNamespaceImportedModuleInstance | instanceThroughNamespaceImport.js:1:8:1:49 | myNamespaceImportedModuleInstanceName |
| myRequiredModuleInstance | instanceThroughRequire.js:1:36:1:70 | require ... tance') |
| net | client1.ts:6:9:6:11 | net |
| process | process2.js:2:10:2:16 | process |
| process | process.js:1:10:1:27 | require('process') |
| process | process.js:2:10:2:23 | global.process |
test_moduleMember
| electron | BrowserWindow | destructuringES6.js:1:10:1:22 | BrowserWindow |
| electron | BrowserWindow | destructuringRequire.js:1:9:1:21 | BrowserWindow |
| framework1 | Component | client1.ts:4:28:4:39 | F1.Component |
| framework2 | Component | client2.ts:4:28:4:39 | F2.Component |
| framework2 | Component | client2_lazy.ts:4:28:4:39 | F2.Component |
| fs | readFileSync | amd1.js:2:3:2:17 | fs.readFileSync |
| fs | readFileSync | amd2.js:3:3:3:17 | fs.readFileSync |
| mod | constructorFunction | moduleUses.js:8:9:8:31 | mod.con ... unction |
@@ -42,9 +58,16 @@ test_moduleMember
| mod | moduleFunction | moduleUses.js:5:9:5:26 | mod.moduleFunction |
| mod | moduleMethod | moduleUses.js:3:1:3:16 | mod.moduleMethod |
| myDefaultImportedModuleInstance | default | instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName |
| net | createServer | client1.ts:6:9:6:24 | net.createServer |
test_ModuleImportNode_getPath
| amd1.js:1:25:1:26 | fs | fs |
| amd2.js:2:12:2:24 | require('fs') | fs |
| client1.ts:4:28:4:29 | F1 | framework1 |
| client1.ts:6:9:6:11 | net | net |
| client2.ts:4:28:4:29 | F2 | framework2 |
| client2_lazy.ts:4:28:4:29 | F2 | framework2 |
| declare-module-client2.ts:3:1:3:22 | import ... 'foo'; | foo |
| declare-module-client.ts:3:1:3:22 | import ... 'foo'; | foo |
| destructuringES6.js:1:1:1:41 | import ... ctron'; | electron |
| destructuringRequire.js:1:27:1:45 | require('electron') | electron |
| instanceThroughDefaultImport.js:1:1:1:82 | import ... tance'; | myDefaultImportedModuleInstance |
@@ -59,10 +82,15 @@ test_ModuleImportNode_getPath
test_ModuleImportNode_getAMethodCall
| amd1.js:1:25:1:26 | fs | amd1.js:2:3:2:29 | fs.read ... a.txt") |
| amd2.js:2:12:2:24 | require('fs') | amd2.js:3:3:3:29 | fs.read ... a.txt") |
| client1.ts:6:9:6:11 | net | client1.ts:6:9:6:26 | net.createServer() |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:3:1:3:18 | mod.moduleMethod() |
test_ModuleImportNode_getAMemberInvocation
| amd1.js:1:25:1:26 | fs | amd1.js:2:3:2:29 | fs.read ... a.txt") |
| amd2.js:2:12:2:24 | require('fs') | amd2.js:3:3:3:29 | fs.read ... a.txt") |
| client1.ts:4:28:4:29 | F1 | client1.ts:4:24:4:41 | new F1.Component() |
| client1.ts:6:9:6:11 | net | client1.ts:6:9:6:26 | net.createServer() |
| client2.ts:4:28:4:29 | F2 | client2.ts:4:24:4:41 | new F2.Component() |
| client2_lazy.ts:4:28:4:29 | F2 | client2_lazy.ts:4:24:4:41 | new F2.Component() |
| destructuringES6.js:1:1:1:41 | import ... ctron'; | destructuringES6.js:2:1:2:19 | new BrowserWindow() |
| destructuringRequire.js:1:27:1:45 | require('electron') | destructuringRequire.js:2:1:2:19 | new BrowserWindow() |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:3:1:3:18 | mod.moduleMethod() |
@@ -71,6 +99,9 @@ test_ModuleImportNode_getAMemberInvocation
test_moduleImportProp
| electron | BrowserWindow | destructuringES6.js:1:10:1:22 | BrowserWindow |
| electron | BrowserWindow | destructuringRequire.js:1:9:1:21 | BrowserWindow |
| framework1 | Component | client1.ts:4:28:4:39 | F1.Component |
| framework2 | Component | client2.ts:4:28:4:39 | F2.Component |
| framework2 | Component | client2_lazy.ts:4:28:4:39 | F2.Component |
| fs | readFileSync | amd1.js:2:3:2:17 | fs.readFileSync |
| fs | readFileSync | amd2.js:3:3:3:17 | fs.readFileSync |
| mod | constructorFunction | moduleUses.js:8:9:8:31 | mod.con ... unction |
@@ -78,18 +109,24 @@ test_moduleImportProp
| mod | moduleFunction | moduleUses.js:5:9:5:26 | mod.moduleFunction |
| mod | moduleMethod | moduleUses.js:3:1:3:16 | mod.moduleMethod |
| myDefaultImportedModuleInstance | default | instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName |
| net | createServer | client1.ts:6:9:6:24 | net.createServer |
test_ModuleImportNode_getAMemberCall
| amd1.js:1:25:1:26 | fs | amd1.js:2:3:2:29 | fs.read ... a.txt") |
| amd2.js:2:12:2:24 | require('fs') | amd2.js:3:3:3:29 | fs.read ... a.txt") |
| client1.ts:6:9:6:11 | net | client1.ts:6:9:6:26 | net.createServer() |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:3:1:3:18 | mod.moduleMethod() |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:6:1:6:3 | f() |
test_ModuleImportNode_getAPropertyRead
| amd1.js:1:25:1:26 | fs | amd1.js:2:3:2:17 | fs.readFileSync |
| amd2.js:2:12:2:24 | require('fs') | amd2.js:3:3:3:17 | fs.readFileSync |
| client1.ts:4:28:4:29 | F1 | client1.ts:4:28:4:39 | F1.Component |
| client1.ts:6:9:6:11 | net | client1.ts:6:9:6:24 | net.createServer |
| client2.ts:4:28:4:29 | F2 | client2.ts:4:28:4:39 | F2.Component |
| client2_lazy.ts:4:28:4:29 | F2 | client2_lazy.ts:4:28:4:39 | F2.Component |
| destructuringES6.js:1:1:1:41 | import ... ctron'; | destructuringES6.js:1:10:1:22 | BrowserWindow |
| destructuringRequire.js:1:27:1:45 | require('electron') | destructuringRequire.js:1:9:1:21 | BrowserWindow |
| instanceThroughDefaultImport.js:1:1:1:82 | import ... tance'; | instanceThroughDefaultImport.js:1:8:1:42 | myDefaultImportedModuleInstanceName |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:3:1:3:16 | mod.moduleMethod |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:5:9:5:26 | mod.moduleFunction |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:8:9:8:31 | mod.con ... unction |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:11:1:11:15 | mod.moduleField |
| moduleUses.js:1:11:1:24 | require('mod') | moduleUses.js:11:1:11:15 | mod.moduleField |