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.
This commit is contained in:
Harry Maclean
2021-10-14 13:51:13 +01:00
parent 647485acde
commit c71f538a5a
6 changed files with 29 additions and 9 deletions

View File

@@ -20,3 +20,5 @@ class Scope extends AstNode, TScopeType {
result.getName() = name
}
}
class SelfScope extends Scope, TSelfScopeType { }

View File

@@ -72,10 +72,7 @@ class ClassVariable extends Variable instanceof ClassVariableImpl {
}
/** A `self` variable. */
class SelfVariable extends LocalVariable instanceof SelfVariableImpl {
/** Gets the method that this `self` variable belongs to. */
MethodBase getMethod() { result = this.getDeclaringScope() }
}
class SelfVariable extends LocalVariable instanceof SelfVariableImpl { }
/** An access to a variable. */
class VariableAccess extends Expr instanceof VariableAccessImpl {

View File

@@ -5,6 +5,12 @@ private import codeql.ruby.ast.internal.Parameter
class TScopeType = TMethodBase or TModuleLike or TBlockLike;
/**
* The scope of a `self` variable.
* This differs from a normal scope because it is not affected by blocks or lambdas.
*/
class TSelfScopeType = TMethodBase or TModuleBase;
private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock;
private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass;
@@ -29,6 +35,12 @@ module Scope {
result = this.getOuterScope().getEnclosingMethod()
}
SelfBase::Range getEnclosingSelfScope() {
this instanceof SelfBase::Range and result = this
or
not this instanceof SelfBase::Range and result = this.getOuterScope().getEnclosingSelfScope()
}
Range getOuterScope() { result = scopeOf(this) }
}
}
@@ -59,6 +71,15 @@ module ModuleBase {
class Range extends Scope::Range, TypeRange { }
}
module SelfBase {
class TypeRange = MethodBase::TypeRange or ModuleBase::TypeRange;
/**
* A `self` variable can appear in a class, module, method or at the top level.
*/
class Range extends Scope::Range, TypeRange { }
}
pragma[noinline]
private predicate rankHeredocBody(File f, Ruby::HeredocBody b, int i) {
b =

View File

@@ -143,7 +143,7 @@ private predicate hasLocation(AstNode n, Location l) {
private module ImplicitSelfSynthesis {
pragma[nomagic]
private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) {
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc))))) and
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
mc = TIdentifierMethodCall(_) and
i = 0
}
@@ -164,7 +164,7 @@ private module ImplicitSelfSynthesis {
not exists(g.(Ruby::Call).getReceiver()) and
not exists(g.(Ruby::Call).getMethod().(Ruby::ScopeResolution).getScope())
) and
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc))))) and
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
i = 0
}

View File

@@ -133,7 +133,7 @@ private module Cached {
not scopeDefinesParameterVariable(scope, name, _) and
not inherits(scope, name, _)
} or
TSelfVariable(MethodBase::Range scope) or
TSelfVariable(SelfBase::Range scope) or
TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) }
// Db types that can be vcalls
@@ -479,7 +479,7 @@ class ClassVariableImpl extends VariableReal, TClassVariable {
}
class SelfVariableImpl extends VariableReal, TSelfVariable {
private MethodBase::Range scope;
private SelfBase::Range scope;
SelfVariableImpl() { this = TSelfVariable(scope) }

View File

@@ -228,7 +228,7 @@ module Ssa {
SelfDefinition() { this.definesAt(v, _, _) }
final override string toString() { result = "self (" + v.getMethod() + ")" }
final override string toString() { result = "self (" + v.getDeclaringScope() + ")" }
final override Location getLocation() { result = this.getControlFlowNode().getLocation() }
}