mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
94 lines
3.2 KiB
Plaintext
94 lines
3.2 KiB
Plaintext
/**
|
|
* @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
|