@@ -294,6 +294,30 @@ private DataFlowCallable viableLibraryCallable(DataFlowCall call) {
294
294
)
295
295
}
296
296
297
+ /** Holds if there is a call like `receiver.extend(M)`. */
298
+ pragma [ nomagic]
299
+ private predicate extendCall ( DataFlow:: ExprNode receiver , Module m ) {
300
+ exists ( DataFlow:: CallNode extendCall |
301
+ extendCall .getMethodName ( ) = "extend" and
302
+ exists ( DataFlow:: LocalSourceNode sourceNode | sourceNode .flowsTo ( extendCall .getArgument ( _) ) |
303
+ selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , m ) or
304
+ m = resolveConstantReadAccess ( sourceNode .asExpr ( ) .getExpr ( ) )
305
+ ) and
306
+ receiver = extendCall .getReceiver ( )
307
+ )
308
+ }
309
+
310
+ /** Holds if there is a call like `M.extend(N)` */
311
+ pragma [ nomagic]
312
+ private predicate extendCallModule ( Module m , Module n ) {
313
+ exists ( DataFlow:: LocalSourceNode receiver , DataFlow:: ExprNode e |
314
+ receiver .flowsTo ( e ) and extendCall ( e , n )
315
+ |
316
+ selfInModule ( receiver .( SsaSelfDefinitionNode ) .getVariable ( ) , m ) or
317
+ m = resolveConstantReadAccess ( receiver .asExpr ( ) .getExpr ( ) )
318
+ )
319
+ }
320
+
297
321
cached
298
322
private module Cached {
299
323
cached
@@ -340,12 +364,29 @@ private module Cached {
340
364
// def c.singleton; end # <- result
341
365
// c.singleton # <- call
342
366
// ```
367
+ // or an `extend`ed instance, e.g.
368
+ // ```rb
369
+ // c = C.new
370
+ // module M
371
+ // def instance; end # <- result
372
+ // end
373
+ // c.extend M
374
+ // c.instance # <- call
375
+ // ```
343
376
exists ( DataFlow:: Node receiver |
344
377
methodCall ( call , receiver , method ) and
345
378
receiver = trackSingletonMethodOnInstance ( result , method )
346
379
)
347
380
or
348
381
// singleton method defined on a module
382
+ // or an `extend`ed module, e.g.
383
+ // ```rb
384
+ // module M
385
+ // def instance; end # <- result
386
+ // end
387
+ // M.extend(M)
388
+ // M.instance # <- call
389
+ // ```
349
390
exists ( DataFlow:: Node sourceNode , Module m |
350
391
flowsToMethodCall ( call , sourceNode , method ) and
351
392
singletonMethodOnModule ( result , method , m )
@@ -664,6 +705,12 @@ private predicate flowsToSingletonMethodObject(
664
705
* class << c
665
706
* def m6; end # not included
666
707
* end
708
+ *
709
+ * module M
710
+ * def instance; end # included in `N` via `extend` call below
711
+ * end
712
+ * N.extend(M)
713
+ * N.instance
667
714
* ```
668
715
*/
669
716
pragma [ nomagic]
@@ -674,6 +721,11 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
674
721
)
675
722
or
676
723
flowsToSingletonMethodObject ( trackModuleAccess ( m ) , method , name )
724
+ or
725
+ exists ( Module other |
726
+ extendCallModule ( m , other ) and
727
+ method = lookupMethod ( other , name )
728
+ )
677
729
}
678
730
679
731
/**
@@ -700,6 +752,11 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
700
752
* class << c
701
753
* def m6; end # included
702
754
* end
755
+ *
756
+ * module M
757
+ * def instance; end # included in `c` via `extend` call below
758
+ * end
759
+ * c.extend(M)
703
760
* ```
704
761
*/
705
762
pragma [ nomagic]
@@ -708,6 +765,12 @@ predicate singletonMethodOnInstance(MethodBase method, string name, Expr object)
708
765
not selfInModule ( object .( SelfVariableReadAccess ) .getVariable ( ) , _) and
709
766
// cannot use `trackModuleAccess` because of negative recursion
710
767
not exists ( resolveConstantReadAccess ( object ) )
768
+ or
769
+ exists ( DataFlow:: ExprNode receiver , Module other |
770
+ extendCall ( receiver , other ) and
771
+ object = receiver .getExprNode ( ) .getExpr ( ) and
772
+ method = lookupMethod ( other , name )
773
+ )
711
774
}
712
775
713
776
/**
0 commit comments