-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathDefinitions.ql
93 lines (87 loc) · 2.96 KB
/
Definitions.ql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* @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()
)
}