Skip to content

Commit

Permalink
Merge pull request #17483 from smowton/smowton/feature/csharp-dataflo…
Browse files Browse the repository at this point in the history
…w-fewer-nodes-including-virtual-dispatch

C#: Restrict dataflow node creation to source and source-referenced entities [virtual-dispatch-inclusive variant]
  • Loading branch information
smowton authored Sep 19, 2024
2 parents f38f818 + bb82dc1 commit 0deefad
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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(_)
Expand Down

0 comments on commit 0deefad

Please sign in to comment.