Skip to content

Commit 0deefad

Browse files
authored
Merge pull request #17483 from smowton/smowton/feature/csharp-dataflow-fewer-nodes-including-virtual-dispatch
C#: Restrict dataflow node creation to source and source-referenced entities [virtual-dispatch-inclusive variant]
2 parents f38f818 + bb82dc1 commit 0deefad

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* `DataFlow::Node` instances are no longer created for library methods and fields that are not callable (either statically or dynamically) or otherwise referred to from source code. This may affect third-party queries that use these nodes to identify library methods or fields that are present in DLL files where those methods or fields are unreferenced. If this presents a problem, consider using `Callable` and other non-dataflow classes to identify such library entities.

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,52 @@ private class InstanceCallable extends Callable {
995995
Location getARelevantLocation() { result = l }
996996
}
997997

998+
/**
999+
* A callable which is either itself defined in source or which is the target
1000+
* of some call in source, and therefore ought to have dataflow nodes created.
1001+
*
1002+
* Note that for library methods these are always unbound declarations, since
1003+
* generic instantiations never have dataflow nodes constructed.
1004+
*/
1005+
private class CallableUsedInSource extends Callable {
1006+
CallableUsedInSource() {
1007+
// Should generate nodes even for abstract methods declared in source
1008+
this.fromSource()
1009+
or
1010+
// Should generate nodes even for synthetic methods derived from source
1011+
this.hasBody()
1012+
or
1013+
exists(Callable target |
1014+
exists(Call c |
1015+
// Note that getADynamicTarget does not always include getTarget.
1016+
target = c.getTarget()
1017+
or
1018+
// Note that getARuntimeTarget cannot be used here, because the
1019+
// DelegateLikeCall case depends on lambda-flow, which in turn
1020+
// uses the dataflow library; hence this would introduce recursion
1021+
// into the definition of data-flow nodes.
1022+
exists(DispatchCall dc | c = dc.getCall() | target = dc.getADynamicTarget())
1023+
)
1024+
or
1025+
target = any(CallableAccess ca).getTarget()
1026+
|
1027+
this = target.getUnboundDeclaration()
1028+
)
1029+
}
1030+
}
1031+
1032+
/**
1033+
* A field or property which is either itself defined in source or which is the target
1034+
* of some access in source, and therefore ought to have dataflow nodes created.
1035+
*/
1036+
private class FieldOrPropertyUsedInSource extends FieldOrProperty {
1037+
FieldOrPropertyUsedInSource() {
1038+
this.fromSource()
1039+
or
1040+
this.getAnAccess().fromSource()
1041+
}
1042+
}
1043+
9981044
/** A collection of cached types and predicates to be evaluated in the same stage. */
9991045
cached
10001046
private module Cached {
@@ -1018,8 +1064,13 @@ private module Cached {
10181064
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
10191065
cfn = def.getExpr().getAControlFlowNode()
10201066
} or
1021-
TExplicitParameterNode(Parameter p, DataFlowCallable c) { p = c.asCallable(_).getAParameter() } or
1022-
TInstanceParameterNode(InstanceCallable c, Location l) { l = c.getARelevantLocation() } or
1067+
TExplicitParameterNode(Parameter p, DataFlowCallable c) {
1068+
p = c.asCallable(_).(CallableUsedInSource).getAParameter()
1069+
} or
1070+
TInstanceParameterNode(InstanceCallable c, Location l) {
1071+
c instanceof CallableUsedInSource and
1072+
l = c.getARelevantLocation()
1073+
} or
10231074
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
10241075
TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn, Boolean isPostUpdate) {
10251076
cfn.getAstNode() instanceof LocalFunctionStmt
@@ -1055,11 +1106,13 @@ private module Cached {
10551106
or
10561107
lambdaCallExpr(_, cfn)
10571108
} or
1058-
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
1109+
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
1110+
sn.getSummarizedCallable() instanceof CallableUsedInSource
1111+
} or
10591112
TParamsArgumentNode(ControlFlow::Node callCfn) {
10601113
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
10611114
} or
1062-
TFlowInsensitiveFieldNode(FieldOrProperty f) { f.isFieldLike() } or
1115+
TFlowInsensitiveFieldNode(FieldOrPropertyUsedInSource f) { f.isFieldLike() } or
10631116
TFlowInsensitiveCapturedVariableNode(LocalScopeVariable v) { v.isCaptured() } or
10641117
TInstanceParameterAccessNode(ControlFlow::Node cfn, Boolean isPostUpdate) {
10651118
cfn = getAPrimaryConstructorParameterCfn(_)

0 commit comments

Comments
 (0)