C#: Rework function pointer/delegate call DF

This commit is contained in:
Tamas Vajk
2021-02-01 11:38:52 +01:00
parent 91152d3a65
commit 7d62e33feb
14 changed files with 133 additions and 275 deletions

View File

@@ -63,7 +63,7 @@ predicate mayEscape(LocalVariable v) {
exists(Callable c, Expr e, Expr succ | c = getACapturingCallableAncestor(v) |
e = getADelegateExpr(c) and
DataFlow::localExprFlow(e, succ) and
not succ = any(DelegateCall dc).getDelegateExpr() and
not succ = any(DelegateCall dc).getExpr() and
not succ = any(Cast cast).getExpr() and
not succ = any(Call call | nonEscapingCall(call)).getAnArgument() and
not succ = any(AssignableDefinition ad | ad.getTarget() instanceof LocalVariable).getSource()

View File

@@ -97,9 +97,7 @@ private class TranslatedDelegateInvokeCall extends TranslatedCompilerGeneratedCa
)
}
override TranslatedExprBase getQualifier() {
result = getTranslatedExpr(generatedBy.getDelegateExpr())
}
override TranslatedExprBase getQualifier() { result = getTranslatedExpr(generatedBy.getExpr()) }
override Instruction getQualifierResult() { result = getQualifier().getResult() }

View File

@@ -11,7 +11,8 @@ cached
private newtype TCallContext =
TEmptyCallContext() or
TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) }
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or
TArgFunctionPointerCallContext(FunctionPointerCall fptrc, int i) { exists(fptrc.getArgument(i)) }
/**
* A call context.
@@ -60,12 +61,14 @@ class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDel
override Location getLocation() { result = arg.getLocation() }
}
/** An argument of a delegate call. */
class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateCallContext {
DelegateCall dc;
/** An argument of a delegate or function pointer call. */
class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
DelegateLikeCall dc;
int arg;
DelegateCallArgumentCallContext() { this = TArgDelegateCallContext(dc, arg) }
DelegateLikeCallArgumentCallContext() {
this = TArgDelegateCallContext(dc, arg) or this = TArgFunctionPointerCallContext(dc, arg)
}
override predicate isArgument(Expr call, int i) {
call = dc and
@@ -76,3 +79,15 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
}
/** An argument of a delegate call. */
class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgDelegateCallContext {
DelegateCallArgumentCallContext() { this = TArgDelegateCallContext(dc, arg) }
}
/** An argument of a function pointer call. */
class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgFunctionPointerCallContext {
FunctionPointerCallArgumentCallContext() { this = TArgFunctionPointerCallContext(dc, arg) }
}

View File

@@ -14,8 +14,14 @@ private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.frameworks.system.linq.Expressions
/** A source of flow for a delegate or function pointer expression. */
private class DelegateLikeFlowSource extends DataFlow::ExprNode {
/** Gets the callable that is referenced in this delegate or function pointer flow source. */
Callable getCallable() { none() }
}
/** A source of flow for a delegate expression. */
private class DelegateFlowSource extends DataFlow::ExprNode {
private class DelegateFlowSource extends DelegateLikeFlowSource {
Callable c;
DelegateFlowSource() {
@@ -27,11 +33,26 @@ private class DelegateFlowSource extends DataFlow::ExprNode {
}
/** Gets the callable that is referenced in this delegate flow source. */
Callable getCallable() { result = c }
override Callable getCallable() { result = c }
}
/** A sink of flow for a delegate expression. */
abstract private class DelegateFlowSink extends DataFlow::Node {
/** A source of flow for a function pointer expression. */
private class FunctionPointerFlowSource extends DelegateLikeFlowSource {
Callable c;
FunctionPointerFlowSource() {
this.getExpr() =
any(Expr e |
c = e.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
)
}
/** Gets the callable that is referenced in this function pointer flow source. */
override Callable getCallable() { result = c }
}
/** A sink of flow for a delegate or function pointer expression. */
abstract private class DelegateLikeFlowSink extends DataFlow::Node {
/**
* Gets an actual run-time target of this delegate call in the given call
* context, if any. The call context records the *last* call required to
@@ -85,25 +106,41 @@ abstract private class DelegateFlowSink extends DataFlow::Node {
*/
cached
Callable getARuntimeTarget(CallContext context) {
exists(DelegateFlowSource dfs |
exists(DelegateLikeFlowSource dfs |
flowsFrom(this, dfs, _, context) and
result = dfs.getCallable()
)
}
}
/** A delegate or function pointer call expression. */
class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
/** Gets the delegate or function pointer call that this expression belongs to. */
DelegateLikeCall getCall() { none() }
}
/** A delegate call expression. */
class DelegateCallExpr extends DelegateFlowSink, DataFlow::ExprNode {
class DelegateCallExpr extends DelegateLikeCallExpr {
DelegateCall dc;
DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() }
DelegateCallExpr() { this.getExpr() = dc.getExpr() }
/** Gets the delegate call that this expression belongs to. */
DelegateCall getDelegateCall() { result = dc }
override DelegateCall getCall() { result = dc }
}
/** A function pointer call expression. */
class FunctionPointerCallExpr extends DelegateLikeCallExpr {
FunctionPointerCall fptrc;
FunctionPointerCallExpr() { this.getExpr() = fptrc.getExpr() }
/** Gets the function pointer call that this expression belongs to. */
override FunctionPointerCall getCall() { result = fptrc }
}
/** A parameter of delegate type belonging to a callable with a flow summary. */
class SummaryDelegateParameterSink extends DelegateFlowSink, ParameterNode {
class SummaryDelegateParameterSink extends DelegateLikeFlowSink, ParameterNode {
SummaryDelegateParameterSink() {
this.getType() instanceof SystemLinqExpressions::DelegateExtType and
this.isParameterOf(any(SummarizedCallable c), _)
@@ -111,7 +148,7 @@ class SummaryDelegateParameterSink extends DelegateFlowSink, ParameterNode {
}
/** A delegate expression that is added to an event. */
class AddEventSource extends DelegateFlowSink, DataFlow::ExprNode {
class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
AddEventExpr ae;
AddEventSource() { this.getExpr() = ae.getRValue() }
@@ -150,7 +187,7 @@ private class NormalReturnNode extends Node {
* records the last call on the path from `node` to `sink`, if any.
*/
private predicate flowsFrom(
DelegateFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
DelegateLikeFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
) {
// Base case
sink = node and
@@ -188,7 +225,8 @@ private predicate flowsFrom(
or
// Flow into a callable (delegate call)
exists(
ParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, int i
ParameterNode mid, CallContext prevLastCall, DelegateLikeCall call, Callable c, Parameter p,
int i
|
flowsFrom(sink, mid, isReturned, prevLastCall) and
isReturned = false and
@@ -238,14 +276,14 @@ private predicate flowIntoNonDelegateCall(NonDelegateCall call, Expr arg, DotNet
}
pragma[noinline]
private predicate flowIntoDelegateCall(DelegateCall call, Callable c, Expr arg, int i) {
exists(DelegateFlowSource dfs, DelegateCallExpr dce |
private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce |
// the call context is irrelevant because the delegate call
// itself will be the context
flowsFrom(dce, dfs, _, _) and
arg = call.getArgument(i) and
c = dfs.getCallable() and
call = dce.getDelegateCall()
call = dce.getCall()
)
}
@@ -255,11 +293,13 @@ private predicate flowOutOfNonDelegateCall(NonDelegateCall call, NormalReturnNod
}
pragma[noinline]
private predicate flowOutOfDelegateCall(DelegateCall dc, NormalReturnNode ret, CallContext lastCall) {
exists(DelegateFlowSource dfs, DelegateCallExpr dce, Callable c |
private predicate flowOutOfDelegateCall(
DelegateLikeCall dc, NormalReturnNode ret, CallContext lastCall
) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce, Callable c |
flowsFrom(dce, dfs, _, lastCall) and
ret.getEnclosingCallable() = c and
c = dfs.getCallable() and
dc = dce.getDelegateCall()
dc = dce.getCall()
)
}

View File

@@ -1,205 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides classes for resolving function pointer calls.
*/
import csharp
private import dotnet
private import semmle.code.csharp.dataflow.CallContext
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.DataFlowPublic
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.frameworks.system.linq.Expressions
/** A source of flow for a function pointer expression. */
private class FunctionPointerFlowSource extends DataFlow::ExprNode {
Callable c;
FunctionPointerFlowSource() {
this.getExpr() =
any(Expr e |
c = e.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
)
}
/** Gets the callable that is referenced in this function pointer flow source. */
Callable getCallable() { result = c }
}
/** A sink of flow for a function pointer expression. */
abstract private class FunctionPointerFlowSink extends DataFlow::Node {
/**
* Gets an actual run-time target of this function pointer call in the given call
* context, if any. The call context records the *last* call required to
* resolve the target, if any.
*
* See examples in `DelegateFlowSink`.
*/
cached
Callable getARuntimeTarget(CallContext context) {
exists(FunctionPointerFlowSource fptrfs |
flowsFrom(this, fptrfs, _, context) and
result = fptrfs.getCallable()
)
}
}
/** A function pointer call expression. */
class FunctionPointerCallExpr extends FunctionPointerFlowSink, DataFlow::ExprNode {
FunctionPointerCall fptrc;
FunctionPointerCallExpr() { this.getExpr() = fptrc.getFunctionPointerExpr() }
/** Gets the function pointer call that this expression belongs to. */
FunctionPointerCall getFunctionPointerCall() { result = fptrc }
}
/** A non-function pointer call. */
private class NonFunctionPointerCall extends Expr {
private DispatchCall dc;
NonFunctionPointerCall() { this = dc.getCall() }
/**
* Gets a run-time target of this call. A target is always a source
* declaration, and if the callable has both CIL and source code, only
* the source code version is returned.
*/
Callable getARuntimeTarget() { result = getCallableForDataFlow(dc.getADynamicTarget()) }
/** Gets the `i`th argument of this call. */
Expr getArgument(int i) { result = dc.getArgument(i) }
}
private class NormalReturnNode extends Node {
NormalReturnNode() { this.(ReturnNode).getKind() instanceof NormalReturnKind }
}
/**
* Holds if data can flow (inter-procedurally) to function pointer `sink` from
* `node`. This predicate searches backwards from `sink` to `node`.
*
* The parameter `isReturned` indicates whether the path from `sink` to
* `node` goes through a returned expression. The call context `lastCall`
* records the last call on the path from `node` to `sink`, if any.
*/
private predicate flowsFrom(
FunctionPointerFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
) {
// Base case
sink = node and
isReturned = false and
lastCall instanceof EmptyCallContext
or
// Local flow
exists(DataFlow::Node mid | flowsFrom(sink, mid, isReturned, lastCall) |
LocalFlow::localFlowStepCommon(node, mid)
or
exists(Ssa::Definition def |
LocalFlow::localSsaFlowStep(def, node, mid) and
LocalFlow::usesInstanceField(def)
)
)
or
// Flow through static field or property
exists(DataFlow::Node mid |
flowsFrom(sink, mid, _, _) and
jumpStep(node, mid) and
isReturned = false and
lastCall instanceof EmptyCallContext
)
or
// Flow into a callable (non-function pointer call)
exists(ParameterNode mid, CallContext prevLastCall, NonFunctionPointerCall call, Parameter p |
flowsFrom(sink, mid, isReturned, prevLastCall) and
isReturned = false and
p = mid.getParameter() and
flowIntoNonFunctionPointerCall(call, node.asExpr(), p) and
lastCall = getLastCall(prevLastCall, call, p.getPosition())
)
or
// Flow into a callable (function pointer call)
exists(
ParameterNode mid, CallContext prevLastCall, FunctionPointerCall call, Callable c, Parameter p,
int i
|
flowsFrom(sink, mid, isReturned, prevLastCall) and
isReturned = false and
flowIntoFunctionPointerCall(call, c, node.asExpr(), i) and
c.getParameter(i) = p and
p = mid.getParameter() and
lastCall = getLastCall(prevLastCall, call, i)
)
or
// Flow out of a callable (non-function pointer call).
exists(DataFlow::ExprNode mid |
flowsFrom(sink, mid, _, lastCall) and
isReturned = true and
flowOutOfNonFunctionPointerCall(mid.getExpr(), node)
)
or
// Flow out of a callable (function pointer call).
exists(DataFlow::ExprNode mid |
flowsFrom(sink, mid, _, _) and
isReturned = true and
flowOutOfFunctionPointerCall(mid.getExpr(), node, lastCall)
)
}
/**
* Gets the last call when tracking flow into `call`. The context
* `prevLastCall` is the previous last call, so the result is the
* previous call if it exists, otherwise `call` is the last call.
*/
bindingset[call, i]
private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
prevLastCall instanceof EmptyCallContext and
result.(ArgumentCallContext).isArgument(call, i)
or
prevLastCall instanceof ArgumentCallContext and
result = prevLastCall
}
pragma[noinline]
private predicate flowIntoNonFunctionPointerCall(
NonFunctionPointerCall call, Expr arg, DotNet::Parameter p
) {
exists(DotNet::Callable callable, int i |
callable = call.getARuntimeTarget() and
p = callable.getAParameter() and
arg = call.getArgument(i) and
i = p.getPosition()
)
}
pragma[noinline]
private predicate flowIntoFunctionPointerCall(FunctionPointerCall call, Callable c, Expr arg, int i) {
exists(FunctionPointerFlowSource fptrfs, FunctionPointerCallExpr fptrce |
// the call context is irrelevant because the function pointer call
// itself will be the context
flowsFrom(fptrce, fptrfs, _, _) and
arg = call.getArgument(i) and
c = fptrfs.getCallable() and
call = fptrce.getFunctionPointerCall()
)
}
pragma[noinline]
private predicate flowOutOfNonFunctionPointerCall(NonFunctionPointerCall call, NormalReturnNode ret) {
call.getARuntimeTarget() = ret.getEnclosingCallable()
}
pragma[noinline]
private predicate flowOutOfFunctionPointerCall(
FunctionPointerCall call, NormalReturnNode ret, CallContext lastCall
) {
exists(FunctionPointerFlowSource fptrfs, FunctionPointerCallExpr fptrce, Callable c |
flowsFrom(fptrce, fptrfs, _, lastCall) and
ret.getEnclosingCallable() = c and
c = fptrfs.getCallable() and
call = fptrce.getFunctionPointerCall()
)
}

View File

@@ -245,7 +245,7 @@ private module CallGraph {
* a library callable and `e` is a delegate argument.
*/
private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) {
c = any(DelegateCall dc | e = dc.getDelegateExpr()) and
c = any(DelegateCall dc | e = dc.getExpr()) and
libraryDelegateCall = false
or
c.getTarget().fromLibrary() and

View File

@@ -8,7 +8,6 @@ import Expr
import semmle.code.csharp.Callable
import semmle.code.csharp.dataflow.CallContext as CallContext
private import semmle.code.csharp.dataflow.internal.DelegateDataFlow
private import semmle.code.csharp.dataflow.internal.FunctionPointerDataFlow
private import semmle.code.csharp.dispatch.Dispatch
private import dotnet
@@ -528,6 +527,39 @@ class MutatorOperatorCall extends OperatorCall {
predicate isPostfix() { mutator_invocation_mode(this, 2) }
}
/**
* A function pointer or delegate call.
*/
abstract class DelegateLikeCall extends Call {
override Callable getTarget() { none() }
/**
* Gets a potential run-time target of this delegate or function pointer call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) { none() }
/**
* Gets the delegate or function pointer expression of this call. For example, the
* delegate expression of `X()` on line 5 is the access to the field `X` in
*
* ```csharp
* class A {
* Action X = () => { };
*
* void CallX() {
* X();
* }
* }
* ```
*/
Expr getExpr() { result = this.getChild(-1) }
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
}
/**
* A delegate call, for example `x()` on line 5 in
*
@@ -541,16 +573,14 @@ class MutatorOperatorCall extends OperatorCall {
* }
* ```
*/
class DelegateCall extends Call, @delegate_invocation_expr {
override Callable getTarget() { none() }
class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
/**
* Gets a potential run-time target of this delegate call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) {
override Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(DelegateCallExpr call |
this = call.getDelegateCall() and
this = call.getCall() and
result = call.getARuntimeTarget(cc)
)
or
@@ -568,7 +598,7 @@ class DelegateCall extends Call, @delegate_invocation_expr {
}
private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
this.getDelegateExpr().(EventAccess).getTarget() = result.getEvent() and
this.getExpr().(EventAccess).getTarget() = result.getEvent() and
enclosingCallable = result.getExpr().getEnclosingCallable()
}
@@ -580,25 +610,12 @@ class DelegateCall extends Call, @delegate_invocation_expr {
exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable())
}
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
/**
* Gets the delegate expression of this delegate call. For example, the
* delegate expression of `X()` on line 5 is the access to the field `X` in
* DEPRECATED: use `getExpr` instead.
*
* ```csharp
* class A {
* Action X = () => { };
*
* void CallX() {
* X();
* }
* }
* ```
* Gets the delegate expression of this call.
*/
Expr getDelegateExpr() { result = this.getChild(-1) }
deprecated Expr getDelegateExpr() { result = this.getExpr() }
override string toString() { result = "delegate call" }
@@ -616,27 +633,18 @@ class DelegateCall extends Call, @delegate_invocation_expr {
* }
* ```
*/
class FunctionPointerCall extends Call, @function_pointer_invocation_expr {
override Callable getTarget() { none() }
class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation_expr {
/**
* Gets a potential run-time target of this function pointer call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) {
override Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(FunctionPointerCallExpr call |
this = call.getFunctionPointerCall() and
this = call.getCall() and
result = call.getARuntimeTarget(cc)
)
}
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
/** Gets the function pointer expression of this call. */
Expr getFunctionPointerExpr() { result = this.getChild(-1) }
override string toString() { result = "function pointer call" }
override string getAPrimaryQlClass() { result = "FunctionPointerCall" }

View File

@@ -78,7 +78,7 @@ predicate depends(ValueOrRefType t, ValueOrRefType u) {
or
exists(DelegateCall dc, DelegateType dt |
dc.getEnclosingCallable().getDeclaringType() = t and
dc.getDelegateExpr().getType() = dt and
dc.getExpr().getType() = dt and
usesType(dt.getUnboundDeclaration(), u)
)
or

View File

@@ -116,12 +116,12 @@ class DelegateFlow
public unsafe void M17()
{
M16(&M2, (i) => {}); // MISSING: a(0) in M2 is calling this lambda
M16(&M2, (i) => { });
}
public unsafe void M18()
{
delegate*<Action<int>, void> fnptr = &M2;
fnptr((i) => {}); // MISSING: a(0) in M2 is calling this lambda
fnptr((i) => { });
}
}

View File

@@ -7,6 +7,8 @@ delegateCall
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... | DelegateFlow.cs:22:12:22:12 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction | DelegateFlow.cs:99:12:99:24 | delegate creation of type Action<Int32> |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... | DelegateFlow.cs:114:15:114:15 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... | DelegateFlow.cs:125:15:125:24 | (...) => ... |
| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... | DelegateFlow.cs:38:12:38:25 | (...) => ... |
| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:33:12:33:12 | access to parameter a |

View File

@@ -9,7 +9,7 @@ where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getNumberOfArguments() = 1 and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd1") and
e.getArgument(0).getValue() = "-40"
select m, e, a

View File

@@ -9,7 +9,7 @@ where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getNumberOfArguments() = 1 and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd7") and
e.getArgument(0).(AddExpr).getRightOperand().(LocalVariableAccess).getTarget().hasName("x")
select m, e, a

View File

@@ -8,7 +8,7 @@ from Method m, DelegateCall e, LocalVariableAccess a
where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd7") and
a.getTarget().getType().(DelegateType).hasQualifiedName("Expressions.D")
select m, e, a

View File

@@ -11,7 +11,7 @@ where
e.getTarget().getName() = "Click" and
e.getTarget().getDeclaringType() = m.getDeclaringType() and
d.getEnclosingCallable() = m and
d.getDelegateExpr() = e and
d.getExpr() = e and
d.getArgument(0) instanceof ThisAccess and
d.getArgument(1).(ParameterAccess).getTarget().hasName("e")
select m, d, e