Skip to content

Commit af5c3c1

Browse files
committed
wip
1 parent e625db2 commit af5c3c1

2 files changed

Lines changed: 63 additions & 115 deletions

File tree

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

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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

233241
private 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-
240243
cached
241244
private 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

shared/namebinding/codeql/namebinding/LocalNameBinding.qll

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -95,28 +95,6 @@ signature module LocalNameBindingInputSig<LocationSig Location> {
9595
*/
9696
predicate declInScope(AstNode definingNode, string name, AstNode scope);
9797

98-
/**
99-
* Holds if it is uncertain whether the reference to local declaration named
100-
* `name` at `definingNode` in scope `scope` is a declaration or an access.
101-
*
102-
* For example, in Ruby variables are not explicitly declared, so we consider
103-
* the first mention of a variable to be its declaration:
104-
*
105-
* ```rb
106-
* a = 1 # declares `a`
107-
* 1.times do | x | # declares `x`
108-
* # marking this as an uncertain declaration means it is correctly
109-
* # identified as a reassignment instead of a new declaration
110-
* a = 2
111-
* end
112-
* ```
113-
*
114-
* This predicate must be a subset of `declInScope`.
115-
*/
116-
default predicate declInScopeIsUncertain(AstNode definingNode, string name, AstNode scope) {
117-
none()
118-
}
119-
12098
/**
12199
* Holds if a local declaration named `name` is implicitly in scope in the given `scope`.
122100
*/
@@ -145,26 +123,6 @@ signature module LocalNameBindingInputSig<LocationSig Location> {
145123
* full control of scope resolution for for specific types of references.
146124
*/
147125
default predicate lookupStartsAt(AstNode n, AstNode scope) { none() }
148-
149-
/**
150-
* Holds if `access` is valid access of the local declared at `definingNode`.
151-
*
152-
* This allows for post-filtering of inferred variable accesses, for example
153-
* in Ruby:
154-
*
155-
* ```rb
156-
* module M
157-
* puts a # calls method `a`
158-
* a = 1 # declares `a`
159-
* puts a # accesses variable `a`
160-
* end
161-
* ```
162-
*
163-
* the scope of `a` is inside `M`, but only the second mention of `a` is an
164-
* actual access.
165-
*/
166-
bindingset[access, definingNode]
167-
default predicate isValidAccess(AstNode access, AstNode definingNode) { any() }
168126
}
169127

170128
/**
@@ -341,12 +299,6 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
341299
not lookupStartsAt(n, _) and
342300
lookup = getEnclosingScope(n)
343301
)
344-
or
345-
exists(Scope scope |
346-
declInScopeIsUncertain(n, name, scope) and
347-
not isTopScope(scope) and
348-
lookup = getEnclosingScope(scope)
349-
)
350302
}
351303

352304
pragma[nomagic]
@@ -356,11 +308,7 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
356308
or
357309
exists(Scope mid |
358310
lookupInScope(name, lookup, mid) and
359-
not exists(AstNode definingNode |
360-
declInScope(definingNode, name, mid) and
361-
// allow to step through uncertain declarations
362-
not declInScopeIsUncertain(definingNode, name, mid)
363-
) and
311+
not declInScope(_, name, mid) and
364312
not implicitDeclInScope(name, mid) and
365313
not isTopScope(mid) and
366314
scope = getEnclosingScope(mid)
@@ -404,16 +352,7 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
404352
private string name;
405353
private AstNode scope;
406354

407-
cached
408-
ExplicitLocal() {
409-
CachedStage::ref() and
410-
this = TExplicitLocal(definingNode, name, scope) and
411-
// skip uncertain declarations that are in fact accesses
412-
not exists(AstNode other |
413-
accessStep(definingNode, TExplicitLocal(other, _, _)) and
414-
other != definingNode
415-
)
416-
}
355+
ExplicitLocal() { this = TExplicitLocal(definingNode, name, scope) }
417356

418357
override AstNode getDefiningNode() { result = definingNode }
419358

@@ -441,48 +380,27 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
441380
}
442381

443382
pragma[nomagic]
444-
private predicate resolveInScope(string name, Scope lookup, TLocal l) {
383+
private predicate resolveInScope(string name, Scope lookup, Local l) {
445384
exists(Scope scope | lookupInScope(name, lookup, scope) |
446385
l = TExplicitLocal(_, name, scope) or
447386
l = TImplicitLocal(name, scope)
448387
)
449388
}
450389

451-
pragma[nomagic]
452-
private predicate accessStep(AstNode access, TLocal l) {
390+
cached
391+
private predicate access(AstNode access, Local l) {
453392
CachedStage::ref() and
454393
exists(Scope lookup, string name |
455394
accessCandInLookupScope(access, name, lookup) and
456395
resolveInScope(name, lookup, l)
457-
|
458-
l instanceof TImplicitLocal
459-
or
460-
l = TExplicitLocal(any(AstNode definingNode | isValidAccess(access, definingNode)), _, _)
461-
)
462-
}
463-
464-
pragma[nomagic]
465-
private predicate accessSteps(AstNode access, Local l) {
466-
accessStep(access, l)
467-
or
468-
exists(AstNode mid, string name, AstNode scope |
469-
accessSteps(mid, l) and
470-
accessStep(access, TExplicitLocal(mid, name, scope)) and
471-
declInScopeIsUncertain(mid, name, scope)
472396
)
473397
}
474398

475399
/** A local access. */
476400
final class LocalAccess extends AstNodeFinal {
477401
private Local l;
478402

479-
cached
480-
LocalAccess() {
481-
CachedStage::ref() and
482-
// `l` having type `Local` and not `TLocal` ensure that it is a proper declaration
483-
accessSteps(this, l) and
484-
accessCand(this, _)
485-
}
403+
LocalAccess() { access(this, l) }
486404

487405
/** Gets the local entity being accessed. */
488406
Local getLocal() { result = l }

0 commit comments

Comments
 (0)