Skip to content

Commit 2770b4f

Browse files
authored
Merge pull request #104 from github/aibaars/variables
Simple implementation of class and instance variables
2 parents f8790c8 + c33c3a1 commit 2770b4f

File tree

7 files changed

+318
-8
lines changed

7 files changed

+318
-8
lines changed

ql/src/codeql_ruby/ast/Variable.qll

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class VariableScope extends TScope {
2828
result = this.getAVariable() and
2929
result.getName() = name
3030
}
31+
32+
/** Gets the scope in which this scope is nested, if any. */
33+
VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) }
3134
}
3235

3336
/** A variable declared in a scope. */
@@ -85,6 +88,23 @@ class GlobalVariable extends Variable, TGlobalVariable {
8588
final override GlobalVariableAccess getAnAccess() { result.getVariable() = this }
8689
}
8790

91+
/** An instance variable. */
92+
class InstanceVariable extends Variable, TInstanceVariable {
93+
override InstanceVariable::Range range;
94+
95+
/** Holds is this variable is a class instance variable. */
96+
final predicate isClassInstanceVariable() { range.isClassInstanceVariable() }
97+
98+
final override InstanceVariableAccess getAnAccess() { result.getVariable() = this }
99+
}
100+
101+
/** A class variable. */
102+
class ClassVariable extends Variable, TClassVariable {
103+
override ClassVariable::Range range;
104+
105+
final override ClassVariableAccess getAnAccess() { result.getVariable() = this }
106+
}
107+
88108
/** An access to a variable. */
89109
class VariableAccess extends Expr {
90110
override VariableAccess::Range range;
@@ -146,7 +166,6 @@ class VariableReadAccess extends VariableAccess {
146166
class LocalVariableAccess extends VariableAccess, @token_identifier {
147167
final override LocalVariableAccess::Range range;
148168

149-
/** Gets the variable this identifier refers to. */
150169
final override LocalVariable getVariable() { result = range.getVariable() }
151170

152171
final override string getAPrimaryQlClass() {
@@ -177,11 +196,10 @@ class LocalVariableWriteAccess extends LocalVariableAccess, VariableWriteAccess
177196
/** An access to a local variable where the value is read. */
178197
class LocalVariableReadAccess extends LocalVariableAccess, VariableReadAccess { }
179198

180-
/** An access to a local variable. */
199+
/** An access to a global variable. */
181200
class GlobalVariableAccess extends VariableAccess, @token_global_variable {
182201
final override GlobalVariableAccess::Range range;
183202

184-
/** Gets the variable this identifier refers to. */
185203
final override GlobalVariable getVariable() { result = range.getVariable() }
186204

187205
final override string getAPrimaryQlClass() { result = "GlobalVariableAccess" }
@@ -192,3 +210,21 @@ class GlobalVariableWriteAccess extends GlobalVariableAccess, VariableWriteAcces
192210

193211
/** An access to a global variable where the value is read. */
194212
class GlobalVariableReadAccess extends GlobalVariableAccess, VariableReadAccess { }
213+
214+
/** An access to an instance variable. */
215+
class InstanceVariableAccess extends VariableAccess, @token_instance_variable {
216+
final override InstanceVariableAccess::Range range;
217+
218+
final override InstanceVariable getVariable() { result = range.getVariable() }
219+
220+
final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" }
221+
}
222+
223+
/** An access to a class variable. */
224+
class ClassVariableAccess extends VariableAccess, @token_class_variable {
225+
final override ClassVariableAccess::Range range;
226+
227+
final override ClassVariable getVariable() { result = range.getVariable() }
228+
229+
final override string getAPrimaryQlClass() { result = "ClassVariableAccess" }
230+
}

ql/src/codeql_ruby/ast/internal/Variable.qll

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,45 @@ private Generated::AstNode parent(Generated::AstNode n) {
99
not n = any(VariableScope s).getScopeElement()
1010
}
1111

12+
private predicate instanceVariableAccess(
13+
Generated::InstanceVariable var, string name, VariableScope scope, boolean instance
14+
) {
15+
name = var.getValue() and
16+
scope = enclosingModuleOrClass(var) and
17+
if hasEnclosingMethod(var) then instance = true else instance = false
18+
}
19+
20+
private predicate classVariableAccess(Generated::ClassVariable var, string name, VariableScope scope) {
21+
name = var.getValue() and
22+
scope = enclosingModuleOrClass(var)
23+
}
24+
25+
predicate hasEnclosingMethod(Generated::AstNode node) {
26+
exists(Callable method | parentCallableScope*(enclosingScope(node)) = TCallableScope(method) |
27+
method instanceof Method or
28+
method instanceof SingletonMethod
29+
)
30+
}
31+
32+
private TCallableScope parentCallableScope(TCallableScope scope) {
33+
exists(Callable c |
34+
scope = TCallableScope(c) and
35+
not c instanceof Method and
36+
not c instanceof SingletonMethod
37+
|
38+
result = scope.(VariableScope).getOuterScope()
39+
)
40+
}
41+
42+
private VariableScope parentScope(VariableScope scope) {
43+
not scope instanceof ModuleOrClassScope and
44+
result = scope.getOuterScope()
45+
}
46+
47+
private ModuleOrClassScope enclosingModuleOrClass(Generated::AstNode node) {
48+
result = parentScope*(enclosingScope(node))
49+
}
50+
1251
private predicate parameterAssignment(
1352
CallableScope::Range scope, string name, Generated::Identifier i
1453
) {
@@ -20,7 +59,6 @@ private predicate parameterAssignment(
2059
private predicate scopeDefinesParameterVariable(
2160
CallableScope::Range scope, string name, Generated::Identifier i
2261
) {
23-
parameterAssignment(scope, name, i) and
2462
// In case of overlapping parameter names (e.g. `_`), only the first
2563
// parameter will give rise to a variable
2664
i =
@@ -69,9 +107,6 @@ private class CapturingScope extends VariableScope {
69107
)
70108
}
71109

72-
/** Gets the scope in which this scope is nested, if any. */
73-
private VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) }
74-
75110
/** Holds if this scope inherits `name` from an outer scope `outer`. */
76111
predicate inherits(string name, VariableScope outer) {
77112
not scopeDefinesParameterVariable(this, name, _) and
@@ -112,10 +147,25 @@ private module Cached {
112147
cached
113148
newtype TVariable =
114149
TGlobalVariable(string name) { name = any(Generated::GlobalVariable var).getValue() } or
150+
TClassVariable(VariableScope scope, string name, Generated::AstNode decl) {
151+
decl =
152+
min(Generated::ClassVariable other |
153+
classVariableAccess(other, name, scope)
154+
|
155+
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
156+
)
157+
} or
158+
TInstanceVariable(VariableScope scope, string name, boolean instance, Generated::AstNode decl) {
159+
decl =
160+
min(Generated::InstanceVariable other |
161+
instanceVariableAccess(other, name, scope, instance)
162+
|
163+
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
164+
)
165+
} or
115166
TLocalVariable(VariableScope scope, string name, Generated::Identifier i) {
116167
scopeDefinesParameterVariable(scope, name, i)
117168
or
118-
scopeAssigns(scope, name, i) and
119169
i =
120170
min(Generated::Identifier other |
121171
scopeAssigns(scope, name, other)
@@ -307,6 +357,22 @@ private module Cached {
307357
predicate isCapturedAccess(LocalVariableAccess::Range access) {
308358
access.getVariable().getDeclaringScope() != enclosingScope(access)
309359
}
360+
361+
cached
362+
predicate instanceVariableAccess(Generated::InstanceVariable var, InstanceVariable v) {
363+
exists(string name, VariableScope scope, boolean instance |
364+
v = TInstanceVariable(scope, name, instance, _) and
365+
instanceVariableAccess(var, name, scope, instance)
366+
)
367+
}
368+
369+
cached
370+
predicate classVariableAccess(Generated::ClassVariable var, ClassVariable variable) {
371+
exists(VariableScope scope, string name |
372+
variable = TClassVariable(scope, name, _) and
373+
classVariableAccess(var, name, scope)
374+
)
375+
}
310376
}
311377

312378
import Cached
@@ -416,6 +482,43 @@ module GlobalVariable {
416482
}
417483
}
418484

485+
private class ModuleOrClassScope = TClassScope or TModuleScope or TTopLevelScope;
486+
487+
module InstanceVariable {
488+
class Range extends Variable::Range, TInstanceVariable {
489+
private ModuleOrClassScope scope;
490+
private boolean instance;
491+
private string name;
492+
private Generated::AstNode decl;
493+
494+
Range() { this = TInstanceVariable(scope, name, instance, decl) }
495+
496+
final override string getName() { result = name }
497+
498+
final predicate isClassInstanceVariable() { instance = false }
499+
500+
final override Location getLocation() { result = decl.getLocation() }
501+
502+
final override VariableScope getDeclaringScope() { result = scope }
503+
}
504+
}
505+
506+
module ClassVariable {
507+
class Range extends Variable::Range, TClassVariable {
508+
private ModuleOrClassScope scope;
509+
private string name;
510+
private Generated::AstNode decl;
511+
512+
Range() { this = TClassVariable(scope, name, decl) }
513+
514+
final override string getName() { result = name }
515+
516+
final override Location getLocation() { result = decl.getLocation() }
517+
518+
final override VariableScope getDeclaringScope() { result = scope }
519+
}
520+
}
521+
419522
module VariableAccess {
420523
abstract class Range extends Expr::Range {
421524
abstract Variable getVariable();
@@ -451,3 +554,23 @@ module GlobalVariableAccess {
451554
final override GlobalVariable getVariable() { result = variable }
452555
}
453556
}
557+
558+
module InstanceVariableAccess {
559+
class Range extends VariableAccess::Range, @token_instance_variable {
560+
InstanceVariable variable;
561+
562+
Range() { instanceVariableAccess(this, variable) }
563+
564+
final override InstanceVariable getVariable() { result = variable }
565+
}
566+
}
567+
568+
module ClassVariableAccess {
569+
class Range extends VariableAccess::Range, @token_class_variable {
570+
ClassVariable variable;
571+
572+
Range() { classVariableAccess(this, variable) }
573+
574+
final override ClassVariable getVariable() { result = variable }
575+
}
576+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@@x = 42
2+
3+
p @@x
4+
5+
def print
6+
p @@x
7+
end
8+
9+
class X
10+
def b
11+
p @@x
12+
end
13+
def self.s
14+
p @@x
15+
end
16+
end
17+
18+
class Y < BasicObject
19+
@@x = 10
20+
end
21+
22+
module M
23+
@@x = 12
24+
end
25+
26+
module N
27+
include M
28+
p @@x
29+
end
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
@top = 1
2+
3+
def foo
4+
@foo = 10
5+
end
6+
7+
def print_foo
8+
puts @foo
9+
end
10+
11+
puts @top
12+
13+
class X
14+
@x = 10
15+
def m()
16+
@y = 7
17+
end
18+
end
19+
20+
module M
21+
@m = 10
22+
def n()
23+
@n = 7
24+
end
25+
end
26+
27+
puts {
28+
@x = 100
29+
}
30+
31+
def bar
32+
1.times { @x = 200 }
33+
end
34+
35+
class C
36+
@x = 42
37+
def x
38+
def y
39+
@x = 10
40+
end
41+
y
42+
p @x
43+
end
44+
end

ql/test/library-tests/variables/varaccess.expected

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11
variableAccess
2+
| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope |
3+
| class_variables.rb:3:3:3:5 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope |
4+
| class_variables.rb:6:4:6:6 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope |
5+
| class_variables.rb:11:7:11:9 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | class scope |
6+
| class_variables.rb:14:6:14:8 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | class scope |
7+
| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:5 | @@x | class_variables.rb:18:1:20:3 | class scope |
8+
| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:5 | @@x | class_variables.rb:22:1:24:3 | module scope |
9+
| class_variables.rb:28:5:28:7 | @@x | class_variables.rb:28:5:28:7 | @@x | class_variables.rb:26:1:29:3 | module scope |
10+
| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | top-level scope |
11+
| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | top-level scope |
12+
| instance_variables.rb:8:8:8:11 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | top-level scope |
13+
| instance_variables.rb:11:6:11:9 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | top-level scope |
14+
| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:13:1:18:3 | class scope |
15+
| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:13:1:18:3 | class scope |
16+
| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:20:1:25:3 | module scope |
17+
| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:20:1:25:3 | module scope |
18+
| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:1:1:44:4 | top-level scope |
19+
| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:1:1:44:4 | top-level scope |
20+
| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:35:1:44:4 | class scope |
21+
| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | class scope |
22+
| instance_variables.rb:42:6:42:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | class scope |
223
| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | class scope |
324
| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | module scope |
425
| nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | module scope |
@@ -221,6 +242,27 @@ implicitWrite
221242
| ssa.rb:64:8:64:8 | a |
222243
| ssa.rb:66:15:66:15 | a |
223244
readAccess
245+
| class_variables.rb:1:1:1:3 | @@x |
246+
| class_variables.rb:3:3:3:5 | @@x |
247+
| class_variables.rb:6:4:6:6 | @@x |
248+
| class_variables.rb:11:7:11:9 | @@x |
249+
| class_variables.rb:14:6:14:8 | @@x |
250+
| class_variables.rb:19:3:19:5 | @@x |
251+
| class_variables.rb:23:3:23:5 | @@x |
252+
| class_variables.rb:28:5:28:7 | @@x |
253+
| instance_variables.rb:1:1:1:4 | @top |
254+
| instance_variables.rb:4:3:4:6 | @foo |
255+
| instance_variables.rb:8:8:8:11 | @foo |
256+
| instance_variables.rb:11:6:11:9 | @top |
257+
| instance_variables.rb:14:3:14:4 | @x |
258+
| instance_variables.rb:16:5:16:6 | @y |
259+
| instance_variables.rb:21:2:21:3 | @m |
260+
| instance_variables.rb:23:4:23:5 | @n |
261+
| instance_variables.rb:28:3:28:4 | @x |
262+
| instance_variables.rb:32:12:32:13 | @x |
263+
| instance_variables.rb:36:3:36:4 | @x |
264+
| instance_variables.rb:39:6:39:7 | @x |
265+
| instance_variables.rb:42:6:42:7 | @x |
224266
| nested_scopes.rb:14:16:14:16 | a |
225267
| nested_scopes.rb:15:11:15:11 | a |
226268
| nested_scopes.rb:16:13:16:13 | a |

0 commit comments

Comments
 (0)