@@ -277,6 +277,20 @@ private predicate hasAdjacentTypeCheckedReads(
277
277
)
278
278
}
279
279
280
+ /** Holds if `call` may resolve to the returned source-code method. */
281
+ private DataFlowCallable viableSourceCallable ( DataFlowCall call ) {
282
+ result = TCfgScope ( getTarget ( call .asCall ( ) ) ) and
283
+ not call .asCall ( ) .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
284
+ }
285
+
286
+ /** Holds if `call` may resolve to the returned summarized library method. */
287
+ private DataFlowCallable viableLibraryCallable ( DataFlowCall call ) {
288
+ exists ( LibraryCallable callable |
289
+ result = TLibraryCallable ( callable ) and
290
+ call .asCall ( ) .getExpr ( ) = callable .getACall ( )
291
+ )
292
+ }
293
+
280
294
cached
281
295
private module Cached {
282
296
cached
@@ -366,13 +380,9 @@ private module Cached {
366
380
/** Gets a viable run-time target for the call `call`. */
367
381
cached
368
382
DataFlowCallable viableCallable ( DataFlowCall call ) {
369
- result = TCfgScope ( getTarget ( call .asCall ( ) ) ) and
370
- not call .asCall ( ) .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
383
+ result = viableSourceCallable ( call )
371
384
or
372
- exists ( LibraryCallable callable |
373
- result = TLibraryCallable ( callable ) and
374
- call .asCall ( ) .getExpr ( ) = callable .getACall ( )
375
- )
385
+ result = viableLibraryCallable ( call )
376
386
}
377
387
378
388
cached
@@ -438,90 +448,103 @@ private DataFlow::LocalSourceNode trackModuleAccess(Module m) {
438
448
result = trackModuleAccess ( m , TypeTracker:: end ( ) )
439
449
}
440
450
441
- pragma [ nomagic]
442
- private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
443
- t .start ( ) and
444
- (
445
- result .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
446
- tp = TResolved ( "NilClass" ) and
447
- exact = true
448
- or
449
- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
450
- tp = TResolved ( "FalseClass" ) and
451
- exact = true
452
- or
453
- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
454
- tp = TResolved ( "TrueClass" ) and
455
- exact = true
456
- or
457
- result .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
458
- tp = TResolved ( "Integer" ) and
459
- exact = true
460
- or
461
- result .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
462
- tp = TResolved ( "Float" ) and
463
- exact = true
464
- or
465
- result .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
466
- tp = TResolved ( "Rational" ) and
467
- exact = true
468
- or
469
- result .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
470
- tp = TResolved ( "Complex" ) and
471
- exact = true
472
- or
473
- result .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
474
- tp = TResolved ( "String" ) and
475
- exact = true
476
- or
477
- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
478
- tp = TResolved ( "Array" ) and
479
- exact = true
480
- or
481
- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
482
- tp = TResolved ( "Hash" ) and
483
- exact = true
484
- or
485
- result .asExpr ( ) .getExpr ( ) instanceof MethodBase and
486
- tp = TResolved ( "Symbol" ) and
487
- exact = true
488
- or
489
- result .asParameter ( ) instanceof BlockParameter and
490
- tp = TResolved ( "Proc" ) and
491
- exact = true
451
+ /** Holds if `n` is an instance of type `tp`. */
452
+ private predicate isInstance ( DataFlow:: Node n , Module tp , boolean exact ) {
453
+ n .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
454
+ tp = TResolved ( "NilClass" ) and
455
+ exact = true
456
+ or
457
+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
458
+ tp = TResolved ( "FalseClass" ) and
459
+ exact = true
460
+ or
461
+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
462
+ tp = TResolved ( "TrueClass" ) and
463
+ exact = true
464
+ or
465
+ n .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
466
+ tp = TResolved ( "Integer" ) and
467
+ exact = true
468
+ or
469
+ n .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
470
+ tp = TResolved ( "Float" ) and
471
+ exact = true
472
+ or
473
+ n .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
474
+ tp = TResolved ( "Rational" ) and
475
+ exact = true
476
+ or
477
+ n .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
478
+ tp = TResolved ( "Complex" ) and
479
+ exact = true
480
+ or
481
+ n .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
482
+ tp = TResolved ( "String" ) and
483
+ exact = true
484
+ or
485
+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
486
+ tp = TResolved ( "Array" ) and
487
+ exact = true
488
+ or
489
+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
490
+ tp = TResolved ( "Hash" ) and
491
+ exact = true
492
+ or
493
+ n .asExpr ( ) .getExpr ( ) instanceof MethodBase and
494
+ tp = TResolved ( "Symbol" ) and
495
+ exact = true
496
+ or
497
+ n .asParameter ( ) instanceof BlockParameter and
498
+ tp = TResolved ( "Proc" ) and
499
+ exact = true
500
+ or
501
+ n .asExpr ( ) .getExpr ( ) instanceof Lambda and
502
+ tp = TResolved ( "Proc" ) and
503
+ exact = true
504
+ or
505
+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
506
+ flowsToMethodCall ( call , sourceNode , "new" ) and
507
+ exact = true and
508
+ n .asExpr ( ) = call
509
+ |
510
+ // `C.new`
511
+ sourceNode = trackModuleAccess ( tp )
492
512
or
493
- result .asExpr ( ) .getExpr ( ) instanceof Lambda and
494
- tp = TResolved ( "Proc" ) and
495
- exact = true
513
+ // `self.new` inside a module
514
+ selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
496
515
or
497
- exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
498
- flowsToMethodCall ( call , sourceNode , "new" ) and
499
- exact = true and
500
- result .asExpr ( ) = call
501
- |
502
- // `C.new`
503
- sourceNode = trackModuleAccess ( tp )
504
- or
505
- // `self.new` inside a module
506
- selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
516
+ // `self.new` inside a singleton method
517
+ selfInMethod ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , tp )
518
+ )
519
+ or
520
+ // `self` reference in method or top-level (but not in module or singleton method,
521
+ // where instance methods cannot be called; only singleton methods)
522
+ n =
523
+ any ( SsaSelfDefinitionNode self |
524
+ exists ( MethodBase m |
525
+ selfInMethod ( self .getVariable ( ) , m , tp ) and
526
+ not m instanceof SingletonMethod and
527
+ if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
528
+ )
507
529
or
508
- // ` self.new` inside a singleton method
509
- selfInMethod ( sourceNode . ( SsaSelfDefinitionNode ) . getVariable ( ) , any ( SingletonMethod sm ) , tp )
530
+ selfInToplevel ( self .getVariable ( ) , tp ) and
531
+ exact = true
510
532
)
511
- or
512
- // `self` reference in method or top-level (but not in module or singleton method,
513
- // where instance methods cannot be called; only singleton methods)
514
- result =
515
- any ( SsaSelfDefinitionNode self |
516
- exists ( MethodBase m |
517
- selfInMethod ( self .getVariable ( ) , m , tp ) and
518
- not m instanceof SingletonMethod and
519
- if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
520
- )
521
- or
522
- selfInToplevel ( self .getVariable ( ) , tp ) and
523
- exact = true
524
- )
533
+ or
534
+ // `in C => c then c.foo`
535
+ asModulePattern ( n , tp ) and
536
+ exact = false
537
+ or
538
+ // `case object when C then object.foo`
539
+ hasAdjacentTypeCheckedReads ( _, _, n .asExpr ( ) , tp ) and
540
+ exact = false
541
+ }
542
+
543
+ pragma [ nomagic]
544
+ private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
545
+ t .start ( ) and
546
+ (
547
+ isInstance ( result , tp , exact )
525
548
or
526
549
exists ( Module m |
527
550
( if m .isClass ( ) then tp = TResolved ( "Class" ) else tp = TResolved ( "Module" ) ) and
@@ -536,14 +559,6 @@ private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
536
559
// needed for e.g. `self.puts`
537
560
selfInMethod ( result .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , m )
538
561
)
539
- or
540
- // `in C => c then c.foo`
541
- asModulePattern ( result , tp ) and
542
- exact = false
543
- or
544
- // `case object when C then object.foo`
545
- hasAdjacentTypeCheckedReads ( _, _, result .asExpr ( ) , tp ) and
546
- exact = false
547
562
)
548
563
or
549
564
exists ( TypeTracker t2 , StepSummary summary |
@@ -778,19 +793,112 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
778
793
result = trackSingletonMethodOnInstance ( method , name , TypeTracker:: end ( ) )
779
794
}
780
795
796
+ /** Same as `isInstance`, but includes local must-flow through SSA definitions. */
797
+ private predicate isInstanceLocalMustFlow ( DataFlow:: Node n , Module tp , boolean exact ) {
798
+ isInstance ( n , tp , exact )
799
+ or
800
+ exists ( DataFlow:: Node mid | isInstanceLocalMustFlow ( mid , tp , exact ) |
801
+ n .asExpr ( ) = mid .( SsaDefinitionNode ) .getDefinition ( ) .getARead ( )
802
+ or
803
+ n .( SsaDefinitionNode ) .getDefinition ( ) .( Ssa:: WriteDefinition ) .assigns ( mid .asExpr ( ) )
804
+ )
805
+ }
806
+
807
+ /**
808
+ * Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, the receiver
809
+ * of `call` is a parameter access, where the corresponding argument of `ctx` is `arg`.
810
+ *
811
+ * `name` is the name of the method being called by `call`.
812
+ */
813
+ pragma [ nomagic]
814
+ private predicate mayBenefitFromCallContext0 (
815
+ CfgNodes:: ExprNodes:: CallCfgNode ctx , ArgumentNode arg , CfgNodes:: ExprNodes:: CallCfgNode call ,
816
+ Callable encl , string name
817
+ ) {
818
+ exists (
819
+ ParameterNodeImpl p , SsaDefinitionNode ssaNode , ParameterPosition ppos , ArgumentPosition apos
820
+ |
821
+ // the receiver of `call` references `p`
822
+ ssaNode = trackInstance ( _, _) and
823
+ LocalFlow:: localFlowSsaParamInput ( p , ssaNode ) and
824
+ flowsToMethodCall ( pragma [ only_bind_into ] ( call ) , pragma [ only_bind_into ] ( ssaNode ) ,
825
+ pragma [ only_bind_into ] ( name ) ) and
826
+ // `p` is a parameter of `encl`,
827
+ encl = call .getScope ( ) and
828
+ p .isParameterOf ( TCfgScope ( encl ) , ppos ) and
829
+ // `ctx` targets `encl`
830
+ getTarget ( ctx ) = encl and
831
+ // `arg` is the argument for `p` in the call `ctx`
832
+ arg .sourceArgumentOf ( ctx , apos ) and
833
+ parameterMatch ( ppos , apos )
834
+ )
835
+ }
836
+
837
+ /**
838
+ * Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, and
839
+ * the receiver of `call` is a parameter access, where the corresponding argument
840
+ * of `ctx` has type `tp`.
841
+ *
842
+ * `name` is the name of the method being called by `call`, and `exact` is pertaining
843
+ * to the type of the argument.
844
+ */
845
+ pragma [ nomagic]
846
+ private predicate mayBenefitFromCallContext1 (
847
+ CfgNodes:: ExprNodes:: CallCfgNode ctx , CfgNodes:: ExprNodes:: CallCfgNode call , Callable encl ,
848
+ Module tp , boolean exact , string name
849
+ ) {
850
+ exists ( ArgumentNode arg |
851
+ mayBenefitFromCallContext0 ( ctx , arg , call , encl , name ) and
852
+ // `arg` has a relevant instance type
853
+ isInstanceLocalMustFlow ( arg , pragma [ only_bind_out ] ( tp ) , exact ) and
854
+ exists ( lookupMethod ( tp , pragma [ only_bind_into ] ( name ) ) )
855
+ )
856
+ }
857
+
781
858
/**
782
859
* Holds if the set of viable implementations that can be called by `call`
783
860
* might be improved by knowing the call context. This is the case if the
784
- * qualifier accesses a parameter of the enclosing callable `c` (including
861
+ * receiver accesses a parameter of the enclosing callable `c` (including
785
862
* the implicit `self` parameter).
786
863
*/
787
- predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) { none ( ) }
864
+ predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) {
865
+ mayBenefitFromCallContext1 ( _, call .asCall ( ) , c .asCallable ( ) , _, _, _)
866
+ }
788
867
789
868
/**
790
869
* Gets a viable dispatch target of `call` in the context `ctx`. This is
791
870
* restricted to those `call`s for which a context might make a difference.
792
871
*/
793
- DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) { none ( ) }
872
+ pragma [ nomagic]
873
+ DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
874
+ // `ctx` can provide a potentially better type bound
875
+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call0 , Callable res |
876
+ call0 = call .asCall ( ) and
877
+ res = result .asCallable ( ) and
878
+ res = getTarget ( call0 ) and // make sure to not include e.g. private methods
879
+ exists ( Module tp , Module m , boolean exact , string name |
880
+ res = lookupMethod ( tp , name ) and
881
+ mayBenefitFromCallContext1 ( ctx .asCall ( ) , pragma [ only_bind_into ] ( call0 ) , _,
882
+ pragma [ only_bind_into ] ( m ) , exact , pragma [ only_bind_into ] ( name ) )
883
+ |
884
+ tp = m
885
+ or
886
+ exact = false and
887
+ tp .getSuperClass + ( ) = m
888
+ )
889
+ )
890
+ or
891
+ // `ctx` cannot provide a type bound
892
+ exists ( ArgumentNode arg |
893
+ mayBenefitFromCallContext0 ( ctx .asCall ( ) , arg , call .asCall ( ) , _, _) and
894
+ not isInstanceLocalMustFlow ( arg , _, _) and
895
+ result = viableSourceCallable ( call )
896
+ )
897
+ or
898
+ // library calls should always be able to resolve
899
+ mayBenefitFromCallContext0 ( ctx .asCall ( ) , _, call .asCall ( ) , _, _) and
900
+ result = viableLibraryCallable ( call )
901
+ }
794
902
795
903
predicate exprNodeReturnedFrom = exprNodeReturnedFromCached / 2 ;
796
904
0 commit comments