diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
index dd7246fa6597..0402c345532e 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
@@ -785,6 +785,18 @@ public static AnnotatedTypeSymbol GetType(this Context cx, Microsoft.CodeAnalysi
return new AnnotatedTypeSymbol(info.Type.DisambiguateType(), info.Nullability.Annotation);
}
+ ///
+ /// Gets the converted type of a syntax node, or default if it could not be determined.
+ ///
+ /// Extractor context.
+ /// The syntax node to determine the converted type for.
+ /// The converted type symbol of the node, or default.
+ public static AnnotatedTypeSymbol GetConvertedType(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node)
+ {
+ var info = GetTypeInfo(cx, node);
+ return new AnnotatedTypeSymbol(info.ConvertedType.DisambiguateType(), info.ConvertedNullability.Annotation);
+ }
+
///
/// Gets the annotated type arguments of an INamedTypeSymbol.
/// This has not yet been exposed on the public API.
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/BinaryPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/BinaryPattern.cs
index 3eaf9cc55c3d..e45b53e3cfae 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/BinaryPattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/BinaryPattern.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class BinaryPattern : Expression
{
public BinaryPattern(Context cx, BinaryPatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), GetKind(syntax.OperatorToken, syntax), parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), GetKind(syntax.OperatorToken, syntax), parent, child, isCompilerGenerated: false, null))
{
Pattern.Create(cx, syntax.Left, this, 0);
Pattern.Create(cx, syntax.Right, this, 1);
@@ -24,4 +24,4 @@ private static ExprKind GetKind(SyntaxToken operatorToken, BinaryPatternSyntax s
};
}
}
-}
\ No newline at end of file
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/ListPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/ListPattern.cs
index 492e2bcb1ce0..a28743058e6d 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/ListPattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/ListPattern.cs
@@ -7,9 +7,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class ListPattern : Expression
{
internal ListPattern(Context cx, ListPatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.LIST_PATTERN, parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), ExprKind.LIST_PATTERN, parent, child, isCompilerGenerated: false, null))
{
syntax.Patterns.ForEach((p, i) => Pattern.Create(cx, p, this, i));
}
}
-}
\ No newline at end of file
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RecursivePattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RecursivePattern.cs
index febbdacd14c4..5e18169ceb14 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RecursivePattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RecursivePattern.cs
@@ -15,7 +15,7 @@ internal class RecursivePattern : Expression
/// The parent pattern/expression.
/// The child index of this pattern.
public RecursivePattern(Context cx, RecursivePatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, isCompilerGenerated: false, null))
{
// Extract the type access
if (syntax.Type is TypeSyntax t)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs
index 4f6c2eac11ff..570f35ac817b 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class RelationalPattern : Expression
{
public RelationalPattern(Context cx, RelationalPatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), GetKind(syntax.OperatorToken), parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), GetKind(syntax.OperatorToken), parent, child, isCompilerGenerated: false, null))
{
Expression.Create(cx, syntax.Expression, this, 0);
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/SlicePattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/SlicePattern.cs
index 69d7cc878beb..4ef1ba856f7f 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/SlicePattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/SlicePattern.cs
@@ -6,7 +6,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class SlicePattern : Expression
{
public SlicePattern(Context cx, SlicePatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.SLICE_PATTERN, parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), ExprKind.SLICE_PATTERN, parent, child, isCompilerGenerated: false, null))
{
if (syntax.Pattern is not null)
{
@@ -14,4 +14,4 @@ public SlicePattern(Context cx, SlicePatternSyntax syntax, IExpressionParentEnti
}
}
}
-}
\ No newline at end of file
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/UnaryPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/UnaryPattern.cs
index 64663c7a6e14..2c7dc32e8f4f 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/UnaryPattern.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/UnaryPattern.cs
@@ -6,9 +6,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class UnaryPattern : Expression
{
public UnaryPattern(Context cx, UnaryPatternSyntax syntax, IExpressionParentEntity parent, int child) :
- base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.NOT_PATTERN, parent, child, isCompilerGenerated: false, null))
+ base(new ExpressionInfo(cx, cx.GetConvertedType(syntax), cx.CreateLocation(syntax.GetLocation()), ExprKind.NOT_PATTERN, parent, child, isCompilerGenerated: false, null))
{
Pattern.Create(cx, syntax.Pattern, this, 0);
}
}
-}
\ No newline at end of file
+}
diff --git a/csharp/ql/test/library-tests/csharp11/patternExprTypes.expected b/csharp/ql/test/library-tests/csharp11/patternExprTypes.expected
new file mode 100644
index 000000000000..c24c0edda27e
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp11/patternExprTypes.expected
@@ -0,0 +1,58 @@
+| ListPattern.cs:7:18:7:19 | [ ... ] | Int32[] |
+| ListPattern.cs:8:18:8:20 | [ ... ] | Int32[] |
+| ListPattern.cs:8:19:8:19 | 1 | Int32 |
+| ListPattern.cs:9:18:9:23 | [ ... ] | Int32[] |
+| ListPattern.cs:9:19:9:19 | _ | Int32 |
+| ListPattern.cs:9:22:9:22 | 2 | Int32 |
+| ListPattern.cs:10:18:10:30 | [ ... ] | Int32[] |
+| ListPattern.cs:10:19:10:23 | Int32 y | Int32 |
+| ListPattern.cs:10:26:10:26 | 3 | Int32 |
+| ListPattern.cs:10:29:10:29 | 4 | Int32 |
+| ListPattern.cs:11:18:11:31 | [ ... ] | Int32[] |
+| ListPattern.cs:11:19:11:19 | 5 | Int32 |
+| ListPattern.cs:11:19:11:24 | ... or ... | Int32 |
+| ListPattern.cs:11:24:11:24 | 6 | Int32 |
+| ListPattern.cs:11:27:11:27 | _ | Int32 |
+| ListPattern.cs:11:30:11:30 | 7 | Int32 |
+| ListPattern.cs:12:18:12:31 | [ ... ] | Int32[] |
+| ListPattern.cs:12:19:12:23 | Int32 a | Int32 |
+| ListPattern.cs:12:26:12:27 | .. | Int32[] |
+| ListPattern.cs:12:30:12:30 | 2 | Int32 |
+| ListPattern.cs:13:18:13:50 | [ ... ] | Int32[] |
+| ListPattern.cs:13:19:13:23 | Int32 b | Int32 |
+| ListPattern.cs:13:26:13:46 | .. | Int32[] |
+| ListPattern.cs:13:29:13:46 | { ... } | Int32[] |
+| ListPattern.cs:13:29:13:46 | { ... } | null |
+| ListPattern.cs:13:39:13:39 | 2 | Int32 |
+| ListPattern.cs:13:39:13:44 | ... or ... | Int32 |
+| ListPattern.cs:13:44:13:44 | 5 | Int32 |
+| ListPattern.cs:13:49:13:49 | 2 | Int32 |
+| ListPattern.cs:20:18:20:19 | [ ... ] | String[] |
+| ListPattern.cs:22:18:22:22 | [ ... ] | String[] |
+| ListPattern.cs:22:19:22:21 | "A" | String |
+| ListPattern.cs:24:18:24:25 | [ ... ] | String[] |
+| ListPattern.cs:24:19:24:19 | _ | String |
+| ListPattern.cs:24:22:24:24 | "B" | String |
+| ListPattern.cs:26:18:26:34 | [ ... ] | String[] |
+| ListPattern.cs:26:19:26:23 | String y | String |
+| ListPattern.cs:26:26:26:28 | "C" | String |
+| ListPattern.cs:26:31:26:33 | "D" | String |
+| ListPattern.cs:28:18:28:37 | [ ... ] | String[] |
+| ListPattern.cs:28:19:28:21 | "E" | String |
+| ListPattern.cs:28:19:28:28 | ... or ... | String |
+| ListPattern.cs:28:26:28:28 | "F" | String |
+| ListPattern.cs:28:31:28:31 | _ | String |
+| ListPattern.cs:28:34:28:36 | "G" | String |
+| ListPattern.cs:30:18:30:33 | [ ... ] | String[] |
+| ListPattern.cs:30:19:30:23 | String a | String |
+| ListPattern.cs:30:26:30:27 | .. | String[] |
+| ListPattern.cs:30:30:30:32 | "H" | String |
+| ListPattern.cs:32:18:32:39 | [ ... ] | String[] |
+| ListPattern.cs:32:19:32:23 | String b | String |
+| ListPattern.cs:32:26:32:33 | .. | String[] |
+| ListPattern.cs:32:29:32:33 | String[] c | String[] |
+| ListPattern.cs:32:36:32:38 | "I" | String |
+| PatternMatchSpan.cs:8:19:8:23 | "ABC" | String |
+| PatternMatchSpan.cs:15:18:15:22 | "DEF" | String |
+| Strings.cs:10:13:10:14 | 42 | Int32 |
+| Strings.cs:11:13:11:13 | _ | Int32 |
diff --git a/csharp/ql/test/library-tests/csharp11/patternExprTypes.ql b/csharp/ql/test/library-tests/csharp11/patternExprTypes.ql
new file mode 100644
index 000000000000..814019ecc818
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp11/patternExprTypes.ql
@@ -0,0 +1,4 @@
+import csharp
+
+from PatternExpr pattern
+select pattern, pattern.getType().toString()
diff --git a/csharp/ql/test/library-tests/csharp8/patterns.expected b/csharp/ql/test/library-tests/csharp8/patterns.expected
index ca4c7f507793..fbfba8a6a825 100644
--- a/csharp/ql/test/library-tests/csharp8/patterns.expected
+++ b/csharp/ql/test/library-tests/csharp8/patterns.expected
@@ -188,16 +188,140 @@ labeledPatternExpr
| patterns.cs:129:27:129:28 | 10 | X |
| patterns.cs:142:31:142:32 | 10 | X |
tupleTypes
-| patterns.cs:59:18:59:27 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:86:15:86:19 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:91:16:91:20 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:108:9:108:20 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:108:24:108:31 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:110:22:110:26 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:111:22:111:26 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:115:9:115:16 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:115:20:115:27 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:117:27:117:33 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:118:28:118:34 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:119:33:119:38 | (..., ...) | file://:0:0:0:0 | (Int32,Int32) |
-| patterns.cs:131:13:131:22 | (..., ...) | file://:0:0:0:0 | (Int32,Object) |
+| patterns.cs:59:18:59:27 | (..., ...) | (Int32,Int32) |
+| patterns.cs:86:15:86:19 | (..., ...) | (Int32,Int32) |
+| patterns.cs:91:16:91:20 | (..., ...) | (Int32,Int32) |
+| patterns.cs:108:9:108:20 | (..., ...) | (Int32,Int32) |
+| patterns.cs:108:24:108:31 | (..., ...) | (Int32,Int32) |
+| patterns.cs:110:22:110:26 | (..., ...) | (Int32,Int32) |
+| patterns.cs:111:22:111:26 | (..., ...) | (Int32,Int32) |
+| patterns.cs:115:9:115:16 | (..., ...) | (Int32,Int32) |
+| patterns.cs:115:20:115:27 | (..., ...) | (Int32,Int32) |
+| patterns.cs:117:27:117:33 | (..., ...) | (Int32,Int32) |
+| patterns.cs:118:28:118:34 | (..., ...) | (Int32,Int32) |
+| patterns.cs:119:33:119:38 | (..., ...) | (Int32,Int32) |
+| patterns.cs:131:13:131:22 | (..., ...) | (Int32,Object) |
+patternExprTypes
+| patterns.cs:9:18:9:29 | MyStruct ms1 | MyStruct |
+| patterns.cs:13:18:13:25 | access to type MyStruct | MyStruct |
+| patterns.cs:13:18:13:40 | MyStruct s | MyStruct |
+| patterns.cs:13:18:13:40 | { ... } | MyStruct |
+| patterns.cs:13:27:13:38 | { ... } | null |
+| patterns.cs:13:32:13:36 | Int32 x | Int32 |
+| patterns.cs:17:18:17:19 | { ... } | null |
+| patterns.cs:17:18:17:21 | Object p | Object |
+| patterns.cs:17:18:17:21 | { ... } | Object |
+| patterns.cs:22:18:22:25 | access to type MyStruct | MyStruct |
+| patterns.cs:22:18:22:53 | { ... } | MyStruct |
+| patterns.cs:22:27:22:53 | { ... } | null |
+| patterns.cs:22:31:22:32 | 12 | Int32 |
+| patterns.cs:22:38:22:51 | { ... } | MyStruct |
+| patterns.cs:22:38:22:51 | { ... } | null |
+| patterns.cs:22:42:22:49 | Int32 subX | Int32 |
+| patterns.cs:27:18:27:25 | access to type MyStruct | MyStruct |
+| patterns.cs:27:18:27:58 | { ... } | MyStruct |
+| patterns.cs:27:27:27:58 | { ... } | null |
+| patterns.cs:27:31:27:32 | 12 | Int32 |
+| patterns.cs:27:38:27:45 | access to type MyStruct | MyStruct |
+| patterns.cs:27:38:27:56 | MyStruct ms | MyStruct |
+| patterns.cs:27:38:27:56 | { ... } | MyStruct |
+| patterns.cs:27:47:27:53 | { ... } | null |
+| patterns.cs:27:51:27:51 | _ | Int32 |
+| patterns.cs:38:18:38:29 | MyStruct ms1 | MyStruct |
+| patterns.cs:41:18:41:29 | MyStruct ms2 | MyStruct |
+| patterns.cs:48:18:48:25 | access to type MyStruct | MyStruct |
+| patterns.cs:48:18:48:38 | { ... } | MyStruct |
+| patterns.cs:48:27:48:38 | { ... } | null |
+| patterns.cs:48:32:48:36 | Int32 x | Int32 |
+| patterns.cs:51:18:51:25 | access to type MyStruct | MyStruct |
+| patterns.cs:51:18:51:38 | MyStruct ms | MyStruct |
+| patterns.cs:51:18:51:38 | { ... } | MyStruct |
+| patterns.cs:51:27:51:35 | { ... } | null |
+| patterns.cs:51:32:51:33 | 10 | Int32 |
+| patterns.cs:54:18:54:30 | { ... } | MyStruct |
+| patterns.cs:54:18:54:30 | { ... } | null |
+| patterns.cs:54:23:54:28 | Int32 x2 | Int32 |
+| patterns.cs:57:18:57:23 | ( ... ) | null |
+| patterns.cs:57:18:57:23 | { ... } | MyStruct |
+| patterns.cs:57:19:57:19 | 1 | Int32 |
+| patterns.cs:57:22:57:22 | 2 | Int32 |
+| patterns.cs:59:18:59:27 | (..., ...) | (Int32,Int32) |
+| patterns.cs:59:23:59:23 | Int32 x | Int32 |
+| patterns.cs:59:26:59:26 | Int32 y | Int32 |
+| patterns.cs:67:18:67:25 | access to type MyStruct | MyStruct |
+| patterns.cs:67:18:67:38 | { ... } | MyStruct |
+| patterns.cs:67:27:67:38 | { ... } | null |
+| patterns.cs:67:32:67:36 | Int32 x | Int32 |
+| patterns.cs:70:18:70:25 | access to type MyStruct | MyStruct |
+| patterns.cs:70:18:70:38 | MyStruct ms | MyStruct |
+| patterns.cs:70:18:70:38 | { ... } | MyStruct |
+| patterns.cs:70:27:70:35 | { ... } | null |
+| patterns.cs:70:32:70:33 | 10 | Int32 |
+| patterns.cs:78:18:78:33 | ( ... ) | null |
+| patterns.cs:78:18:78:33 | { ... } | ITuple |
+| patterns.cs:78:19:78:23 | Int32 x | Int32 |
+| patterns.cs:78:26:78:32 | Single y | Single |
+| patterns.cs:80:18:80:19 | ( ... ) | null |
+| patterns.cs:80:18:80:19 | { ... } | ITuple |
+| patterns.cs:82:18:82:19 | { ... } | Object |
+| patterns.cs:82:18:82:19 | { ... } | null |
+| patterns.cs:88:18:88:23 | ( ... ) | null |
+| patterns.cs:88:18:88:23 | { ... } | (Int32,Int32) |
+| patterns.cs:88:19:88:19 | 1 | Int32 |
+| patterns.cs:88:22:88:22 | 2 | Int32 |
+| patterns.cs:93:18:93:27 | ( ... ) | null |
+| patterns.cs:93:18:93:27 | { ... } | (Int32,Int32) |
+| patterns.cs:93:19:93:19 | 1 | Int32 |
+| patterns.cs:93:22:93:26 | Int32 x | Int32 |
+| patterns.cs:94:18:94:23 | ( ... ) | null |
+| patterns.cs:94:18:94:23 | { ... } | (Int32,Int32) |
+| patterns.cs:94:19:94:19 | 2 | Int32 |
+| patterns.cs:94:22:94:22 | _ | Int32 |
+| patterns.cs:101:13:101:17 | Int32 y | Int32 |
+| patterns.cs:102:13:102:13 | _ | Int32 |
+| patterns.cs:110:13:110:17 | ( ... ) | null |
+| patterns.cs:110:13:110:17 | { ... } | (Int32,Int32) |
+| patterns.cs:110:14:110:14 | 0 | Int32 |
+| patterns.cs:110:16:110:16 | 1 | Int32 |
+| patterns.cs:111:13:111:17 | ( ... ) | null |
+| patterns.cs:111:13:111:17 | { ... } | (Int32,Int32) |
+| patterns.cs:111:14:111:14 | 1 | Int32 |
+| patterns.cs:111:16:111:16 | 0 | Int32 |
+| patterns.cs:117:13:117:22 | ( ... ) | null |
+| patterns.cs:117:13:117:22 | { ... } | (Int32,Int32) |
+| patterns.cs:117:14:117:14 | 0 | Int32 |
+| patterns.cs:117:16:117:21 | Int32 y2 | Int32 |
+| patterns.cs:118:13:118:23 | ( ... ) | null |
+| patterns.cs:118:13:118:23 | { ... } | (Int32,Int32) |
+| patterns.cs:118:14:118:19 | Int32 x2 | Int32 |
+| patterns.cs:118:22:118:22 | 0 | Int32 |
+| patterns.cs:119:13:119:28 | ( ... ) | null |
+| patterns.cs:119:13:119:28 | { ... } | (Int32,Int32) |
+| patterns.cs:119:14:119:19 | Int32 x2 | Int32 |
+| patterns.cs:119:22:119:27 | Int32 y2 | Int32 |
+| patterns.cs:128:13:128:20 | access to type MyStruct | MyStruct |
+| patterns.cs:128:13:128:33 | { ... } | MyStruct |
+| patterns.cs:128:22:128:33 | { ... } | null |
+| patterns.cs:128:27:128:31 | Int32 x | Int32 |
+| patterns.cs:129:13:129:20 | access to type MyStruct | MyStruct |
+| patterns.cs:129:13:129:33 | MyStruct ms | MyStruct |
+| patterns.cs:129:13:129:33 | { ... } | MyStruct |
+| patterns.cs:129:22:129:30 | { ... } | null |
+| patterns.cs:129:27:129:28 | 10 | Int32 |
+| patterns.cs:130:13:130:18 | ( ... ) | null |
+| patterns.cs:130:13:130:18 | { ... } | MyStruct |
+| patterns.cs:130:14:130:14 | 1 | Int32 |
+| patterns.cs:130:17:130:17 | 2 | Int32 |
+| patterns.cs:131:13:131:22 | (..., ...) | (Int32,Object) |
+| patterns.cs:131:18:131:18 | Int32 x | Int32 |
+| patterns.cs:131:21:131:21 | _ | null |
+| patterns.cs:138:17:138:17 | 1 | Int32 |
+| patterns.cs:139:17:139:17 | 2 | Int32 |
+| patterns.cs:140:17:140:24 | Object y | Object |
+| patterns.cs:140:36:140:37 | { ... } | Object |
+| patterns.cs:140:36:140:37 | { ... } | null |
+| patterns.cs:141:17:141:22 | access to type String | String |
+| patterns.cs:142:17:142:24 | access to type MyStruct | MyStruct |
+| patterns.cs:142:17:142:36 | { ... } | MyStruct |
+| patterns.cs:142:26:142:34 | { ... } | null |
+| patterns.cs:142:31:142:32 | 10 | Int32 |
diff --git a/csharp/ql/test/library-tests/csharp8/patterns.ql b/csharp/ql/test/library-tests/csharp8/patterns.ql
index fc758eb92160..164eea21e09a 100644
--- a/csharp/ql/test/library-tests/csharp8/patterns.ql
+++ b/csharp/ql/test/library-tests/csharp8/patterns.ql
@@ -68,4 +68,8 @@ query predicate isRecursivePatternExprWithDecl(
query predicate labeledPatternExpr(LabeledPatternExpr e, string s) { s = e.getLabel() }
-query predicate tupleTypes(TupleExpr te, Type t) { te.getType() = t }
+query predicate tupleTypes(TupleExpr te, string type) { te.getType().toString() = type }
+
+query predicate patternExprTypes(PatternExpr pattern, string type) {
+ pattern.getType().toString() = type
+}
diff --git a/csharp/ql/test/library-tests/csharp9/patternExprTypes.expected b/csharp/ql/test/library-tests/csharp9/patternExprTypes.expected
new file mode 100644
index 000000000000..f85df3d0802d
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp9/patternExprTypes.expected
@@ -0,0 +1,61 @@
+| BinaryPattern.cs:8:14:8:16 | a | Char |
+| BinaryPattern.cs:8:14:8:23 | ... or ... | Char |
+| BinaryPattern.cs:8:21:8:23 | b | Char |
+| BinaryPattern.cs:10:14:10:21 | Object o | Object |
+| BinaryPattern.cs:10:14:10:51 | ... and ... | BinaryPattern |
+| BinaryPattern.cs:10:27:10:39 | access to type BinaryPattern | BinaryPattern |
+| BinaryPattern.cs:10:27:10:51 | BinaryPattern u | BinaryPattern |
+| BinaryPattern.cs:10:27:10:51 | { ... } | BinaryPattern |
+| BinaryPattern.cs:10:41:10:49 | { ... } | null |
+| BinaryPattern.cs:10:47:10:47 | 1 | Int32 |
+| BinaryPattern.cs:12:14:12:21 | Object o | Object |
+| BinaryPattern.cs:12:14:12:41 | ... and ... | BinaryPattern |
+| BinaryPattern.cs:12:27:12:41 | BinaryPattern u | BinaryPattern |
+| BinaryPattern.cs:18:13:18:13 | 1 | Int32 |
+| BinaryPattern.cs:18:13:18:18 | ... or ... | Int32 |
+| BinaryPattern.cs:18:18:18:18 | 2 | Int32 |
+| BinaryPattern.cs:19:13:19:13 | _ | Int32 |
+| ParenthesizedPattern.cs:9:18:9:19 | { ... } | null |
+| ParenthesizedPattern.cs:9:18:9:22 | Object p1 | Object |
+| ParenthesizedPattern.cs:9:18:9:22 | { ... } | Object |
+| ParenthesizedPattern.cs:13:19:13:20 | { ... } | null |
+| ParenthesizedPattern.cs:13:19:13:23 | Object p2 | Object |
+| ParenthesizedPattern.cs:13:19:13:23 | { ... } | Object |
+| ParenthesizedPattern.cs:22:13:22:13 | 1 | Int32 |
+| ParenthesizedPattern.cs:23:14:23:14 | 2 | Int32 |
+| ParenthesizedPattern.cs:24:13:24:15 | T t | T |
+| ParenthesizedPattern.cs:24:27:24:28 | { ... } | T |
+| ParenthesizedPattern.cs:24:27:24:28 | { ... } | null |
+| ParenthesizedPattern.cs:25:14:25:22 | Object o1 | Object |
+| ParenthesizedPattern.cs:25:37:25:38 | { ... } | Object |
+| ParenthesizedPattern.cs:25:37:25:38 | { ... } | null |
+| ParenthesizedPattern.cs:26:14:26:19 | access to type String | String |
+| RelationalPattern.cs:6:14:6:19 | >= ... | Char |
+| RelationalPattern.cs:8:14:8:18 | > ... | Char |
+| RelationalPattern.cs:10:14:10:19 | <= ... | Char |
+| RelationalPattern.cs:12:14:12:18 | < ... | Char |
+| RelationalPattern.cs:18:13:18:13 | 1 | Int32 |
+| RelationalPattern.cs:19:13:19:14 | > ... | Int32 |
+| RelationalPattern.cs:20:13:20:13 | _ | Int32 |
+| TargetType.cs:31:38:31:41 | null | null |
+| TypePattern.cs:8:18:8:30 | ( ... ) | null |
+| TypePattern.cs:8:18:8:30 | { ... } | (Object,Object) |
+| TypePattern.cs:8:19:8:21 | access to type Int32 | Int32 |
+| TypePattern.cs:8:24:8:29 | access to type String | String |
+| TypePattern.cs:11:13:11:15 | access to type Int32 | Int32 |
+| TypePattern.cs:12:13:12:20 | Double d | Double |
+| TypePattern.cs:13:13:13:25 | access to type String | String |
+| TypePattern.cs:14:13:14:27 | Object o | Object |
+| UnaryPattern.cs:8:14:8:20 | not ... | Char |
+| UnaryPattern.cs:8:18:8:20 | a | Char |
+| UnaryPattern.cs:10:14:10:21 | not ... | Object |
+| UnaryPattern.cs:10:18:10:21 | null | null |
+| UnaryPattern.cs:12:14:12:41 | not ... | Object |
+| UnaryPattern.cs:12:18:12:29 | access to type UnaryPattern | UnaryPattern |
+| UnaryPattern.cs:12:18:12:41 | UnaryPattern u | UnaryPattern |
+| UnaryPattern.cs:12:18:12:41 | { ... } | UnaryPattern |
+| UnaryPattern.cs:12:31:12:39 | { ... } | null |
+| UnaryPattern.cs:12:37:12:37 | 1 | Int32 |
+| UnaryPattern.cs:18:13:18:17 | not ... | Int32 |
+| UnaryPattern.cs:18:17:18:17 | 1 | Int32 |
+| UnaryPattern.cs:19:13:19:13 | _ | Int32 |
diff --git a/csharp/ql/test/library-tests/csharp9/patternExprTypes.ql b/csharp/ql/test/library-tests/csharp9/patternExprTypes.ql
new file mode 100644
index 000000000000..814019ecc818
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp9/patternExprTypes.ql
@@ -0,0 +1,4 @@
+import csharp
+
+from PatternExpr pattern
+select pattern, pattern.getType().toString()