mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
First draft of a jump-to-definition query
TODO: flesh out this message
This commit is contained in:
100
ql/src/queries/analysis/Definitions.ql
Normal file
100
ql/src/queries/analysis/Definitions.ql
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @name Definitions
|
||||||
|
* @description Jump to definition helper query.
|
||||||
|
* @kind definitions
|
||||||
|
* @id rb/jump-to-definition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* - instance and class variables
|
||||||
|
* - should `Foo.new` point to `Foo#initialize`?
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ruby
|
||||||
|
import codeql_ruby.ast.internal.Module
|
||||||
|
import codeql_ruby.controlflow.ControlFlowGraph
|
||||||
|
import codeql_ruby.controlflow.CfgNodes
|
||||||
|
import codeql_ruby.dataflow.SSA
|
||||||
|
|
||||||
|
from DefLoc loc, Location src, Location target, string kind
|
||||||
|
where
|
||||||
|
(
|
||||||
|
exists(ConstantReadAccess read, ConstantWriteAccess write | ConstantDefLoc(read, write) = loc |
|
||||||
|
src = read.getLocation() and
|
||||||
|
target = write.getLocation() and
|
||||||
|
kind = "constant"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(MethodCall call, Method meth | LocalMethodLoc(call, meth) = loc |
|
||||||
|
src = call.getLocation() and
|
||||||
|
target = meth.getLocation() and
|
||||||
|
kind = "method"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableReadAccess read, Ssa::WriteDefinition write |
|
||||||
|
LocalVariableLoc(read, write) = loc
|
||||||
|
|
|
||||||
|
src = read.getLocation() and
|
||||||
|
target = write.getLocation() and
|
||||||
|
kind = "variable"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
select src, target, kind
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition location info for different identifiers.
|
||||||
|
* Each branch holds two values that have a `getLocation()` predicate.
|
||||||
|
* 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) } or
|
||||||
|
/** A call to a method that is defined in the same class as the call. */
|
||||||
|
LocalMethodLoc(MethodCall call, Method meth) {
|
||||||
|
meth = lookupMethod(call.getEnclosingModule().getModule(), call.getMethodName()) and
|
||||||
|
call.getReceiver() instanceof Self
|
||||||
|
} or
|
||||||
|
/** A local variable. */
|
||||||
|
LocalVariableLoc(VariableReadAccess read, Ssa::WriteDefinition write) {
|
||||||
|
read = write.getARead().getExpr() and not read.getLocation() = write.getLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fully qualified name for a constant, based on the context in which it is defined.
|
||||||
|
*
|
||||||
|
* For example, given
|
||||||
|
* ```ruby
|
||||||
|
* module Foo
|
||||||
|
* module Bar
|
||||||
|
* class Baz
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* the constant `Bar` has the fully qualified name `Foo::Bar::Baz`.
|
||||||
|
*/
|
||||||
|
string constantQualifiedName(ConstantWriteAccess w) {
|
||||||
|
not exists(ConstantWriteAccess w2 | w2.getAChild() = w) and result = w.getName()
|
||||||
|
or
|
||||||
|
exists(ConstantWriteAccess w2 |
|
||||||
|
w2.getAChild() = w and result = constantQualifiedName(w2) + "::" + w.getName()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
ConstantWriteAccess definitionOf(ConstantReadAccess r) {
|
||||||
|
result =
|
||||||
|
max(ConstantWriteAccess w |
|
||||||
|
TResolved(constantQualifiedName(w)) = resolveScopeExpr(r)
|
||||||
|
|
|
||||||
|
w order by w.getLocation().toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
8
ql/test/query-tests/analysis/Definitions.expected
Normal file
8
ql/test/query-tests/analysis/Definitions.expected
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
| Definitions.rb:4:7:4:9 | Definitions.rb@4:7:4:9 | Definitions.rb:7:5:9:7 | Definitions.rb@7:5:9:7 | method |
|
||||||
|
| Definitions.rb:8:7:8:7 | Definitions.rb@8:7:8:7 | Definitions.rb:7:11:7:11 | Definitions.rb@7:11:7:11 | variable |
|
||||||
|
| Definitions.rb:12:7:12:7 | Definitions.rb@12:7:12:7 | Definitions.rb:3:5:5:7 | Definitions.rb@3:5:5:7 | method |
|
||||||
|
| Definitions.rb:20:7:20:7 | Definitions.rb@20:7:20:7 | Definitions.rb:1:1:15:3 | Definitions.rb@1:1:15:3 | constant |
|
||||||
|
| Definitions.rb:20:7:20:10 | Definitions.rb@20:7:20:10 | Definitions.rb:2:3:14:5 | Definitions.rb@2:3:14:5 | constant |
|
||||||
|
| Definitions.rb:20:18:20:18 | Definitions.rb@20:18:20:18 | Definitions.rb:19:11:19:11 | Definitions.rb@19:11:19:11 | variable |
|
||||||
|
| Definitions.rb:26:1:26:1 | Definitions.rb@26:1:26:1 | Definitions.rb:17:1:24:3 | Definitions.rb@17:1:24:3 | constant |
|
||||||
|
| Definitions.rb:26:1:26:4 | Definitions.rb@26:1:26:4 | Definitions.rb:18:3:23:5 | Definitions.rb@18:3:23:5 | constant |
|
||||||
1
ql/test/query-tests/analysis/Definitions.qlref
Normal file
1
ql/test/query-tests/analysis/Definitions.qlref
Normal file
@@ -0,0 +1 @@
|
|||||||
|
queries/analysis/Definitions.ql
|
||||||
26
ql/test/query-tests/analysis/Definitions.rb
Normal file
26
ql/test/query-tests/analysis/Definitions.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
module A
|
||||||
|
class B
|
||||||
|
def f
|
||||||
|
g 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def g x
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
def h
|
||||||
|
f
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module C
|
||||||
|
class D
|
||||||
|
def h y
|
||||||
|
A::B.new.g y
|
||||||
|
UnknownClass.some_method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
C::D.new
|
||||||
Reference in New Issue
Block a user