|
5 | 5 | private import csharp |
6 | 6 | private import semmle.code.csharp.commons.QualifiedName |
7 | 7 | private import semmle.code.csharp.frameworks.system.linq.Expressions |
| 8 | +private import semmle.code.csharp.frameworks.System |
8 | 9 | private import codeql.dataflow.internal.FlowSummaryImpl |
9 | 10 | private import codeql.dataflow.internal.AccessPathSyntax as AccessPath |
10 | 11 | private import DataFlowImplSpecific as DataFlowImplSpecific |
@@ -480,3 +481,47 @@ private class SummarizedCallableWithCallback extends Public::SummarizedCallable |
480 | 481 | model = "heuristic-callback" |
481 | 482 | } |
482 | 483 | } |
| 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