@@ -117,23 +117,52 @@ private module Cached {
117117 result = call .( DelegateDataFlowCall ) .getARuntimeTarget ( cc )
118118 }
119119
120+ /**
121+ * Holds if the set of viable implementations that can be called by `call`
122+ * might be improved by knowing the call context. This is the case if the
123+ * call is a delegate call, or if the qualifier accesses a parameter of
124+ * the enclosing callable `c` (including the implicit `this` parameter).
125+ */
126+ private predicate mayBenefitFromCallContext ( DataFlowCall call , Callable c ) {
127+ c = call .getEnclosingCallable ( ) and
128+ (
129+ exists ( CallContext cc | exists ( viableDelegateCallable ( call , cc ) ) |
130+ not cc instanceof EmptyCallContext
131+ )
132+ or
133+ call .( NonDelegateDataFlowCall ) .getDispatchCall ( ) .mayBenefitFromCallContext ( )
134+ )
135+ }
136+
120137 /**
121138 * Holds if the call context `ctx` reduces the set of viable run-time
122139 * targets of call `call` in `c`.
123140 */
124141 cached
125142 predicate reducedViableImplInCallContext ( DataFlowCall call , DataFlowCallable c , DataFlowCall ctx ) {
126- c = viableImpl ( ctx ) and
127- c = call .getEnclosingCallable ( ) and
128- exists ( CallContext cc | exists ( viableDelegateCallable ( call , cc ) ) |
129- not cc instanceof EmptyCallContext
143+ exists ( int tgts , int ctxtgts |
144+ mayBenefitFromCallContext ( call , c ) and
145+ c = viableCallable ( ctx ) and
146+ ctxtgts = count ( viableImplInCallContext ( call , ctx ) ) and
147+ tgts = strictcount ( viableImpl ( call ) ) and
148+ ctxtgts < tgts
130149 )
131150 }
132151
152+ /**
153+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
154+ * restricted to those `call`s for which a context might make a difference.
155+ */
133156 private DotNet:: Callable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
134157 exists ( ArgumentCallContext cc | result = viableDelegateCallable ( call , cc ) |
135158 cc .isArgument ( ctx .getExpr ( ) , _)
136159 )
160+ or
161+ result =
162+ call
163+ .( NonDelegateDataFlowCall )
164+ .getDispatchCall ( )
165+ .getADynamicTargetInCallContext ( ctx .( NonDelegateDataFlowCall ) .getDispatchCall ( ) )
137166 }
138167
139168 /**
@@ -155,9 +184,10 @@ private module Cached {
155184 cached
156185 predicate reducedViableImplInReturn ( DataFlowCallable c , DataFlowCall call ) {
157186 exists ( int tgts , int ctxtgts |
187+ mayBenefitFromCallContext ( call , _) and
158188 c = viableImpl ( call ) and
159- ctxtgts = strictcount ( DataFlowCall ctx | c = viableImplInCallContext ( call , ctx ) ) and
160- tgts = strictcount ( DataFlowCall ctx | viableImpl ( ctx ) = call .getEnclosingCallable ( ) ) and
189+ ctxtgts = count ( DataFlowCall ctx | c = viableImplInCallContext ( call , ctx ) ) and
190+ tgts = strictcount ( DataFlowCall ctx | viableCallable ( ctx ) = call .getEnclosingCallable ( ) ) and
161191 ctxtgts < tgts
162192 )
163193 }
@@ -278,6 +308,9 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
278308
279309 NonDelegateDataFlowCall ( ) { this = TNonDelegateCall ( cfn , dc ) }
280310
311+ /** Gets the underlying call. */
312+ DispatchCall getDispatchCall ( ) { result = dc }
313+
281314 override DotNet:: Callable getARuntimeTarget ( ) {
282315 result = getCallableForDataFlow ( dc .getADynamicTarget ( ) )
283316 }
0 commit comments