11private import TreeSitter
2+ private import codeql.ruby.AST
23private import codeql.ruby.ast.Scope
34private import codeql.ruby.ast.internal.AST
45private import codeql.ruby.ast.internal.Parameter
6+ private import codeql.ruby.ast.internal.Variable
57
68class TScopeType = TMethodBase or TModuleLike or TBlockLike ;
79
@@ -15,6 +17,10 @@ private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock;
1517
1618private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass ;
1719
20+ private class TScopeReal = TMethodBase or TModuleLike or TDoBlock or TLambda or TBraceBlock ;
21+
22+ private class TScopeSynth = TBlockSynth ;
23+
1824module Scope {
1925 class TypeRange = Callable:: TypeRange or ModuleBase:: TypeRange or @ruby_end_block;
2026
@@ -128,3 +134,44 @@ Scope::Range scopeOf(Ruby::AstNode n) {
128134 not p instanceof Scope:: Range and result = scopeOf ( p )
129135 )
130136}
137+
138+ abstract class ScopeImpl extends AstNode , TScopeType {
139+ abstract Scope getOuterScopeImpl ( ) ;
140+
141+ abstract Variable getAVariableImpl ( ) ;
142+
143+ final Variable getVariableImpl ( string name ) {
144+ result = this .getAVariableImpl ( ) and
145+ result .getName ( ) = name
146+ }
147+ }
148+
149+ private class ScopeRealImpl extends ScopeImpl , TScopeReal {
150+ private Scope:: Range range ;
151+
152+ ScopeRealImpl ( ) { range = toGenerated ( this ) }
153+
154+ override Scope getOuterScopeImpl ( ) { toGenerated ( result ) = range .getOuterScope ( ) }
155+
156+ override Variable getAVariableImpl ( ) { result .getDeclaringScope ( ) = this }
157+ }
158+
159+ // We desugar for loops by implementing them as calls to `each` with a block
160+ // argument. Though this is how the desugaring is described in the MRI parser,
161+ // in practice there is not a real nested scope created, so variables that
162+ // may appear to be local to the loop body (e.g. the iteration variable) are
163+ // scoped to the outer scope rather than the loop body.
164+ private class ScopeSynthImpl extends ScopeImpl , TScopeSynth {
165+ ScopeSynthImpl ( ) { this = TBlockSynth ( _, _) }
166+
167+ override Scope getOuterScopeImpl ( ) { scopeOf ( toGeneratedInclSynth ( this ) ) = toGenerated ( result ) }
168+
169+ override Variable getAVariableImpl ( ) {
170+ // Synthesized variables introduced as parameters to this scope
171+ // As this variable is also synthetic, it is genuinely local to this scope.
172+ exists ( SimpleParameter p | p = TSimpleParameterSynth ( this , _) |
173+ p .getVariable ( ) = result and
174+ exists ( TLocalVariableAccessSynth ( p , _, result ) )
175+ )
176+ }
177+ }
0 commit comments