Skip to content

Commit c71f538

Browse files
committed
Extend the scope of self variables
`self` variables are scoped to methods, modules, classes and the top-level of the program. Prior to this change, they were treated as being scoped just to methods. This change means we (once again) correctly synthesise `self` receivers for method calls in class bodies, module bodies and at the top-level.
1 parent 647485a commit c71f538

File tree

6 files changed

+29
-9
lines changed

6 files changed

+29
-9
lines changed

ruby/ql/lib/codeql/ruby/ast/Scope.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ class Scope extends AstNode, TScopeType {
2020
result.getName() = name
2121
}
2222
}
23+
24+
class SelfScope extends Scope, TSelfScopeType { }

ruby/ql/lib/codeql/ruby/ast/Variable.qll

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,7 @@ class ClassVariable extends Variable instanceof ClassVariableImpl {
7272
}
7373

7474
/** A `self` variable. */
75-
class SelfVariable extends LocalVariable instanceof SelfVariableImpl {
76-
/** Gets the method that this `self` variable belongs to. */
77-
MethodBase getMethod() { result = this.getDeclaringScope() }
78-
}
75+
class SelfVariable extends LocalVariable instanceof SelfVariableImpl { }
7976

8077
/** An access to a variable. */
8178
class VariableAccess extends Expr instanceof VariableAccessImpl {

ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ private import codeql.ruby.ast.internal.Parameter
55

66
class TScopeType = TMethodBase or TModuleLike or TBlockLike;
77

8+
/**
9+
* The scope of a `self` variable.
10+
* This differs from a normal scope because it is not affected by blocks or lambdas.
11+
*/
12+
class TSelfScopeType = TMethodBase or TModuleBase;
13+
814
private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock;
915

1016
private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass;
@@ -29,6 +35,12 @@ module Scope {
2935
result = this.getOuterScope().getEnclosingMethod()
3036
}
3137

38+
SelfBase::Range getEnclosingSelfScope() {
39+
this instanceof SelfBase::Range and result = this
40+
or
41+
not this instanceof SelfBase::Range and result = this.getOuterScope().getEnclosingSelfScope()
42+
}
43+
3244
Range getOuterScope() { result = scopeOf(this) }
3345
}
3446
}
@@ -59,6 +71,15 @@ module ModuleBase {
5971
class Range extends Scope::Range, TypeRange { }
6072
}
6173

74+
module SelfBase {
75+
class TypeRange = MethodBase::TypeRange or ModuleBase::TypeRange;
76+
77+
/**
78+
* A `self` variable can appear in a class, module, method or at the top level.
79+
*/
80+
class Range extends Scope::Range, TypeRange { }
81+
}
82+
6283
pragma[noinline]
6384
private predicate rankHeredocBody(File f, Ruby::HeredocBody b, int i) {
6485
b =

ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private predicate hasLocation(AstNode n, Location l) {
143143
private module ImplicitSelfSynthesis {
144144
pragma[nomagic]
145145
private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) {
146-
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc))))) and
146+
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
147147
mc = TIdentifierMethodCall(_) and
148148
i = 0
149149
}
@@ -164,7 +164,7 @@ private module ImplicitSelfSynthesis {
164164
not exists(g.(Ruby::Call).getReceiver()) and
165165
not exists(g.(Ruby::Call).getMethod().(Ruby::ScopeResolution).getScope())
166166
) and
167-
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc))))) and
167+
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
168168
i = 0
169169
}
170170

ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ private module Cached {
133133
not scopeDefinesParameterVariable(scope, name, _) and
134134
not inherits(scope, name, _)
135135
} or
136-
TSelfVariable(MethodBase::Range scope) or
136+
TSelfVariable(SelfBase::Range scope) or
137137
TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) }
138138

139139
// Db types that can be vcalls
@@ -479,7 +479,7 @@ class ClassVariableImpl extends VariableReal, TClassVariable {
479479
}
480480

481481
class SelfVariableImpl extends VariableReal, TSelfVariable {
482-
private MethodBase::Range scope;
482+
private SelfBase::Range scope;
483483

484484
SelfVariableImpl() { this = TSelfVariable(scope) }
485485

ruby/ql/lib/codeql/ruby/dataflow/SSA.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ module Ssa {
228228

229229
SelfDefinition() { this.definesAt(v, _, _) }
230230

231-
final override string toString() { result = "self (" + v.getMethod() + ")" }
231+
final override string toString() { result = "self (" + v.getDeclaringScope() + ")" }
232232

233233
final override Location getLocation() { result = this.getControlFlowNode().getLocation() }
234234
}

0 commit comments

Comments
 (0)