Skip to content

Commit 453c9de

Browse files
committed
C#: Add flow summaries for compiler generated deconstruct methods.
1 parent c713e8b commit 453c9de

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

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

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

0 commit comments

Comments
 (0)