mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
QL code and tests for C#/C++/JavaScript.
This commit is contained in:
88
javascript/ql/src/NodeJS/CyclicImport.ql
Normal file
88
javascript/ql/src/NodeJS/CyclicImport.ql
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @name Cyclic module import
|
||||
* @description If a module indirectly imports itself, some modules involved in the import cycle may end up
|
||||
* with partially loaded dependencies. This is error-prone and should be avoided.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/node/cyclic-import
|
||||
* @tags reliability
|
||||
* maintainability
|
||||
* frameworks/node.js
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Gets the shortest suffix of the path of `c1` that differs from the
|
||||
* corresponding suffix of `c2`; if that suffix is a proper suffix, it is
|
||||
* additionally prefixed with `.../`.
|
||||
*/
|
||||
string ppDiff(Container c1, Container c2) {
|
||||
relatedAncestors(c1, c2) and
|
||||
if c1.getBaseName() = c2.getBaseName() then
|
||||
result = ppDiff(c1.getParentContainer(), c2.getParentContainer()) + "/" + c1.getBaseName()
|
||||
else if not exists(c1.getParentContainer()) or
|
||||
sourceLocationPrefix(c1.getParentContainer().getAbsolutePath()) then
|
||||
result = "/" + c1.getBaseName()
|
||||
else
|
||||
result = ".../" + c1.getBaseName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c1` and `c2` are related modules (as determined by predicate
|
||||
* `relatedModules`), or (transitive) parent folders of such modules.
|
||||
*
|
||||
* This predicate is used to restrict the domain of `ppDiff`.
|
||||
*/
|
||||
predicate relatedAncestors(Container c1, Container c2) {
|
||||
exists (NodeModule m, NodeModule n | relatedModules(m, n) |
|
||||
c1 = m.getFile() and c2 = n.getFile()
|
||||
) or
|
||||
relatedAncestors(c1.(Folder).getAChildContainer(), c2.(Folder).getAChildContainer())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pretty-printed name for `m` that distinguishes it from `other`:
|
||||
* this is simply the name of `m` it is different from the name of `other`,
|
||||
* or else a suffix of the path of `m` that is different from `other` as
|
||||
* computed by `ppDiff`.
|
||||
*/
|
||||
string pp(NodeModule m, NodeModule other) {
|
||||
relatedModules(m, other) and
|
||||
if m.getName() = other.getName() and m != other then
|
||||
result = ppDiff(m.getFile(), other.getFile())
|
||||
else
|
||||
result = m.getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` imports `n` or vice versa.
|
||||
*
|
||||
* This predicate is used to restrict the domain of `pp`.
|
||||
*/
|
||||
predicate relatedModules(NodeModule m, NodeModule n) {
|
||||
n = m.getAnImportedModule() or m = n.getAnImportedModule()
|
||||
}
|
||||
|
||||
from NodeModule m, Require r, NodeModule imported, string msg,
|
||||
ASTNode linktarget, string linktext
|
||||
where r = m.getAnImport() and
|
||||
imported = r.getImportedModule() and
|
||||
if imported = m then
|
||||
// set linktarget and linktext to dummy values in this case
|
||||
(msg = "directly imports itself" and linktarget = m and linktext = "")
|
||||
else
|
||||
// find an import in `imported` that (directly or indirectly) imports `m`
|
||||
exists (Require r2, Module imported2 | r2 = imported.getAnImport() and imported2 = r2.getImportedModule() |
|
||||
imported2.getAnImportedModule*() = m and
|
||||
msg = "imports module " + pp(imported, m) + ", which in turn $@ it" and
|
||||
linktarget = r2 and
|
||||
// check whether it is a direct or indirect import
|
||||
(if imported2 = m then
|
||||
linktext = "imports"
|
||||
else
|
||||
// only report indirect imports if there is no direct import
|
||||
(linktext = "indirectly imports" and not imported.getAnImportedModule() = m))
|
||||
)
|
||||
select r, "Module " + pp(m, imported) + " " + msg + ".", linktarget, linktext
|
||||
Reference in New Issue
Block a user