mirror of
https://github.com/github/codeql.git
synced 2026-04-12 02:24:00 +02:00
94 lines
3.0 KiB
Plaintext
94 lines
3.0 KiB
Plaintext
/**
|
|
* @name Definitions
|
|
* @description Jump to definition helper query.
|
|
* @kind definitions
|
|
* @id rb/jump-to-definition
|
|
*/
|
|
|
|
/*
|
|
* TODO:
|
|
* - should `Foo.new` point to `Foo#initialize`?
|
|
*/
|
|
|
|
import codeql.ruby.AST
|
|
import codeql.ruby.dataflow.SSA
|
|
import codeql.ruby.dataflow.internal.DataFlowDispatch
|
|
|
|
from DefLoc loc, Expr src, Expr target, string kind
|
|
where
|
|
ConstantDefLoc(src, target) = loc and kind = "constant"
|
|
or
|
|
MethodLoc(src, target) = loc and kind = "method"
|
|
or
|
|
LocalVariableLoc(src, target) = loc and kind = "variable"
|
|
or
|
|
InstanceVariableLoc(src, target) = loc and kind = "instance variable"
|
|
or
|
|
ClassVariableLoc(src, target) = loc and kind = "class variable"
|
|
select src, target, kind
|
|
|
|
/**
|
|
* Definition location info for different identifiers.
|
|
* Each branch holds two values that are subclasses of `Expr`.
|
|
* The first is the "source" - some usage of an identifier.
|
|
* The second is the "target" - the definition of that identifier.
|
|
*/
|
|
newtype DefLoc =
|
|
/** A constant, module or class. */
|
|
ConstantDefLoc(ConstantReadAccess read, ConstantWriteAccess write) {
|
|
write = definitionOf(read.getAQualifiedName())
|
|
} or
|
|
/** A method call. */
|
|
MethodLoc(MethodCall call, Method meth) {
|
|
meth = call.getATarget()
|
|
or
|
|
// include implicit `initialize` calls
|
|
meth = getInitializeTarget(call.getAControlFlowNode())
|
|
} or
|
|
/** A local variable. */
|
|
LocalVariableLoc(VariableReadAccess read, VariableWriteAccess write) {
|
|
exists(Ssa::WriteDefinition w |
|
|
write = w.getWriteAccess().getAstNode() and
|
|
read = w.getARead().getExpr() and
|
|
not read.isSynthesized()
|
|
)
|
|
} or
|
|
/** An instance variable */
|
|
InstanceVariableLoc(InstanceVariableReadAccess read, InstanceVariableWriteAccess write) {
|
|
/*
|
|
* We consider instance variables to be "defined" in the initialize method of their enclosing class.
|
|
* If that method doesn't exist, we won't provide any jump-to-def information for the instance variable.
|
|
*/
|
|
|
|
exists(Method m |
|
|
m.getAChild+() = write and
|
|
m.getName() = "initialize" and
|
|
write.getVariable() = read.getVariable()
|
|
)
|
|
} or
|
|
/** A class variable */
|
|
ClassVariableLoc(ClassVariableReadAccess read, ClassVariableWriteAccess write) {
|
|
read.getVariable() = write.getVariable() and
|
|
not exists(MethodBase m | m.getAChild+() = write)
|
|
}
|
|
|
|
/**
|
|
* Gets the constant write that defines the given constant.
|
|
* Modules often don't have a unique definition, as they are opened multiple times in different
|
|
* files. In these cases we arbitrarily pick the definition with the lexicographically least
|
|
* location.
|
|
*/
|
|
pragma[noinline]
|
|
ConstantWriteAccess definitionOf(string fqn) {
|
|
fqn = any(ConstantReadAccess read).getAQualifiedName() and
|
|
result =
|
|
min(ConstantWriteAccess w, Location l |
|
|
w.getAQualifiedName() = fqn and l = w.getLocation()
|
|
|
|
|
w
|
|
order by
|
|
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
|
l.getEndColumn()
|
|
)
|
|
}
|