Skip to content

Commit c6ac6d4

Browse files
authored
Merge pull request github#2 from max-schaefer/reintroduce-type-defs
Reintroduce type definitions to API graphs
2 parents 09752d4 + 28cf270 commit c6ac6d4

File tree

6 files changed

+118
-12
lines changed

6 files changed

+118
-12
lines changed

javascript/ql/src/semmle/javascript/ApiGraphs.qll

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ module API {
313313
module Node {
314314
/** Gets a node whose type has the given qualified name. */
315315
Node ofType(string moduleName, string exportedName) {
316-
result = Impl::MkHasUnderlyingType(moduleName, exportedName).(Node).getInstance()
316+
result = Impl::MkTypeUse(moduleName, exportedName).(Node).getInstance()
317317
}
318318
}
319319

@@ -374,28 +374,31 @@ module API {
374374
exists(SSA::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
375375
)
376376
)
377+
or
378+
any(TypeDefinition td).getTypeName().hasQualifiedName(m, _)
377379
} or
378380
MkModuleImport(string m) {
379381
imports(_, m)
380382
or
381383
any(TypeAnnotation n).hasQualifiedName(m, _)
382384
or
383-
any(Type t).hasUnderlyingType(m, _)
385+
any(DataFlow::Node n).hasUnderlyingType(m, _)
384386
} or
385387
MkClassInstance(DataFlow::ClassNode cls) { cls = trackDefNode(_) and hasSemantics(cls) } or
386388
MkAsyncFuncResult(DataFlow::FunctionNode f) {
387389
f = trackDefNode(_) and f.getFunction().isAsync() and hasSemantics(f)
388390
} or
389391
MkDef(DataFlow::Node nd) { rhs(_, _, nd) } or
390392
MkUse(DataFlow::Node nd) { use(_, _, nd) } or
391-
/**
392-
* A TypeScript type, identified by name of the type-annotation.
393-
* This API node is exclusively used by `API::Node::ofType`.
394-
*/
395-
MkHasUnderlyingType(string moduleName, string exportName) {
393+
/** A definition of a TypeScript type. */
394+
MkTypeDef(string moduleName, string exportName) {
395+
exists(TypeDefinition td | td.getTypeName().hasQualifiedName(moduleName, exportName))
396+
} or
397+
/** A use of a TypeScript type. */
398+
MkTypeUse(string moduleName, string exportName) {
396399
any(TypeAnnotation n).hasQualifiedName(moduleName, exportName)
397400
or
398-
any(Type t).hasUnderlyingType(moduleName, exportName)
401+
exists(DataFlow::Node nd | nd.hasUnderlyingType(moduleName, exportName))
399402
} or
400403
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
401404
trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
@@ -404,9 +407,10 @@ module API {
404407
class TDef = MkModuleDef or TNonModuleDef;
405408

406409
class TNonModuleDef =
407-
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkSyntheticCallbackArg;
410+
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkTypeDef or
411+
MkSyntheticCallbackArg;
408412

409-
class TUse = MkModuleUse or MkModuleImport or MkUse or MkHasUnderlyingType;
413+
class TUse = MkModuleUse or MkModuleImport or MkUse or MkTypeUse;
410414

411415
private predicate hasSemantics(DataFlow::Node nd) { not nd.getTopLevel().isExterns() }
412416

@@ -584,7 +588,7 @@ module API {
584588
)
585589
or
586590
exists(string moduleName, string exportName |
587-
base = MkHasUnderlyingType(moduleName, exportName) and
591+
base = MkTypeUse(moduleName, exportName) and
588592
lbl = Label::instance() and
589593
ref.(DataFlow::SourceNode).hasUnderlyingType(moduleName, exportName)
590594
)
@@ -821,9 +825,13 @@ module API {
821825
)
822826
or
823827
exists(string moduleName, string exportName |
828+
pred = MkModuleExport(moduleName) and
829+
lbl = Label::member(exportName) and
830+
succ = MkTypeDef(moduleName, exportName)
831+
or
824832
pred = MkModuleImport(moduleName) and
825833
lbl = Label::member(exportName) and
826-
succ = MkHasUnderlyingType(moduleName, exportName)
834+
succ = MkTypeUse(moduleName, exportName)
827835
)
828836
or
829837
exists(DataFlow::Node nd, DataFlow::FunctionNode f |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#-----| root
2+
#-----| module mongoose -> use (module mongoose)
3+
#-----| module typescript-test -> def (module typescript-test)
4+
5+
#-----| def (module typescript-test)
6+
#-----| member exports -> def (member exports (module typescript-test))
7+
8+
#-----| use (module mongoose)
9+
#-----| member exports -> use (member exports (module mongoose))
10+
11+
#-----| def (member exports (module typescript-test))
12+
#-----| member Sized -> def (member Sized (member exports (module typescript-test)))
13+
14+
#-----| use (member exports (module mongoose))
15+
#-----| member Model -> use (member Model (member exports (module mongoose)))
16+
17+
#-----| def (member Sized (member exports (module typescript-test)))
18+
19+
#-----| use (member Model (member exports (module mongoose)))
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @name API graph
3+
* @description Shows the API graph of a code base.
4+
* @kind graph
5+
* @id js/api-graph
6+
*/
7+
8+
import javascript
9+
10+
/** Gets a string representation of the location information for `nd`. */
11+
string locationString(API::Node nd) {
12+
exists(string fp, int sl, int sc, int el, int ec | nd.hasLocationInfo(fp, sl, sc, el, ec) |
13+
result = fp + ":" + sl + ":" + sc + ":" + el + ":" + ec
14+
)
15+
}
16+
17+
/**
18+
* Gets the rank of node `nd` among all nodes ordered by depth, string representation,
19+
* and location.
20+
*/
21+
int nodeRank(API::Node nd) {
22+
nd =
23+
rank[result + 1](API::Node nd2 |
24+
|
25+
nd2 order by nd2.getDepth(), nd2.toString(), locationString(nd2)
26+
)
27+
}
28+
29+
/**
30+
* Gets the rank of `lbl` among all labels on outgoing edges of `pred`, ordered alphabetically.
31+
*/
32+
int labelRank(API::Node pred, string lbl, API::Node succ) {
33+
lbl + "->" + nodeRank(succ) =
34+
rank[result + 1](string l, API::Node s |
35+
s = pred.getASuccessor(l)
36+
or
37+
pred.refersTo(s) and l = ""
38+
|
39+
l + "->" + nodeRank(s)
40+
)
41+
}
42+
43+
query predicate nodes(API::Node f, string key, string value) {
44+
key = "semmle.order" and
45+
value = nodeRank(f).toString()
46+
}
47+
48+
query predicate edges(API::Node pred, API::Node succ, string key, string value) {
49+
exists(string lbl |
50+
succ = pred.getASuccessor(lbl)
51+
or
52+
pred.refersTo(succ) and
53+
lbl = ""
54+
|
55+
key = "semmle.label" and
56+
value = lbl
57+
or
58+
key = "semmle.order" and
59+
value = labelRank(pred, lbl, succ).toString()
60+
)
61+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface Sized {
2+
getSize(): number;
3+
}
4+
5+
import * as mongoose from "mongoose";
6+
var myModel : mongoose.Model;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "typescript-test",
3+
"dependencies": {
4+
"mongoose": "*"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"include": ["."],
3+
"compilerOptions": {
4+
"esModuleInterop": true
5+
}
6+
}

0 commit comments

Comments
 (0)