Skip to content

Commit 3fab3ea

Browse files
committed
C#: Add flow summaries for compiler generated deconstruct methods.
1 parent 6366d5c commit 3fab3ea

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

‎csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
private import csharp
66
private import semmle.code.csharp.commons.QualifiedName
77
private import semmle.code.csharp.frameworks.system.linq.Expressions
8+
private import semmle.code.csharp.frameworks.System
89
private import codeql.dataflow.internal.FlowSummaryImpl
910
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
1011
private import DataFlowImplSpecific as DataFlowImplSpecific
@@ -480,3 +481,47 @@ private class SummarizedCallableWithCallback extends Public::SummarizedCallable
480481
model = "heuristic-callback"
481482
}
482483
}
484+
485+
/**
486+
* A class of summaries for compiler generated Deconstruct methods.
487+
*
488+
* For record types, the compiler generates a `Deconstruct` method that allows deconstructing the record into its components.
489+
* ```csharp
490+
* public record R(int X, int Y);
491+
*
492+
* var r = new R(1, 2);
493+
* var (x, y) = r;
494+
* ```
495+
* In this case the compiler generates a `Deconstruct` method which is implicitly invoked on line 4 in the example above.
496+
*
497+
* The compiler generated `Deconstruct` method looks like:
498+
* ```csharp
499+
* public void Deconstruct(out int X, out int Y) {
500+
* X = this.X;
501+
* Y = this.Y;
502+
* }
503+
* ```
504+
* That is, we need to create summaries for compiler generated `Deconstruct` methods that reflect the flow from the record's properties
505+
* to the corresponding out parameters.
506+
*/
507+
private class SummarizedDeconstruct extends Public::SummarizedCallable instanceof DeconstructMethod {
508+
SummarizedDeconstruct() { this.isCompilerGenerated() }
509+
510+
override predicate propagatesFlow(
511+
string input, string output, boolean preservesValue, Public::Provenance provenance,
512+
boolean isExact, string model
513+
) {
514+
exists(Parameter p, int i, Property prop, string qualifier, string name |
515+
p = this.getParameter(i) and
516+
prop = this.getDeclaringType().getProperty(p.getName())
517+
|
518+
prop.hasFullyQualifiedName(qualifier, name) and
519+
input = "Argument[this].Property[" + getQualifiedName(qualifier, name) + "]" and
520+
output = "Argument[" + i + "]" and
521+
preservesValue = true and
522+
provenance = "manual" and
523+
isExact = true and
524+
model = "deconstruct"
525+
)
526+
}
527+
}

0 commit comments

Comments
 (0)