Skip to content

Commit 9206549

Browse files
author
Max Schaefer
committed
JavaScript: Make integration of TypeScript canonical names with modules in API graphs more consistent.
Previously, canonical names were direct successors of module definitions/uses, now they are successors of exports/imports.
1 parent 77abff7 commit 9206549

File tree

3 files changed

+46
-21
lines changed

3 files changed

+46
-21
lines changed

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

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -346,16 +346,30 @@ module API {
346346
exists(SSA::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
347347
)
348348
)
349+
or
350+
m = any(CanonicalName n | isDefined(n)).getExternalModuleName()
351+
} or
352+
MkModuleImport(string m) {
353+
imports(_, m)
354+
or
355+
m = any(CanonicalName n | isUsed(n)).getExternalModuleName()
349356
} or
350-
MkModuleImport(string m) { imports(_, m) } or
351357
MkClassInstance(DataFlow::ClassNode cls) { cls = trackDefNode(_) and hasSemantics(cls) } or
352358
MkAsyncFuncResult(DataFlow::FunctionNode f) {
353359
f = trackDefNode(_) and f.getFunction().isAsync() and hasSemantics(f)
354360
} or
355361
MkDef(DataFlow::Node nd) { rhs(_, _, nd) } or
356362
MkUse(DataFlow::Node nd) { use(_, _, nd) } or
357-
MkCanonicalNameDef(CanonicalName n) { isDefined(n) } or
358-
MkCanonicalNameUse(CanonicalName n) { isUsed(n) }
363+
MkCanonicalNameDef(CanonicalName n) {
364+
// module roots are represented by `MkModuleExport` nodes
365+
not n.isRoot() and
366+
isDefined(n)
367+
} or
368+
MkCanonicalNameUse(CanonicalName n) {
369+
// module roots are represented by `MkModuleImport` nodes
370+
not n.isRoot() and
371+
isUsed(n)
372+
}
359373

360374
class TDef = MkModuleDef or TNonModuleDef;
361375

@@ -399,6 +413,18 @@ module API {
399413
)
400414
}
401415

416+
private TApiNode mkCanonicalNameDef(CanonicalName cn) {
417+
if cn.isModuleRoot()
418+
then result = MkModuleExport(cn.getExternalModuleName())
419+
else result = MkCanonicalNameDef(cn)
420+
}
421+
422+
private TApiNode mkCanonicalNameUse(CanonicalName cn) {
423+
if cn.isModuleRoot()
424+
then result = MkModuleImport(cn.getExternalModuleName())
425+
else result = MkCanonicalNameUse(cn)
426+
}
427+
402428
/**
403429
* Holds if `rhs` is the right-hand side of a definition of a node that should have an
404430
* incoming edge from `base` labeled `lbl` in the API graph.
@@ -698,20 +724,11 @@ module API {
698724
succ = MkClassInstance(trackDefNode(def))
699725
)
700726
or
701-
exists(CanonicalName cn |
702-
pred = MkRoot() and
703-
lbl = Label::mod(cn.getExternalModuleName())
704-
|
705-
succ = MkCanonicalNameUse(cn) or
706-
succ = MkCanonicalNameDef(cn)
707-
)
708-
or
709-
exists(CanonicalName cn1, CanonicalName cn2 |
710-
cn2 = cn1.getAChild() and
711-
lbl = Label::member(cn2.getName())
712-
|
713-
(pred = MkCanonicalNameDef(cn1) or pred = MkCanonicalNameUse(cn1)) and
714-
(succ = MkCanonicalNameDef(cn2) or succ = MkCanonicalNameUse(cn2))
727+
exists(CanonicalName cn1, string n, CanonicalName cn2 |
728+
pred in [mkCanonicalNameDef(cn1), mkCanonicalNameUse(cn1)] and
729+
cn2 = cn1.getChild(n) and
730+
lbl = Label::member(n) and
731+
succ in [mkCanonicalNameDef(cn2), mkCanonicalNameUse(cn2)]
715732
)
716733
or
717734
exists(DataFlow::Node nd, DataFlow::FunctionNode f |

javascript/ql/src/semmle/javascript/CanonicalNames.qll

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ class CanonicalName extends @symbol {
2525
CanonicalName getParent() { symbol_parent(this, result) }
2626

2727
/**
28-
* Gets a child of this canonical name, i.e. an extension of its qualified name.
28+
* Gets a child of this canonical name, that is, an extension of its qualified name.
2929
*/
3030
CanonicalName getAChild() { result.getParent() = this }
3131

32+
/**
33+
* Gets the child of this canonical name that has the given `name`, if any.
34+
*/
35+
CanonicalName getChild(string name) {
36+
result = getAChild() and
37+
result.getName() = name
38+
}
39+
3240
/**
3341
* Gets the name without prefix.
3442
*/

javascript/ql/test/ApiGraphs/typed/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ app.use(bodyParser.json());
1111

1212
app.post("/find", (req, res) => {
1313
let v = JSON.parse(req.body.x);
14-
getCollection().find({ id: v }); /* use (member find (instance (member Collection (module mongodb)))) */
14+
getCollection().find({ id: v }); /* use (member find (instance (member Collection (member exports (module mongodb))))) */
1515
});
1616

1717
import * as mongoose from "mongoose";
1818
declare function getMongooseModel(): mongoose.Model;
1919
declare function getMongooseQuery(): mongoose.Query;
2020
app.post("/find", (req, res) => {
2121
let v = JSON.parse(req.body.x);
22-
getMongooseModel().find({ id: v }); /* def (parameter 0 (member find (instance (member Model (module mongoose))))) */
23-
getMongooseQuery().find({ id: v }); /* def (parameter 0 (member find (instance (member Query (module mongoose))))) */
22+
getMongooseModel().find({ id: v }); /* def (parameter 0 (member find (instance (member Model (member exports (module mongoose)))))) */
23+
getMongooseQuery().find({ id: v }); /* def (parameter 0 (member find (instance (member Query (member exports (module mongoose)))))) */
2424
});

0 commit comments

Comments
 (0)