-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathAST.qll
More file actions
189 lines (164 loc) · 5.07 KB
/
AST.qll
File metadata and controls
189 lines (164 loc) · 5.07 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
overlay[local]
module;
import codeql.Locations
import ast.Call
import ast.Control
import ast.Constant
import ast.Erb
import ast.Expr
import ast.Literal
import ast.Method
import ast.Module
import ast.Parameter
import ast.Operation
import ast.Pattern
import ast.Scope
import ast.Statement
import ast.Variable
private import ast.internal.AST
private import ast.internal.Scope
private import ast.internal.Synthesis
private import ast.internal.TreeSitter
private import Customizations
private import Diagnostics
cached
private module Cached {
cached
ModuleBase getEnclosingModule(Scope s) {
result = s
or
not s instanceof ModuleBase and result = getEnclosingModule(s.getOuterScope())
}
cached
MethodBase getEnclosingMethod(Scope s) {
result = s
or
not s instanceof MethodBase and
not s instanceof ModuleBase and
result = getEnclosingMethod(s.getOuterScope())
}
cached
Toplevel getEnclosingToplevel(Scope s) {
result = s
or
result = getEnclosingToplevel(s.getOuterScope())
}
}
private import Cached
/**
* A node in the abstract syntax tree. This class is the base class for all Ruby
* program elements.
*/
class AstNode extends TAstNode {
/**
* Gets the name of a primary CodeQL class to which this node belongs.
*
* This predicate always has a result. If no primary class can be
* determined, the result is `"???"`. If multiple primary classes match,
* this predicate can have multiple results.
*/
string getAPrimaryQlClass() { result = "???" }
/**
* Gets a comma-separated list of the names of the primary CodeQL classes to
* which this element belongs.
*/
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
/** Gets the enclosing module, if any. */
final ModuleBase getEnclosingModule() { result = getEnclosingModule(scopeOfInclSynth(this)) }
/** Gets the enclosing method, if any. */
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
/** Gets the enclosing top-level. */
final Toplevel getEnclosingToplevel() { result = getEnclosingToplevel(scopeOfInclSynth(this)) }
/** Gets a textual representation of this node. */
cached
string toString() { none() }
/** Gets the location of this node. */
Location getLocation() { result = getLocation(this) }
/** Gets the file of this node. */
final File getFile() { result = this.getLocation().getFile() }
/** Gets a child node of this `AstNode`. */
final AstNode getAChild() { result = this.getAChild(_) }
/** Gets the parent of this `AstNode`, if this node is not a root node. */
final AstNode getParent() { result.getAChild() = this }
/**
* Gets a child of this node, which can also be retrieved using a predicate
* named `pred`.
*/
cached
AstNode getAChild(string pred) {
pred = "getDesugared" and
result = this.getDesugared()
}
/**
* Holds if this node was synthesized to represent an implicit AST node not
* present in the source code. In the following example method call, the
* receiver is an implicit `self` reference, for which there is a synthesized
* `Self` node.
*
* ```rb
* foo(123)
* ```
*/
final predicate isSynthesized() { this = getSynthChild(_, _) }
/**
* Gets the desugared version of this AST node, if any.
*
* For example, the desugared version of
*
* ```rb
* x += y
* ```
*
* is
*
* ```rb
* x = x + y
* ```
*
* when `x` is a variable. Whenever an AST node can be desugared,
* then the desugared version is used in the control-flow graph.
*/
final AstNode getDesugared() { result = getSynthChild(this, -1) }
}
/** A Ruby source file */
class RubyFile extends File {
RubyFile() {
exists(Location loc |
ruby_ast_node_location(_, loc) and
this = loc.getFile()
)
}
/** Gets a token in this file. */
private Ruby::Token getAToken() { result.getLocation().getFile() = this }
/** Holds if `line` contains a token. */
private predicate line(int line, boolean comment) {
exists(Ruby::Token token, Location l |
token = this.getAToken() and
l = token.getLocation() and
line in [l.getStartLine() .. l.getEndLine()] and
if token instanceof @ruby_token_comment then comment = true else comment = false
)
}
/** Gets the number of lines in this file. */
int getNumberOfLines() { result = max([0, this.getAToken().getLocation().getEndLine()]) }
/** Gets the number of lines of code in this file. */
int getNumberOfLinesOfCode() { result = count(int line | this.line(line, false)) }
/** Gets the number of lines of comments in this file. */
int getNumberOfLinesOfComments() { result = count(int line | this.line(line, true)) }
}
/**
* A successfully extracted file, that is, a file that was extracted and
* contains no extraction errors or warnings.
*/
class SuccessfullyExtractedFile extends File {
SuccessfullyExtractedFile() {
not exists(Diagnostic d |
d.getLocation().getFile() = this and
(
d instanceof ExtractionError
or
d instanceof ExtractionWarning
)
)
}
}