Skip to content

Commit 25dbb07

Browse files
committed
wip
1 parent 7b832c3 commit 25dbb07

2 files changed

Lines changed: 81 additions & 40 deletions

File tree

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

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ predicate scopeDefinesParameterVariable(
114114
)
115115
}
116116

117-
pragma[nomagic]
117+
bindingset[i]
118+
pragma[inline_late]
118119
private string variableNameInScope(Ruby::AstNode i, Scope::Range scope) {
119120
scope = scopeOf(i) and
120121
(
@@ -182,8 +183,12 @@ private module Input implements LocalNameBindingInputSig<Location> {
182183
|
183184
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
184185
) and
185-
not scopeDefinesParameterVariable(scope, name, _, _) and
186-
not inherits(scope, name, _)
186+
not scopeDefinesParameterVariable(scope, name, _, _)
187+
}
188+
189+
predicate declInScopeIsUncertain(AstNode definingNode, string name, AstNode scope) {
190+
declInScope(definingNode, name, scope) and
191+
explicitAssignmentNode(definingNode, _)
187192
}
188193

189194
predicate implicitDeclInScope(string name, AstNode scope) {
@@ -219,10 +224,20 @@ private module Input implements LocalNameBindingInputSig<Location> {
219224
n instanceof Ruby::Self and
220225
name = "self"
221226
}
227+
228+
bindingset[access, definingNode]
229+
predicate isValidAccess(AstNode access, AstNode definingNode) {
230+
not access.getLocation().strictlyBefore(definingNode.getLocation())
231+
}
222232
}
223233

224234
private import LocalNameBinding<Location, Input>
225235

236+
pragma[nomagic]
237+
predicate access(Ruby::AstNode access, VariableReal variable) {
238+
exists(Local l | variable = TLocalVariableReal(l) | access = l.getAnAccess())
239+
}
240+
226241
cached
227242
private module Cached {
228243
cached
@@ -398,14 +413,6 @@ private module Cached {
398413
i = any(Ruby::ExpressionReferencePattern x).getValue()
399414
}
400415

401-
cached
402-
predicate access(Ruby::AstNode access, VariableReal variable) {
403-
exists(Local l | variable = TLocalVariableReal(l) |
404-
access = l.getAnAccess() and
405-
not access.getLocation().strictlyBefore(l.getDefiningNode().getLocation())
406-
)
407-
}
408-
409416
private class Access extends Ruby::Token {
410417
Access() {
411418
access(this, _) or
@@ -456,29 +463,6 @@ private module Cached {
456463

457464
import Cached
458465

459-
/** Holds if this scope inherits `name` from an outer scope `outer`. */
460-
private predicate inherits(Scope::Range scope, string name, Scope::Range outer) {
461-
(
462-
scope instanceof Ruby::Block or
463-
scope instanceof Ruby::DoBlock or
464-
scope instanceof Ruby::Lambda
465-
) and
466-
not scopeDefinesParameterVariable(scope, name, _, _) and
467-
(
468-
outer = scope.getOuterScope() and
469-
(
470-
scopeDefinesParameterVariable(outer, name, _, _)
471-
or
472-
exists(Ruby::AstNode i |
473-
scopeAssigns(outer, name, i) and
474-
i.getLocation().strictlyBefore(scope.getLocation())
475-
)
476-
)
477-
or
478-
inherits(scope.getOuterScope(), name, outer)
479-
)
480-
}
481-
482466
abstract class VariableImpl extends TVariable {
483467
abstract string getNameImpl();
484468

shared/namebinding/codeql/namebinding/LocalNameBinding.qll

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ 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+
* This predicate must be a subset of `declInScope`.
106+
*/
107+
default predicate declInScopeIsUncertain(AstNode definingNode, string name, AstNode scope) {
108+
none()
109+
}
110+
98111
/**
99112
* Holds if a local declaration named `name` is implicitly in scope in the given `scope`.
100113
*/
@@ -123,6 +136,12 @@ signature module LocalNameBindingInputSig<LocationSig Location> {
123136
* full control of scope resolution for for specific types of references.
124137
*/
125138
default predicate lookupStartsAt(AstNode n, AstNode scope) { none() }
139+
140+
/**
141+
* Holds if `access` is valid access of the local declared at `definingNode`.
142+
*/
143+
bindingset[access, definingNode]
144+
default predicate isValidAccess(AstNode access, AstNode definingNode) { any() }
126145
}
127146

128147
/**
@@ -299,6 +318,12 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
299318
not lookupStartsAt(n, _) and
300319
lookup = getEnclosingScope(n)
301320
)
321+
or
322+
exists(Scope scope |
323+
declInScopeIsUncertain(n, name, scope) and
324+
not isTopScope(scope) and
325+
lookup = getEnclosingScope(scope)
326+
)
302327
}
303328

304329
pragma[nomagic]
@@ -308,7 +333,11 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
308333
or
309334
exists(Scope mid |
310335
lookupInScope(name, lookup, mid) and
311-
not declInScope(_, name, mid) and
336+
not exists(AstNode definingNode |
337+
declInScope(definingNode, name, mid) and
338+
// allow to step through uncertain declarations
339+
not declInScopeIsUncertain(definingNode, name, mid)
340+
) and
312341
not implicitDeclInScope(name, mid) and
313342
not isTopScope(mid) and
314343
scope = getEnclosingScope(mid)
@@ -352,7 +381,15 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
352381
private string name;
353382
private AstNode scope;
354383

355-
ExplicitLocal() { this = TExplicitLocal(definingNode, name, scope) }
384+
cached
385+
ExplicitLocal() {
386+
CachedStage::ref() and
387+
this = TExplicitLocal(definingNode, name, scope) and
388+
// skip uncertain declarations that are in fact accesses
389+
not exists(AstNode other |
390+
accessStep(definingNode, TExplicitLocal(other, _, _)) and other != definingNode
391+
)
392+
}
356393

357394
override AstNode getDefiningNode() { result = definingNode }
358395

@@ -380,27 +417,47 @@ module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location>
380417
}
381418

382419
pragma[nomagic]
383-
private predicate resolveInScope(string name, Scope lookup, Local l) {
420+
private predicate resolveInScope(string name, Scope lookup, TLocal l) {
384421
exists(Scope scope | lookupInScope(name, lookup, scope) |
385422
l = TExplicitLocal(_, name, scope) or
386423
l = TImplicitLocal(name, scope)
387424
)
388425
}
389426

390-
cached
391-
private predicate access(AstNode access, Local l) {
427+
pragma[nomagic]
428+
private predicate accessStep(AstNode access, TLocal l) {
392429
CachedStage::ref() and
393430
exists(Scope lookup, string name |
394431
accessCandInLookupScope(access, name, lookup) and
395432
resolveInScope(name, lookup, l)
433+
|
434+
l instanceof TImplicitLocal
435+
or
436+
l = TExplicitLocal(any(AstNode definingNode | isValidAccess(access, definingNode)), _, _)
437+
)
438+
}
439+
440+
pragma[nomagic]
441+
private predicate accessSteps(AstNode access, Local l) {
442+
accessStep(access, l)
443+
or
444+
exists(AstNode mid, string name, AstNode scope |
445+
accessSteps(mid, l) and
446+
accessStep(access, TExplicitLocal(mid, name, scope)) and
447+
declInScopeIsUncertain(mid, name, scope)
396448
)
397449
}
398450

399451
/** A local access. */
400452
final class LocalAccess extends AstNodeFinal {
401453
private Local l;
402454

403-
LocalAccess() { access(this, l) }
455+
cached
456+
LocalAccess() {
457+
CachedStage::ref() and
458+
accessSteps(this, l) and
459+
accessCand(this, _)
460+
}
404461

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

0 commit comments

Comments
 (0)