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]
This commit is contained in:
Chris Smowton
2024-09-19 15:33:47 +01:00
committed by GitHub
2 changed files with 61 additions and 4 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `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.

View File

@@ -995,6 +995,52 @@ private class InstanceCallable extends Callable {
Location getARelevantLocation() { result = l }
}
/**
* A callable which is either itself defined in source or which is the target
* of some call in source, and therefore ought to have dataflow nodes created.
*
* Note that for library methods these are always unbound declarations, since
* generic instantiations never have dataflow nodes constructed.
*/
private class CallableUsedInSource extends Callable {
CallableUsedInSource() {
// Should generate nodes even for abstract methods declared in source
this.fromSource()
or
// Should generate nodes even for synthetic methods derived from source
this.hasBody()
or
exists(Callable target |
exists(Call c |
// Note that getADynamicTarget does not always include getTarget.
target = c.getTarget()
or
// Note that getARuntimeTarget cannot be used here, because the
// DelegateLikeCall case depends on lambda-flow, which in turn
// uses the dataflow library; hence this would introduce recursion
// into the definition of data-flow nodes.
exists(DispatchCall dc | c = dc.getCall() | target = dc.getADynamicTarget())
)
or
target = any(CallableAccess ca).getTarget()
|
this = target.getUnboundDeclaration()
)
}
}
/**
* A field or property which is either itself defined in source or which is the target
* of some access in source, and therefore ought to have dataflow nodes created.
*/
private class FieldOrPropertyUsedInSource extends FieldOrProperty {
FieldOrPropertyUsedInSource() {
this.fromSource()
or
this.getAnAccess().fromSource()
}
}
/** A collection of cached types and predicates to be evaluated in the same stage. */
cached
private module Cached {
@@ -1018,8 +1064,13 @@ private module Cached {
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
cfn = def.getExpr().getAControlFlowNode()
} or
TExplicitParameterNode(Parameter p, DataFlowCallable c) { p = c.asCallable(_).getAParameter() } or
TInstanceParameterNode(InstanceCallable c, Location l) { l = c.getARelevantLocation() } or
TExplicitParameterNode(Parameter p, DataFlowCallable c) {
p = c.asCallable(_).(CallableUsedInSource).getAParameter()
} or
TInstanceParameterNode(InstanceCallable c, Location l) {
c instanceof CallableUsedInSource and
l = c.getARelevantLocation()
} or
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn, Boolean isPostUpdate) {
cfn.getAstNode() instanceof LocalFunctionStmt
@@ -1055,11 +1106,13 @@ private module Cached {
or
lambdaCallExpr(_, cfn)
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
sn.getSummarizedCallable() instanceof CallableUsedInSource
} or
TParamsArgumentNode(ControlFlow::Node callCfn) {
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
} or
TFlowInsensitiveFieldNode(FieldOrProperty f) { f.isFieldLike() } or
TFlowInsensitiveFieldNode(FieldOrPropertyUsedInSource f) { f.isFieldLike() } or
TFlowInsensitiveCapturedVariableNode(LocalScopeVariable v) { v.isCaptured() } or
TInstanceParameterAccessNode(ControlFlow::Node cfn, Boolean isPostUpdate) {
cfn = getAPrimaryConstructorParameterCfn(_)