@@ -95,10 +95,11 @@ predicate scopeDefinesParameterVariable(
9595 // In case of overlapping parameter names (e.g. `_`), only the first
9696 // parameter will give rise to a variable
9797 i =
98- min ( Ruby:: Identifier other |
99- parameterAssignment ( scope , name , other , _)
98+ min ( Ruby:: Identifier other , int startline , int startcolumn |
99+ parameterAssignment ( scope , name , other , _) and
100+ other .getLocation ( ) .hasLocationInfo ( _, startline , startcolumn , _, _)
100101 |
101- other order by other . getLocation ( ) . getStartLine ( ) , other . getLocation ( ) . getStartColumn ( )
102+ other order by startline , startcolumn
102103 ) and
103104 parameterAssignment ( scope , name , _, pos )
104105 or
@@ -177,17 +178,29 @@ private module Input implements LocalNameBindingInputSig<Location> {
177178 predicate declInScope ( AstNode definingNode , string name , AstNode scope ) {
178179 scopeDefinesParameterVariable ( scope , name , definingNode , _)
179180 or
180- declInScopeIsUncertain ( definingNode , name , scope )
181- }
182-
183- predicate declInScopeIsUncertain ( AstNode definingNode , string name , AstNode scope ) {
184181 definingNode =
185- min ( Ruby:: AstNode other |
186- scopeAssigns ( scope , name , other )
182+ min ( Ruby:: AstNode other , int startline , int startcolumn |
183+ scopeAssigns ( scope , name , other ) and
184+ other .getLocation ( ) .hasLocationInfo ( _, startline , startcolumn , _, _)
187185 |
188- other order by other . getLocation ( ) . getStartLine ( ) , other . getLocation ( ) . getStartColumn ( )
186+ other order by startline , startcolumn
189187 ) and
190- not scopeDefinesParameterVariable ( scope , name , _, _)
188+ not scopeDefinesParameterVariable ( scope , name , _, _) and
189+ not exists ( AstNode top , Ruby:: AstNode outer |
190+ /*
191+ * ```rb
192+ * a = 1 # declares `a`
193+ * 1.times do | x | # declares `x`
194+ * a = 2 # does not declare `a`
195+ * end
196+ * ```
197+ */
198+
199+ not Input:: isTopScope ( scope ) and
200+ top = scopeOf ( scope ) and
201+ scopeAssigns ( top , name , outer ) and
202+ outer .getLocation ( ) .strictlyBefore ( definingNode .getLocation ( ) )
203+ )
191204 }
192205
193206 predicate implicitDeclInScope ( string name , AstNode scope ) {
@@ -223,20 +236,10 @@ private module Input implements LocalNameBindingInputSig<Location> {
223236 n instanceof Ruby:: Self and
224237 name = "self"
225238 }
226-
227- bindingset [ access, definingNode]
228- predicate isValidAccess ( AstNode access , AstNode definingNode ) {
229- not access .getLocation ( ) .strictlyBefore ( definingNode .getLocation ( ) )
230- }
231239}
232240
233241private import LocalNameBinding< Location , Input >
234242
235- pragma [ nomagic]
236- predicate access ( Ruby:: AstNode access , VariableReal variable ) {
237- exists ( Local l | variable = TLocalVariableReal ( l ) | access = l .getAnAccess ( ) )
238- }
239-
240243cached
241244private module Cached {
242245 cached
@@ -247,18 +250,20 @@ private module Cached {
247250 } or
248251 TClassVariable ( Scope:: Range scope , string name , Ruby:: AstNode decl ) {
249252 decl =
250- min ( Ruby:: ClassVariable other |
251- classVariableAccess ( other , name , scope )
253+ min ( Ruby:: ClassVariable other , int startline , int startcolumn |
254+ classVariableAccess ( other , name , scope ) and
255+ other .getLocation ( ) .hasLocationInfo ( _, startline , startcolumn , _, _)
252256 |
253- other order by other . getLocation ( ) . getStartLine ( ) , other . getLocation ( ) . getStartColumn ( )
257+ other order by startline , startcolumn
254258 )
255259 } or
256260 TInstanceVariable ( Scope:: Range scope , string name , boolean instance , Ruby:: AstNode decl ) {
257261 decl =
258- min ( Ruby:: InstanceVariable other |
259- instanceVariableAccess ( other , name , scope , instance )
262+ min ( Ruby:: InstanceVariable other , int startline , int startcolumn |
263+ instanceVariableAccess ( other , name , scope , instance ) and
264+ other .getLocation ( ) .hasLocationInfo ( _, startline , startcolumn , _, _)
260265 |
261- other order by other . getLocation ( ) . getStartLine ( ) , other . getLocation ( ) . getStartColumn ( )
266+ other order by startline , startcolumn
262267 )
263268 } or
264269 TLocalVariableReal ( Local l ) or
@@ -412,6 +417,31 @@ private module Cached {
412417 i = any ( Ruby:: ExpressionReferencePattern x ) .getValue ( )
413418 }
414419
420+ cached
421+ predicate access ( Ruby:: AstNode access , VariableReal variable ) {
422+ exists ( Local l |
423+ variable = TLocalVariableReal ( l ) and
424+ access = l .getAnAccess ( )
425+ |
426+ l instanceof ImplicitLocal
427+ or
428+ /*
429+ * In the example below, `a` is declared in the scope of `M`, but only the
430+ * second mention of `a` is an actual access.
431+ *
432+ * ```rb
433+ * module M
434+ * puts a # calls method `a`
435+ * a = 1 # declares `a`
436+ * puts a # accesses variable `a`
437+ * end
438+ * ```
439+ */
440+
441+ not access .getLocation ( ) .strictlyBefore ( l .getDefiningNode ( ) .getLocation ( ) )
442+ )
443+ }
444+
415445 private class Access extends Ruby:: Token {
416446 Access ( ) {
417447 access ( this , _) or
0 commit comments