Skip to content

Commit 56ac990

Browse files
authored
Merge pull request github#7720 from michaelnebel/csharp/extended-prop-patterns
C#: Desugar property patterns that uses member access syntax.
2 parents bb2feda + 7cbeffc commit 56ac990

File tree

6 files changed

+209
-5
lines changed

6 files changed

+209
-5
lines changed
Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Generic;
13
using Microsoft.CodeAnalysis.CSharp.Syntax;
24
using Semmle.Extraction.Kinds;
35
using Semmle.Extraction.Entities;
@@ -10,17 +12,69 @@ internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpression
1012
base(new ExpressionInfo(cx, null, cx.CreateLocation(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null))
1113
{
1214
child = 0;
13-
var trapFile = cx.TrapWriter.Writer;
1415
foreach (var sub in pp.Subpatterns)
1516
{
16-
var p = Expressions.Pattern.Create(cx, sub.Pattern, this, child++);
17-
if (sub.NameColon is null)
17+
if (sub.ExpressionColon is null)
1818
{
19-
Context.ModelError(sub, "Expected to find 'Name:' in pattern.");
19+
Context.ModelError(sub, "Expected to find 'Expression:' in pattern.");
2020
continue;
2121
}
22-
trapFile.exprorstmt_name(p, sub.NameColon.Name.ToString());
22+
MakeExpressions(cx, this, sub, child++);
23+
}
24+
}
25+
26+
private record AccessStep(string Identifier, Microsoft.CodeAnalysis.Location Location);
27+
28+
private class AccessStepPack
29+
{
30+
public readonly List<AccessStep> Prefix = new List<AccessStep>();
31+
public AccessStep Last { get; private set; }
32+
33+
public AccessStepPack Add(string identifier, Microsoft.CodeAnalysis.Location location)
34+
{
35+
Prefix.Add(Last);
36+
Last = new AccessStep(identifier, location);
37+
return this;
38+
}
39+
40+
public AccessStepPack(string identifier, Microsoft.CodeAnalysis.Location location) =>
41+
Last = new AccessStep(identifier, location);
42+
}
43+
44+
private static AccessStepPack GetAccessStepPack(ExpressionSyntax syntax) =>
45+
syntax switch
46+
{
47+
MemberAccessExpressionSyntax memberAccess => GetAccessStepPack(memberAccess.Expression).Add(memberAccess.Name.Identifier.ValueText, memberAccess.Name.Identifier.GetLocation()),
48+
IdentifierNameSyntax identifier => new AccessStepPack(identifier.Identifier.Text, identifier.GetLocation()),
49+
_ => throw new InternalError(syntax, "Unexpected expression syntax in property patterns."),
50+
};
51+
52+
private static AccessStepPack GetAccessStepPack(BaseExpressionColonSyntax syntax) =>
53+
syntax switch
54+
{
55+
NameColonSyntax ncs => new AccessStepPack(ncs.Name.ToString(), ncs.Name.GetLocation()),
56+
ExpressionColonSyntax ecs => GetAccessStepPack(ecs.Expression),
57+
_ => throw new InternalError(syntax, "Unsupported expression colon in property pattern."),
58+
};
59+
60+
private static Expression CreateSyntheticExp(Context cx, Microsoft.CodeAnalysis.Location location, IExpressionParentEntity parent, int child) =>
61+
new Expression(new ExpressionInfo(cx, null, cx.CreateLocation(location), ExprKind.PROPERTY_PATTERN, parent, child, false, null));
62+
63+
private static void MakeExpressions(Context cx, IExpressionParentEntity parent, SubpatternSyntax syntax, int child)
64+
{
65+
var trapFile = cx.TrapWriter.Writer;
66+
var pack = GetAccessStepPack(syntax.ExpressionColon!);
67+
68+
foreach (var step in pack.Prefix)
69+
{
70+
var exp = CreateSyntheticExp(cx, step.Location, parent, child);
71+
trapFile.exprorstmt_name(exp, step.Identifier);
72+
parent = exp;
73+
child = 0;
2374
}
75+
76+
var p = Expressions.Pattern.Create(cx, syntax.Pattern, parent, child);
77+
trapFile.exprorstmt_name(p, pack.Last.Identifier);
2478
}
2579
}
2680
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: majorAnalysis
3+
---
4+
* Added support for C# 10 [Extended property patterns](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#extended-property-patterns).
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
3+
public record Point(int X, int Y);
4+
public record Line(Point P1, Point P2);
5+
public record Wrap(Line L);
6+
7+
public class PropertyPatterns
8+
{
9+
private static Line l = new Line(new Point(1, 2), new Point(3, 6));
10+
private static Wrap w = new Wrap(l);
11+
12+
public void M1()
13+
{
14+
if (l is { P1: { X: 1 } })
15+
{
16+
}
17+
18+
if (l is { P1.X: 2 })
19+
{
20+
}
21+
}
22+
23+
public void M2()
24+
{
25+
if (w is { L: { P2: { Y: 3 } } })
26+
{
27+
}
28+
29+
if (w is { L.P2.Y: 4 })
30+
{
31+
}
32+
}
33+
34+
public void M3()
35+
{
36+
if (w is { L: { P2.Y: 5 } })
37+
{
38+
}
39+
40+
if (w is { L.P2: { Y: 6 } })
41+
{
42+
}
43+
}
44+
45+
public void M4()
46+
{
47+
if (l is { P1: { X: 7 }, P1: { Y: 8 } })
48+
{
49+
}
50+
51+
if (l is { P1.X: 9, P1.Y: 10 })
52+
{
53+
}
54+
}
55+
}
56+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
propertyPatterns
2+
| PropertyPatterns.cs:14:18:14:33 | { ... } |
3+
| PropertyPatterns.cs:14:24:14:31 | { ... } |
4+
| PropertyPatterns.cs:18:18:18:28 | { ... } |
5+
| PropertyPatterns.cs:18:20:18:21 | { ... } |
6+
| PropertyPatterns.cs:25:18:25:40 | { ... } |
7+
| PropertyPatterns.cs:25:23:25:38 | { ... } |
8+
| PropertyPatterns.cs:25:29:25:36 | { ... } |
9+
| PropertyPatterns.cs:29:18:29:30 | { ... } |
10+
| PropertyPatterns.cs:29:20:29:20 | { ... } |
11+
| PropertyPatterns.cs:29:22:29:23 | { ... } |
12+
| PropertyPatterns.cs:36:18:36:35 | { ... } |
13+
| PropertyPatterns.cs:36:23:36:33 | { ... } |
14+
| PropertyPatterns.cs:36:25:36:26 | { ... } |
15+
| PropertyPatterns.cs:40:18:40:35 | { ... } |
16+
| PropertyPatterns.cs:40:20:40:20 | { ... } |
17+
| PropertyPatterns.cs:40:26:40:33 | { ... } |
18+
| PropertyPatterns.cs:47:18:47:47 | { ... } |
19+
| PropertyPatterns.cs:47:24:47:31 | { ... } |
20+
| PropertyPatterns.cs:47:38:47:45 | { ... } |
21+
| PropertyPatterns.cs:51:18:51:38 | { ... } |
22+
| PropertyPatterns.cs:51:20:51:21 | { ... } |
23+
| PropertyPatterns.cs:51:29:51:30 | { ... } |
24+
propertyPatternChild
25+
| PropertyPatterns.cs:14:18:14:33 | { ... } | 0 | PropertyPatterns.cs:14:24:14:31 | { ... } |
26+
| PropertyPatterns.cs:14:24:14:31 | { ... } | 0 | PropertyPatterns.cs:14:29:14:29 | 1 |
27+
| PropertyPatterns.cs:18:18:18:28 | { ... } | 0 | PropertyPatterns.cs:18:20:18:21 | { ... } |
28+
| PropertyPatterns.cs:18:20:18:21 | { ... } | 0 | PropertyPatterns.cs:18:26:18:26 | 2 |
29+
| PropertyPatterns.cs:25:18:25:40 | { ... } | 0 | PropertyPatterns.cs:25:23:25:38 | { ... } |
30+
| PropertyPatterns.cs:25:23:25:38 | { ... } | 0 | PropertyPatterns.cs:25:29:25:36 | { ... } |
31+
| PropertyPatterns.cs:25:29:25:36 | { ... } | 0 | PropertyPatterns.cs:25:34:25:34 | 3 |
32+
| PropertyPatterns.cs:29:18:29:30 | { ... } | 0 | PropertyPatterns.cs:29:20:29:20 | { ... } |
33+
| PropertyPatterns.cs:29:20:29:20 | { ... } | 0 | PropertyPatterns.cs:29:22:29:23 | { ... } |
34+
| PropertyPatterns.cs:29:22:29:23 | { ... } | 0 | PropertyPatterns.cs:29:28:29:28 | 4 |
35+
| PropertyPatterns.cs:36:18:36:35 | { ... } | 0 | PropertyPatterns.cs:36:23:36:33 | { ... } |
36+
| PropertyPatterns.cs:36:23:36:33 | { ... } | 0 | PropertyPatterns.cs:36:25:36:26 | { ... } |
37+
| PropertyPatterns.cs:36:25:36:26 | { ... } | 0 | PropertyPatterns.cs:36:31:36:31 | 5 |
38+
| PropertyPatterns.cs:40:18:40:35 | { ... } | 0 | PropertyPatterns.cs:40:20:40:20 | { ... } |
39+
| PropertyPatterns.cs:40:20:40:20 | { ... } | 0 | PropertyPatterns.cs:40:26:40:33 | { ... } |
40+
| PropertyPatterns.cs:40:26:40:33 | { ... } | 0 | PropertyPatterns.cs:40:31:40:31 | 6 |
41+
| PropertyPatterns.cs:47:18:47:47 | { ... } | 0 | PropertyPatterns.cs:47:24:47:31 | { ... } |
42+
| PropertyPatterns.cs:47:18:47:47 | { ... } | 1 | PropertyPatterns.cs:47:38:47:45 | { ... } |
43+
| PropertyPatterns.cs:47:24:47:31 | { ... } | 0 | PropertyPatterns.cs:47:29:47:29 | 7 |
44+
| PropertyPatterns.cs:47:38:47:45 | { ... } | 0 | PropertyPatterns.cs:47:43:47:43 | 8 |
45+
| PropertyPatterns.cs:51:18:51:38 | { ... } | 0 | PropertyPatterns.cs:51:20:51:21 | { ... } |
46+
| PropertyPatterns.cs:51:18:51:38 | { ... } | 1 | PropertyPatterns.cs:51:29:51:30 | { ... } |
47+
| PropertyPatterns.cs:51:20:51:21 | { ... } | 0 | PropertyPatterns.cs:51:26:51:26 | 9 |
48+
| PropertyPatterns.cs:51:29:51:30 | { ... } | 0 | PropertyPatterns.cs:51:35:51:36 | 10 |
49+
propertyPatternLabels
50+
| PropertyPatterns.cs:14:24:14:31 | { ... } | P1 |
51+
| PropertyPatterns.cs:14:29:14:29 | 1 | X |
52+
| PropertyPatterns.cs:18:20:18:21 | { ... } | P1 |
53+
| PropertyPatterns.cs:18:26:18:26 | 2 | X |
54+
| PropertyPatterns.cs:25:23:25:38 | { ... } | L |
55+
| PropertyPatterns.cs:25:29:25:36 | { ... } | P2 |
56+
| PropertyPatterns.cs:25:34:25:34 | 3 | Y |
57+
| PropertyPatterns.cs:29:20:29:20 | { ... } | L |
58+
| PropertyPatterns.cs:29:22:29:23 | { ... } | P2 |
59+
| PropertyPatterns.cs:29:28:29:28 | 4 | Y |
60+
| PropertyPatterns.cs:36:23:36:33 | { ... } | L |
61+
| PropertyPatterns.cs:36:25:36:26 | { ... } | P2 |
62+
| PropertyPatterns.cs:36:31:36:31 | 5 | Y |
63+
| PropertyPatterns.cs:40:20:40:20 | { ... } | L |
64+
| PropertyPatterns.cs:40:26:40:33 | { ... } | P2 |
65+
| PropertyPatterns.cs:40:31:40:31 | 6 | Y |
66+
| PropertyPatterns.cs:47:24:47:31 | { ... } | P1 |
67+
| PropertyPatterns.cs:47:29:47:29 | 7 | X |
68+
| PropertyPatterns.cs:47:38:47:45 | { ... } | P1 |
69+
| PropertyPatterns.cs:47:43:47:43 | 8 | Y |
70+
| PropertyPatterns.cs:51:20:51:21 | { ... } | P1 |
71+
| PropertyPatterns.cs:51:26:51:26 | 9 | X |
72+
| PropertyPatterns.cs:51:29:51:30 | { ... } | P1 |
73+
| PropertyPatterns.cs:51:35:51:36 | 10 | Y |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import csharp
2+
3+
query predicate propertyPatterns(PropertyPatternExpr exp) { any() }
4+
5+
query predicate propertyPatternChild(PropertyPatternExpr pp, int n, PatternExpr child) {
6+
child = pp.getPattern(n)
7+
}
8+
9+
query predicate propertyPatternLabels(LabeledPatternExpr exp, string label) {
10+
label = exp.getLabel()
11+
}

csharp/ql/test/library-tests/csharp10/recordTypes.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
recordTypes
2+
| PropertyPatterns.cs:3:1:3:34 | Point |
3+
| PropertyPatterns.cs:4:1:4:39 | Line |
4+
| PropertyPatterns.cs:5:1:5:27 | Wrap |
25
| RecordTypes.cs:3:1:6:2 | MyEntry |
36
| RecordTypes.cs:8:1:8:53 | MyClassRecord |
47
| RecordTypes.cs:10:1:10:70 | MyReadonlyRecordStruct |
@@ -9,5 +12,8 @@ recordStructs
912
| RecordTypes.cs:12:1:12:51 | MyRecordStruct1 |
1013
| WithExpression.cs:9:1:9:47 | MyRecordStruct2 |
1114
recordClass
15+
| PropertyPatterns.cs:3:1:3:34 | Point |
16+
| PropertyPatterns.cs:4:1:4:39 | Line |
17+
| PropertyPatterns.cs:5:1:5:27 | Wrap |
1218
| RecordTypes.cs:3:1:6:2 | MyEntry |
1319
| RecordTypes.cs:8:1:8:53 | MyClassRecord |

0 commit comments

Comments
 (0)