Skip to content

Commit b5586e5

Browse files
committed
Ruby
1 parent fac23d3 commit b5586e5

7 files changed

Lines changed: 205 additions & 103 deletions

File tree

ruby/ql/lib/codeql/ruby/ast/Parameter.qll

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class BlockParameter extends NamedParameter, TBlockParameter {
134134
final override string getName() { result = g.getName().getValue() }
135135

136136
final override LocalVariable getVariable() {
137-
result = TLocalVariableReal(_, _, g.getName()) or
137+
result.(LocalVariableReal).getDefiningNode() = g.getName() or
138138
result = TLocalVariableSynth(this, 0)
139139
}
140140

@@ -164,7 +164,7 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
164164
final override string getAPrimaryQlClass() { result = "HashSplatParameter" }
165165

166166
final override LocalVariable getVariable() {
167-
result = TLocalVariableReal(_, _, g.getName()) or
167+
result.(LocalVariableReal).getDefiningNode() = g.getName() or
168168
result = TLocalVariableSynth(this, 0)
169169
}
170170

@@ -212,7 +212,9 @@ class KeywordParameter extends NamedParameter, TKeywordParameter {
212212

213213
final override string getAPrimaryQlClass() { result = "KeywordParameter" }
214214

215-
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
215+
final override LocalVariable getVariable() {
216+
result.(LocalVariableReal).getDefiningNode() = g.getName()
217+
}
216218

217219
/**
218220
* Gets the default value, i.e. the value assigned to the parameter when one
@@ -262,7 +264,9 @@ class OptionalParameter extends NamedParameter, TOptionalParameter {
262264
*/
263265
final Expr getDefaultValue() { toGenerated(result) = g.getValue() }
264266

265-
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
267+
final override LocalVariable getVariable() {
268+
result.(LocalVariableReal).getDefiningNode() = g.getName()
269+
}
266270

267271
final override string toString() { result = this.getName() }
268272

@@ -293,7 +297,7 @@ class SplatParameter extends NamedParameter, TSplatParameter {
293297
final override string getAPrimaryQlClass() { result = "SplatParameter" }
294298

295299
final override LocalVariable getVariable() {
296-
result = TLocalVariableReal(_, _, g.getName()) or
300+
result.(LocalVariableReal).getDefiningNode() = g.getName() or
297301
result = TLocalVariableSynth(this, 0)
298302
}
299303

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SimpleParameterRealImpl extends SimpleParameterImpl, TSimpleParameterReal
3333

3434
SimpleParameterRealImpl() { this = TSimpleParameterReal(g) }
3535

36-
override LocalVariable getVariableImpl() { result = TLocalVariableReal(_, _, g) }
36+
override LocalVariable getVariableImpl() { result.(LocalVariableReal).getDefiningNode() = g }
3737

3838
override string getNameImpl() { result = g.getValue() }
3939
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private Ruby::AstNode specialParentOf(Ruby::AstNode n) {
118118
]
119119
}
120120

121-
private Ruby::AstNode parentOf(Ruby::AstNode n) {
121+
Ruby::AstNode parentOf(Ruby::AstNode n) {
122122
n = getHereDocBody(result)
123123
or
124124
result = specialParentOf(n).getParent()

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,12 @@ private predicate hasLocation(AstNode n, Location l) {
296296
private module ImplicitSelfSynthesis {
297297
pragma[nomagic]
298298
private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) {
299-
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
300-
mc = TIdentifierMethodCall(_) and
301-
i = 0
299+
exists(SelfVariableImpl self |
300+
self.getDeclaringScopeImpl() = scopeOf(toGenerated(mc)).getEnclosingSelfScope() and
301+
child = SynthChild(SelfKind(self)) and
302+
mc = TIdentifierMethodCall(_) and
303+
i = 0
304+
)
302305
}
303306

304307
private class IdentifierMethodCallSelfSynthesis extends Synthesis {
@@ -309,13 +312,14 @@ private module ImplicitSelfSynthesis {
309312

310313
pragma[nomagic]
311314
private predicate regularMethodCallSelfSynthesis(TRegularMethodCall mc, int i, Child child) {
312-
exists(Ruby::AstNode g |
315+
exists(Ruby::AstNode g, SelfVariableImpl self |
313316
mc = TRegularMethodCall(g) and
314317
// If there's no explicit receiver, then the receiver is implicitly `self`.
315-
not exists(g.(Ruby::Call).getReceiver())
316-
) and
317-
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
318-
i = 0
318+
not exists(g.(Ruby::Call).getReceiver()) and
319+
self.getDeclaringScopeImpl() = scopeOf(toGenerated(mc)).getEnclosingSelfScope() and
320+
child = SynthChild(SelfKind(self)) and
321+
i = 0
322+
)
319323
}
320324

321325
private class RegularMethodCallSelfSynthesis extends Synthesis {
@@ -338,9 +342,10 @@ private module ImplicitSelfSynthesis {
338342
*/
339343
pragma[nomagic]
340344
private SelfKind getSelfKind(InstanceVariableAccess var) {
341-
exists(Ruby::AstNode owner |
345+
exists(Ruby::AstNode owner, SelfVariableImpl self |
346+
self.getDeclaringScopeImpl() = scopeOf(owner).getEnclosingSelfScope() and
342347
owner = toGenerated(instanceVarAccessSynthParentStar(var)) and
343-
result = SelfKind(TSelfVariable(scopeOf(owner).getEnclosingSelfScope()))
348+
result = SelfKind(self)
344349
)
345350
}
346351

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

Lines changed: 110 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ overlay[local]
22
module;
33

44
private import TreeSitter
5+
private import codeql.namebinding.LocalNameBinding
56
private import codeql.ruby.AST
67
private import codeql.ruby.CFG
78
private import codeql.ruby.ast.internal.AST
@@ -137,11 +138,97 @@ private predicate scopeAssigns(Scope::Range scope, string name, Ruby::AstNode i)
137138
name = variableNameInScope(i, scope)
138139
}
139140

141+
private module Input implements LocalNameBindingInputSig<Location> {
142+
predicate cacheRevRef() { exists(TVariable v) implies any() }
143+
144+
class AstNode = Ruby::AstNode;
145+
146+
AstNode getChild(AstNode parent, int index) {
147+
parent = parentOf(result) and
148+
(
149+
index = result.getParentIndex()
150+
or
151+
not exists(result.getParentIndex()) and
152+
index = -1
153+
)
154+
}
155+
156+
class Conditional extends AstNode {
157+
Conditional() { none() }
158+
159+
AstNode getCondition() { none() }
160+
161+
AstNode getThen() { none() }
162+
163+
AstNode getElse() { none() }
164+
}
165+
166+
class ShadowingDecl extends AstNode {
167+
ShadowingDecl() { none() }
168+
169+
AstNode getLhs() { none() }
170+
171+
AstNode getRhs() { none() }
172+
173+
AstNode getElse() { none() }
174+
}
175+
176+
predicate declInScope(AstNode definingNode, string name, AstNode scope) {
177+
scopeDefinesParameterVariable(scope, name, definingNode, _)
178+
or
179+
definingNode =
180+
min(Ruby::AstNode other |
181+
scopeAssigns(scope, name, other)
182+
|
183+
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
184+
) and
185+
not scopeDefinesParameterVariable(scope, name, _, _) and
186+
not inherits(scope, name, _)
187+
}
188+
189+
predicate implicitDeclInScope(string name, AstNode scope) {
190+
name = "self" and
191+
scope instanceof SelfBase::Range
192+
}
193+
194+
predicate isTopScope(AstNode scope) {
195+
scope instanceof Scope::Range and
196+
not (
197+
scope instanceof Ruby::Block or
198+
scope instanceof Ruby::DoBlock or
199+
scope instanceof Ruby::Lambda
200+
)
201+
}
202+
203+
predicate accessCand(AstNode n, string name) {
204+
name = variableNameInScope(n, _) and
205+
(
206+
explicitAssignmentNode(n, _)
207+
or
208+
implicitAssignmentNode(n)
209+
or
210+
scopeDefinesParameterVariable(_, _, n, _)
211+
or
212+
vcall(n)
213+
// or
214+
// n = any(Ruby::VariableReferencePattern vr).getName()
215+
)
216+
or
217+
n instanceof Ruby::Self and
218+
name = "self"
219+
}
220+
}
221+
222+
private import LocalNameBinding<Location, Input>
223+
140224
cached
141225
private module Cached {
142226
cached
143227
newtype TVariable =
144-
TGlobalVariable(string name) { name = any(Ruby::GlobalVariable var).getValue() } or
228+
TGlobalVariable(string name) {
229+
CachedStage::ref() and
230+
name = any(Ruby::GlobalVariable var).getValue()
231+
} or
145232
TClassVariable(Scope::Range scope, string name, Ruby::AstNode decl) {
146233
decl =
147234
min(Ruby::ClassVariable other |
@@ -158,19 +245,7 @@ private module Cached {
158245
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
159246
)
160247
} or
161-
TLocalVariableReal(Scope::Range scope, string name, Ruby::AstNode i) {
162-
scopeDefinesParameterVariable(scope, name, i, _)
163-
or
164-
i =
165-
min(Ruby::AstNode other |
166-
scopeAssigns(scope, name, other)
167-
|
168-
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
169-
) and
170-
not scopeDefinesParameterVariable(scope, name, _, _) and
171-
not inherits(scope, name, _)
172-
} or
173-
TSelfVariable(SelfBase::Range scope) or
248+
TLocalVariableReal(Local l) or
174249
TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) }
175250

176251
// Db types that can be vcalls
@@ -321,39 +396,20 @@ private module Cached {
321396
i = any(Ruby::ExpressionReferencePattern x).getValue()
322397
}
323398

324-
pragma[nomagic]
325-
private predicate hasScopeAndName(VariableReal variable, Scope::Range scope, string name) {
326-
variable.getNameImpl() = name and
327-
scope = variable.getDeclaringScopeImpl()
328-
}
329-
330399
cached
331400
predicate access(Ruby::AstNode access, VariableReal variable) {
332-
exists(string name, Scope::Range scope |
333-
pragma[only_bind_into](name) = variableNameInScope(access, scope)
334-
|
335-
hasScopeAndName(variable, scope, name) and
336-
not access.getLocation().strictlyBefore(variable.getLocationImpl()) and
337-
// In case of overlapping parameter names, later parameters should not
338-
// be considered accesses to the first parameter
339-
if parameterAssignment(_, _, access, _)
340-
then scopeDefinesParameterVariable(_, _, access, _)
341-
else any()
342-
or
343-
exists(Scope::Range declScope |
344-
hasScopeAndName(variable, declScope, pragma[only_bind_into](name)) and
345-
inherits(scope, name, declScope)
346-
)
401+
exists(Local l | variable = TLocalVariableReal(l) |
402+
access = l.getAnAccess() and
403+
not access.getLocation().strictlyBefore(l.getDefiningNode().getLocation())
347404
)
348405
}
349406

350407
private class Access extends Ruby::Token {
351408
Access() {
352-
access(this.(Ruby::Identifier), _) or
409+
access(this, _) or
353410
this instanceof Ruby::GlobalVariable or
354411
this instanceof Ruby::InstanceVariable or
355-
this instanceof Ruby::ClassVariable or
356-
this instanceof Ruby::Self
412+
this instanceof Ruby::ClassVariable
357413
}
358414
}
359415

@@ -429,10 +485,9 @@ abstract class VariableImpl extends TVariable {
429485
abstract Location getLocationImpl();
430486
}
431487

432-
class TVariableReal =
433-
TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal or TSelfVariable;
488+
class TVariableReal = TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal;
434489

435-
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable;
490+
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth;
436491

437492
/**
438493
* A "real" (i.e. non-synthesized) variable. This class only exists to
@@ -458,19 +513,19 @@ private class VariableRealAdapter extends VariableImpl, TVariableReal instanceof
458513
}
459514

460515
class LocalVariableReal extends VariableReal, TLocalVariableReal {
461-
private Scope::Range scope;
462-
private string name;
463-
private Ruby::AstNode i;
516+
private Local l;
464517

465-
LocalVariableReal() { this = TLocalVariableReal(scope, name, i) }
518+
LocalVariableReal() { this = TLocalVariableReal(l) }
466519

467-
final override string getNameImpl() { result = name }
520+
Ruby::AstNode getDefiningNode() { result = l.getDefiningNode() }
468521

469-
final override Location getLocationImpl() { result = i.getLocation() }
522+
final override string getNameImpl() { result = l.getName() }
470523

471-
final override Scope::Range getDeclaringScopeImpl() { result = scope }
524+
final override Location getLocationImpl() { result = l.getLocation() }
525+
526+
final override Scope::Range getDeclaringScopeImpl() { result = l.getScope() }
472527

473-
final VariableAccess getDefiningAccessImpl() { toGenerated(result) = i }
528+
final VariableAccess getDefiningAccessImpl() { toGenerated(result) = l.getDefiningNode() }
474529
}
475530

476531
class LocalVariableSynth extends VariableImpl, TLocalVariableSynth {
@@ -531,32 +586,18 @@ class ClassVariableImpl extends VariableReal, TClassVariable {
531586
final override Scope::Range getDeclaringScopeImpl() { result = scope }
532587
}
533588

534-
class SelfVariableImpl extends VariableReal, TSelfVariable {
535-
private SelfBase::Range scope;
536-
537-
SelfVariableImpl() { this = TSelfVariable(scope) }
589+
class SelfVariableImpl extends LocalVariableReal {
590+
private ImplicitLocal l;
538591

539-
final override string getNameImpl() { result = "self" }
540-
541-
final override Location getLocationImpl() { result = scope.getLocation() }
542-
543-
final override Scope::Range getDeclaringScopeImpl() { result = scope }
592+
SelfVariableImpl() { this = TLocalVariableReal(l) }
544593
}
545594

546595
abstract class VariableAccessImpl extends Expr, TVariableAccess {
547596
abstract VariableImpl getVariableImpl();
548597
}
549598

550599
module LocalVariableAccess {
551-
predicate range(Ruby::Identifier id, TLocalVariableReal v) {
552-
access(id, v) and
553-
(
554-
explicitWriteAccess(id, _) or
555-
implicitWriteAccess(id) or
556-
vcall(id) or
557-
id = any(Ruby::VariableReferencePattern vr).getName()
558-
)
559-
}
600+
predicate range(Ruby::AstNode n, TLocalVariableReal v) { access(n, v) }
560601
}
561602

562603
class TVariableAccessReal =
@@ -681,7 +722,8 @@ private class SelfVariableAccessReal extends SelfVariableAccessImpl, TSelfReal {
681722

682723
SelfVariableAccessReal() {
683724
exists(Ruby::Self self |
684-
this = TSelfReal(self) and var = TSelfVariable(scopeOf(self).getEnclosingSelfScope())
725+
this = TSelfReal(self) and
726+
LocalVariableAccess::range(self, var)
685727
)
686728
}
687729

ruby/ql/lib/qlpack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies:
1414
codeql/ssa: ${workspace}
1515
codeql/tutorial: ${workspace}
1616
codeql/util: ${workspace}
17+
codeql/namebinding: ${workspace}
1718
dataExtensions:
1819
- codeql/ruby/frameworks/**/model.yml
1920
- codeql/ruby/frameworks/**/*.model.yml

0 commit comments

Comments
 (0)