mirror of
https://github.com/github/codeql.git
synced 2026-03-26 01:08:16 +01:00
130 lines
3.4 KiB
Plaintext
130 lines
3.4 KiB
Plaintext
/**
|
|
* Provides predicates for measuring the quality of the call graph, that is,
|
|
* the number of calls that could be resolved to a callee.
|
|
*/
|
|
|
|
import javascript
|
|
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
|
private import semmle.javascript.dependencies.Dependencies
|
|
private import semmle.javascript.dependencies.FrameworkLibraries
|
|
private import semmle.javascript.frameworks.Testing
|
|
private import DataFlow
|
|
|
|
/**
|
|
* Gets the root folder of the snapshot.
|
|
*
|
|
* This is selected as the location for project-wide metrics.
|
|
*/
|
|
Folder projectRoot() { result.getRelativePath() = "" }
|
|
|
|
/** A file we ignore because it is a test file or compiled/generated/bundled code. */
|
|
class IgnoredFile extends File {
|
|
IgnoredFile() {
|
|
any(Test t).getFile() = this
|
|
or
|
|
getRelativePath().regexpMatch("(?i).*/test(case)?s?/.*")
|
|
or
|
|
getBaseName().regexpMatch("(?i)(.*[._\\-]|^)(min|bundle|concat|spec|tests?)\\.[a-zA-Z]+")
|
|
or
|
|
exists(TopLevel tl | tl.getFile() = this |
|
|
tl.isExterns()
|
|
or
|
|
tl instanceof FrameworkLibraryInstance
|
|
)
|
|
}
|
|
}
|
|
|
|
/** An call site that is relevant for analysis quality. */
|
|
class RelevantInvoke extends InvokeNode {
|
|
RelevantInvoke() { not getFile() instanceof IgnoredFile }
|
|
}
|
|
|
|
/** An call site that is relevant for analysis quality. */
|
|
class RelevantFunction extends Function {
|
|
RelevantFunction() {
|
|
not getFile() instanceof IgnoredFile and
|
|
hasBody() // ignore abstract or ambient functions
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a data flow node that can be resolved to a function, usually a callback.
|
|
*
|
|
* These are not part of the static call graph, but the data flow analysis can
|
|
* track them, so we consider them resolved.
|
|
*/
|
|
SourceNode resolvableCallback() {
|
|
result instanceof FunctionNode
|
|
or
|
|
exists(Node arg |
|
|
FlowSteps::argumentPassing(_, arg, _, result) and
|
|
resolvableCallback().flowsTo(arg)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets a data flow node that can be resolved to an invocation of a callback.
|
|
*
|
|
* These are not part of the static call graph, but the data flow analysis can
|
|
* track them, so we consider them resolved.
|
|
*/
|
|
SourceNode nodeLeadingToInvocation() {
|
|
exists(result.getAnInvocation())
|
|
or
|
|
exists(Node arg |
|
|
FlowSteps::argumentPassing(_, arg, _, nodeLeadingToInvocation()) and
|
|
result.flowsTo(arg)
|
|
)
|
|
or
|
|
exists(PartialInvokeNode invoke, Node arg |
|
|
invoke.isPartialArgument(arg, _, _) and
|
|
result.flowsTo(arg)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if there is a call edge `invoke -> f` between a relevant invocation
|
|
* and a relevant function.
|
|
*/
|
|
predicate relevantCall(RelevantInvoke invoke, RelevantFunction f) {
|
|
FlowSteps::calls(invoke, f)
|
|
}
|
|
|
|
/**
|
|
* A call site that can be resolved to a function in the same project.
|
|
*/
|
|
class ResolvableCall extends RelevantInvoke {
|
|
ResolvableCall() {
|
|
relevantCall(this, _)
|
|
or
|
|
this = resolvableCallback().getAnInvocation()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call site that could not be resolved.
|
|
*/
|
|
class UnresolvableCall extends RelevantInvoke {
|
|
UnresolvableCall() {
|
|
not this instanceof ResolvableCall
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function with at least one call site.
|
|
*/
|
|
class FunctionWithCallers extends RelevantFunction {
|
|
FunctionWithCallers() {
|
|
FlowSteps::calls(_, this)
|
|
or
|
|
this = nodeLeadingToInvocation().getAstNode()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function without any call sites.
|
|
*/
|
|
class FunctionWithoutCallers extends RelevantFunction {
|
|
FunctionWithoutCallers() { not this instanceof FunctionWithCallers }
|
|
}
|