Skip to content

Commit cf35299

Browse files
authored
Merge pull request #10910 from hvitved/ruby/call-graph-refactor
Ruby: Refactor call graph logic for singleton methods
2 parents ac013f9 + db699ae commit cf35299

File tree

1 file changed

+45
-21
lines changed

1 file changed

+45
-21
lines changed

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,10 @@ private module Cached {
375375
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
376376
RelevantCall call, Module m, string method
377377
) {
378-
exists(SsaSelfDefinitionNode self, Module target, MethodBase caller |
378+
exists(SsaSelfDefinitionNode self, MethodBase caller |
379379
flowsToMethodCallReceiver(call, self, method) and
380-
target = m.getSuperClass*() and
381-
selfInMethod(self.getVariable(), caller, target) and
382-
singletonMethod(caller, _, _) and
383-
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
384-
// methods on Object, if the block is actually evaluated in the context of another class.
385-
// The 'self' inside such a singleton method could then be any class, leading to self-calls
386-
// being resolved to arbitrary singleton methods.
387-
// To remedy this, we do not allow following super-classes all the way to Object.
388-
not (m != target and target = TResolved("Object"))
380+
selfInMethod(self.getVariable(), caller, m) and
381+
singletonMethod(caller, _, _)
389382
)
390383
}
391384

@@ -441,20 +434,22 @@ private module Cached {
441434
// M.extend(M)
442435
// M.instance # <- call
443436
// ```
444-
exists(Module m | result = lookupSingletonMethod(m, method) |
437+
exists(Module m, boolean exact | result = lookupSingletonMethod(m, method, exact) |
445438
// ```rb
446439
// def C.singleton; end # <- result
447440
// C.singleton # <- call
448441
// ```
449-
moduleFlowsToMethodCallReceiver(call, m, method)
442+
moduleFlowsToMethodCallReceiver(call, m, method) and
443+
exact = true
450444
or
451445
// ```rb
452446
// class C
453447
// def self.singleton; end # <- result
454448
// self.singleton # <- call
455449
// end
456450
// ```
457-
selfInModuleFlowsToMethodCallReceiver(call, m, method)
451+
selfInModuleFlowsToMethodCallReceiver(call, m, method) and
452+
exact = true
458453
or
459454
// ```rb
460455
// class C
@@ -464,7 +459,8 @@ private module Cached {
464459
// end
465460
// end
466461
// ```
467-
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method)
462+
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method) and
463+
exact = false
468464
)
469465
)
470466
or
@@ -796,26 +792,54 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
796792
)
797793
}
798794

799-
/**
800-
* Holds if `method` is a singleton method named `name`, defined on module
801-
* `m`, or any transitive base class of `m`.
802-
*/
803795
pragma[nomagic]
804-
private MethodBase lookupSingletonMethod(Module m, string name) {
796+
private MethodBase lookupSingletonMethodDirect(Module m, string name) {
805797
singletonMethodOnModule(result, name, m)
806798
or
807-
// cannot be part of `singletonMethodOnModule` because it would introduce
808-
// negative recursion below
809799
exists(DataFlow::LocalSourceNode sourceNode |
810800
sourceNode = trackModuleAccess(m) and
811801
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
812802
flowsToSingletonMethodObject(sourceNode, result, name)
813803
)
804+
}
805+
806+
/**
807+
* Holds if `method` is a singleton method named `name`, defined on module
808+
* `m`, or any transitive base class of `m`.
809+
*/
810+
pragma[nomagic]
811+
private MethodBase lookupSingletonMethod(Module m, string name) {
812+
result = lookupSingletonMethodDirect(m, name)
814813
or
814+
// cannot use `lookupSingletonMethodDirect` because it would introduce
815+
// negative recursion
815816
not singletonMethodOnModule(_, name, m) and
816817
result = lookupSingletonMethod(m.getSuperClass(), name)
817818
}
818819

820+
pragma[nomagic]
821+
private MethodBase lookupSingletonMethodInSubClasses(Module m, string name) {
822+
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
823+
// methods on Object, if the block is actually evaluated in the context of another class.
824+
// The 'self' inside such a singleton method could then be any class, leading to self-calls
825+
// being resolved to arbitrary singleton methods.
826+
// To remedy this, we do not allow following super-classes all the way to Object.
827+
not m = TResolved("Object") and
828+
exists(Module sub | sub.getSuperClass() = m |
829+
result = lookupSingletonMethodDirect(sub, name) or
830+
result = lookupSingletonMethodInSubClasses(sub, name)
831+
)
832+
}
833+
834+
pragma[nomagic]
835+
private MethodBase lookupSingletonMethod(Module m, string name, boolean exact) {
836+
result = lookupSingletonMethod(m, name) and
837+
exact in [false, true]
838+
or
839+
result = lookupSingletonMethodInSubClasses(m, name) and
840+
exact = false
841+
}
842+
819843
/**
820844
* Holds if `method` is a singleton method named `name`, defined on expression
821845
* `object`, where `object` is not likely to resolve to a module:

0 commit comments

Comments
 (0)