From 21fd45be332b22b81d9953be98eead183eb8537c Mon Sep 17 00:00:00 2001 From: DoctorKrolic <70431552+DoctorKrolic@users.noreply.github.com> Date: Tue, 9 Jul 2024 00:36:52 +0300 Subject: [PATCH] Improve parser recovery around nullable types in patterns (#72805) --- .../CSharp/Portable/Parser/LanguageParser.cs | 79 +- .../Parser/LanguageParser_Patterns.cs | 51 +- .../Emit2/Semantics/PatternMatchingTests.cs | 18 +- .../Emit2/Semantics/PatternMatchingTests2.cs | 27 +- .../Emit2/Semantics/PatternMatchingTests4.cs | 35 +- .../PatternMatchingTests_NullableTypes.cs | 675 ++++++++++++++++++ .../Test/Semantic/Semantics/NullableTests.cs | 16 +- .../Test/Syntax/Parsing/AsyncParsingTests.cs | 194 ++++- .../Test/Syntax/Parsing/AwaitParsingTests.cs | 359 ++++++++++ .../Parsing/DeclarationExpressionTests.cs | 61 +- .../Syntax/Parsing/NullableParsingTests.cs | 582 +++++++++++++++ .../Syntax/Parsing/PatternParsingTests2.cs | 384 ++++++++++ .../PatternParsingTests_ListPatterns.cs | 507 ++++++++++++- .../Syntax/Parsing/StatementParsingTests.cs | 323 +++++++++ .../Parsing/SwitchExpressionParsingTests.cs | 654 +++++++++++++++++ 15 files changed, 3851 insertions(+), 114 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 31c2e242c1c8e..887dfabee8fb7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7223,18 +7223,79 @@ bool canFollowNullableType() case ParseTypeMode.AfterIs: case ParseTypeMode.DefinitePattern: case ParseTypeMode.AsExpression: - // These contexts might be a type that is at the end of an expression. In these contexts we only - // permit the nullable qualifier if it is followed by a token that could not start an - // expression, because for backward compatibility we want to consider a `?` token as part of the - // `?:` operator if possible. + // We are currently after `?` token after a nullable type pattern and need to decide how to + // parse what we see next. In the case of an identifier (e.g. `x ? a` there are two ways we can + // see things // - // Similarly, if we have `T?[]` we do want to treat that as an array of nullables (following - // existing parsing), not a conditional that returns a list. + // 1. As a start of conditional expression, e.g. `var a = obj is string ? a : b` + // 2. As a designation of a nullable-typed pattern, e.g. `if (obj is string? str)` + // + // Since nullable types (no matter reference or value types) are not valid in patterns by + // default we are biased towards the first option and consider case 2 only for error recovery + // purposes (if we parse here as nullable type pattern an error will be reported during + // binding). This condition checks for simple cases, where we better use option 2 and parse a + // nullable-typed pattern + if (IsTrueIdentifier(this.CurrentToken)) + { + // In a non-async method, `await` is a simple identifier. However, if we see `x ? await` + // it's almost certainly the start of an `await expression` in a conditional expression + // (e.g. `x is Y ? await ...`), not a nullable type pattern (since users would not use + // 'await' as the name of a variable). So just treat this as a conditional expression. + // Similarly, `async` can start a simple lambda in a conditional expression + // (e.g. `x is Y ? async a => ...`). The correct behavior is to treat `async` as a keyword + if (this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.AwaitKeyword) + return false; + + var nextTokenKind = PeekToken(1).Kind; + + // These token either 100% end a pattern or start a new one: + + // A literal token starts a new pattern. Can occur in list pattern with missing separation + // `,`. For example, in `x is [int[]? arr 5]` we'd prefer to parse this as a missing `,` + // after `arr` + if (SyntaxFacts.IsLiteral(nextTokenKind)) + return true; + + // A predefined type is basically the same case: `x is [string[]? slice char ch]`. We'd + // prefer to parse this as a missing `,` after `slice`. + if (SyntaxFacts.IsPredefinedType(nextTokenKind)) + return true; + + // `)`, `]` and `}` obviously end a pattern. For example: + // `if (x is int? i)`, `indexable[x is string? s]`, `x is { Prop: Type? typeVar }` + if (nextTokenKind is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken or SyntaxKind.CloseBraceToken) + return true; + + // `{` starts a new pattern. For example: `x is A? { ...`. Note, that `[` and `(` are not + // in the list because they can start an invocation/indexer + if (nextTokenKind == SyntaxKind.OpenBraceToken) + return true; + + // `,` ends a pattern in list/property pattern. For example `x is { Prop1: Type1? type, Prop2: Type2 }` or + // `x is [Type1? type, ...]` + if (nextTokenKind == SyntaxKind.CommaToken) + return true; + + // `;` ends a pattern if it finishes an expression statement: var y = x is bool? b; + if (nextTokenKind == SyntaxKind.SemicolonToken) + return true; + + // EndOfFileToken is obviously the end of parsing. We are better parsing a pattern rather + // than an unfinished conditional expression + if (nextTokenKind == SyntaxKind.EndOfFileToken) + return true; + + return false; + } + + // If nothing from above worked permit the nullable qualifier if it is followed by a token that + // could not start an expression. If we have `T?[]` we do want to treat that as an array of + // nullables (following existing parsing), not a conditional that returns a list. return !CanStartExpression() || this.CurrentToken.Kind is SyntaxKind.OpenBracketToken; case ParseTypeMode.NewExpression: - // A nullable qualifier is permitted as part of the type in a `new` expression. - // e.g. `new int?()` is allowed. It creates a null value of type `Nullable`. - // Similarly `new int? {}` is allowed. + // A nullable qualifier is permitted as part of the type in a `new` expression. e.g. `new + // int?()` is allowed. It creates a null value of type `Nullable`. Similarly `new int? {}` + // is allowed. return this.CurrentToken.Kind is SyntaxKind.OpenParenToken or // ctor parameters SyntaxKind.OpenBracketToken or // array type diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index e9b3e9a56cb83..ad961242a9386 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -50,21 +50,21 @@ when ConvertExpressionToType(expr, out var leftType): }; } - private PatternSyntax ParsePattern(Precedence precedence, bool afterIs = false, bool whenIsKeyword = false) + private PatternSyntax ParsePattern(Precedence precedence, bool afterIs = false, bool inSwitchArmPattern = false) { - return ParseDisjunctivePattern(precedence, afterIs, whenIsKeyword); + return ParseDisjunctivePattern(precedence, afterIs, inSwitchArmPattern); } - private PatternSyntax ParseDisjunctivePattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseDisjunctivePattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { - PatternSyntax result = ParseConjunctivePattern(precedence, afterIs, whenIsKeyword); + PatternSyntax result = ParseConjunctivePattern(precedence, afterIs, inSwitchArmPattern); while (this.CurrentToken.ContextualKind == SyntaxKind.OrKeyword) { result = _syntaxFactory.BinaryPattern( SyntaxKind.OrPattern, result, ConvertToKeyword(this.EatToken()), - ParseConjunctivePattern(precedence, afterIs, whenIsKeyword)); + ParseConjunctivePattern(precedence, afterIs, inSwitchArmPattern)); } return result; @@ -101,16 +101,16 @@ private bool LooksLikeTypeOfPattern() return false; } - private PatternSyntax ParseConjunctivePattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseConjunctivePattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { - PatternSyntax result = ParseNegatedPattern(precedence, afterIs, whenIsKeyword); + PatternSyntax result = ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern); while (this.CurrentToken.ContextualKind == SyntaxKind.AndKeyword) { result = _syntaxFactory.BinaryPattern( SyntaxKind.AndPattern, result, ConvertToKeyword(this.EatToken()), - ParseNegatedPattern(precedence, afterIs, whenIsKeyword)); + ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern)); } return result; @@ -155,21 +155,21 @@ private bool ScanDesignation(bool permitTuple) } } - private PatternSyntax ParseNegatedPattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseNegatedPattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { if (this.CurrentToken.ContextualKind == SyntaxKind.NotKeyword) { return _syntaxFactory.UnaryPattern( ConvertToKeyword(this.EatToken()), - ParseNegatedPattern(precedence, afterIs, whenIsKeyword)); + ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern)); } else { - return ParsePrimaryPattern(precedence, afterIs, whenIsKeyword); + return ParsePrimaryPattern(precedence, afterIs, inSwitchArmPattern); } } - private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { // handle common error recovery situations during typing var tk = this.CurrentToken.Kind; @@ -192,10 +192,10 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b switch (CurrentToken.Kind) { case SyntaxKind.OpenBracketToken: - return this.ParseListPattern(whenIsKeyword); + return this.ParseListPattern(inSwitchArmPattern); case SyntaxKind.DotDotToken: return _syntaxFactory.SlicePattern(EatToken(), - IsPossibleSubpatternElement() ? ParsePattern(precedence, afterIs: false, whenIsKeyword) : null); + IsPossibleSubpatternElement() ? ParsePattern(precedence, afterIs: false, inSwitchArmPattern) : null); case SyntaxKind.LessThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanToken: @@ -214,7 +214,8 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b TypeSyntax? type = null; if (LooksLikeTypeOfPattern()) { - type = this.ParseType(afterIs ? ParseTypeMode.AfterIs : ParseTypeMode.DefinitePattern); + type = this.ParseType( + afterIs ? ParseTypeMode.AfterIs : ParseTypeMode.DefinitePattern); if (type.IsMissing || !CanTokenFollowTypeInPattern(precedence)) { // either it is not shaped like a type, or it is a constant expression. @@ -223,7 +224,7 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b } } - var pattern = ParsePatternContinued(type, precedence, whenIsKeyword); + var pattern = ParsePatternContinued(type, precedence, inSwitchArmPattern); if (pattern != null) return pattern; @@ -262,14 +263,14 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) } } - private PatternSyntax? ParsePatternContinued(TypeSyntax? type, Precedence precedence, bool whenIsKeyword) + private PatternSyntax? ParsePatternContinued(TypeSyntax? type, Precedence precedence, bool inSwitchArmPattern) { if (type?.Kind == SyntaxKind.IdentifierName) { var typeIdentifier = (IdentifierNameSyntax)type; var typeIdentifierToken = typeIdentifier.Identifier; if (typeIdentifierToken.ContextualKind == SyntaxKind.VarKeyword && - (this.CurrentToken.Kind == SyntaxKind.OpenParenToken || this.IsValidPatternDesignation(whenIsKeyword))) + (this.CurrentToken.Kind == SyntaxKind.OpenParenToken || this.IsValidPatternDesignation(inSwitchArmPattern))) { // we have a "var" pattern; "var" is not permitted to be a stand-in for a type (or a constant) in a pattern. var varToken = ConvertToKeyword(typeIdentifierToken); @@ -295,7 +296,7 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) var closeParenToken = this.EatToken(SyntaxKind.CloseParenToken); parsePropertyPatternClause(out PropertyPatternClauseSyntax? propertyPatternClause0); - var designation0 = TryParseSimpleDesignation(whenIsKeyword); + var designation0 = TryParseSimpleDesignation(inSwitchArmPattern); if (type == null && propertyPatternClause0 == null && @@ -333,12 +334,12 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) { return _syntaxFactory.RecursivePattern( type, positionalPatternClause: null, propertyPatternClause, - TryParseSimpleDesignation(whenIsKeyword)); + TryParseSimpleDesignation(inSwitchArmPattern)); } if (type != null) { - var designation = TryParseSimpleDesignation(whenIsKeyword); + var designation = TryParseSimpleDesignation(inSwitchArmPattern); if (designation != null) return _syntaxFactory.DeclarationPattern(type, designation); @@ -431,7 +432,7 @@ private CSharpSyntaxNode ParseExpressionOrPatternForSwitchStatement() { var savedState = _termState; _termState |= TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement; - var pattern = ParsePattern(Precedence.Conditional, whenIsKeyword: true); + var pattern = ParsePattern(Precedence.Conditional, inSwitchArmPattern: true); _termState = savedState; return ConvertPatternToExpressionIfPossible(pattern); } @@ -589,7 +590,7 @@ SeparatedSyntaxList parseSwitchExpressionArms() var savedState = _termState; _termState |= TerminatorState.IsPatternInSwitchExpressionArm; - var pattern = ParsePattern(Precedence.Coalescing, whenIsKeyword: true); + var pattern = ParsePattern(Precedence.Coalescing, inSwitchArmPattern: true); _termState = savedState; // We use a precedence that excludes lambdas, assignments, and a conditional which could have a @@ -628,7 +629,7 @@ SeparatedSyntaxList parseSwitchExpressionArms() } } - private ListPatternSyntax ParseListPattern(bool whenIsKeyword) + private ListPatternSyntax ParseListPattern(bool inSwitchArmPattern) { var openBracket = this.EatToken(SyntaxKind.OpenBracketToken); var list = this.ParseCommaSeparatedSyntaxList( @@ -645,7 +646,7 @@ private ListPatternSyntax ParseListPattern(bool whenIsKeyword) openBracket, list, this.EatToken(SyntaxKind.CloseBracketToken), - TryParseSimpleDesignation(whenIsKeyword)); + TryParseSimpleDesignation(inSwitchArmPattern)); } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs index ac537827d6664..2e6c81c785b24 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs @@ -4773,26 +4773,28 @@ public static void Main(object o) private static object M() => null; }"; var compilation = CreateCompilation(program).VerifyDiagnostics( - // (9,32): error CS1525: Invalid expression term 'break' - // case Color? Color2: - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(9, 32), - // (9,32): error CS1003: Syntax error, ':' expected - // case Color? Color2: - Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(9, 32), // (8,18): error CS0118: 'Color' is a variable but is used like a type // case Color Color: Diagnostic(ErrorCode.ERR_BadSKknown, "Color").WithArguments("Color", "variable", "type").WithLocation(8, 18), // (9,25): error CS0103: The name 'Color2' does not exist in the current context // case Color? Color2: - Diagnostic(ErrorCode.ERR_NameNotInContext, "Color2").WithArguments("Color2").WithLocation(9, 25) - ); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Color2").WithArguments("Color2").WithLocation(9, 25), + // (9,32): error CS1525: Invalid expression term 'break' + // case Color? Color2: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(9, 32), + // (9,32): error CS1003: Syntax error, ':' expected + // case Color? Color2: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(9, 32)); + var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var colorDecl = GetPatternDeclarations(tree, "Color").ToArray(); var colorRef = GetReferences(tree, "Color").ToArray(); + Assert.Equal(1, colorDecl.Length); Assert.Equal(2, colorRef.Length); + Assert.Null(model.GetSymbolInfo(colorRef[0]).Symbol); VerifyModelForDeclarationOrVarSimplePattern(model, colorDecl[0], colorRef[1]); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs index 8b28cfd8f6468..3cf79e3499614 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs @@ -4,11 +4,7 @@ #nullable disable -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -2680,7 +2676,7 @@ public void M(string s) ); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void IsNullableReferenceType_01() { var source = @@ -2712,25 +2708,12 @@ public void M5(object o) { // (10,22): error CS8650: It is not legal to use nullable reference type 'string?' in an is-type expression; use the underlying type 'string' instead. // var t = o is string?; Diagnostic(ErrorCode.ERR_IsNullableType, "string?").WithArguments("string").WithLocation(10, 22), - // (13,30): error CS0103: The name '_' does not exist in the current context - // var t = o is string? _; - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(13, 30), - // (13,31): error CS1003: Syntax error, ':' expected - // var t = o is string? _; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":").WithLocation(13, 31), - // (13,31): error CS1525: Invalid expression term ';' + // (13,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. // var t = o is string? _; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(13, 31), - // (16,22): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'object', with 2 out parameters and a void return type. + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(13, 22), + // (16,23): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(string? _)").WithArguments("object", "2").WithLocation(16, 22), - // (16,29): error CS1003: Syntax error, ',' expected - // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(",").WithLocation(16, 29), - // (16,31): error CS1003: Syntax error, ',' expected - // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_SyntaxError, "_").WithArguments(",").WithLocation(16, 31) - ); + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(16, 23)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs index 9f8814f4ecf8b..4665233220ada 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs @@ -3759,11 +3759,11 @@ public class A static void M(object o, bool c) { if (o is A?) { } // error 1 (can't test for is nullable reference type) - if (o is A? b1) { } // error 2 (missing :) + if (o is A? b1) { } // error 2 (can't test for is nullable reference type) if (o is A? b2 && c) { } // error 3 (missing :) - if (o is A[]? b5) { } // error 4 (missing :) + if (o is A[]? b5) { } // error 4 (can't test for is nullable reference type) if (o is A[]? b6 && c) { } // error 5 (missing :) - if (o is A[][]? b7) { } // error 6 (missing :) + if (o is A[][]? b7) { } // error 6 (can't test for is nullable reference type) if (o is A[][]? b8 && c) { } // error 7 (missing :) if (o is A? && c) { } // error 8 (can't test for is nullable reference type) _ = o is A[][]?; // error 9 (can't test for is nullable reference type) @@ -3776,36 +3776,27 @@ static void M(object o, bool c) // (7,18): error CS8650: It is not legal to use nullable reference type 'A?' in an is-type expression; use the underlying type 'A' instead. // if (o is A?) { } // error 1 (can't test for is nullable reference type) Diagnostic(ErrorCode.ERR_IsNullableType, "A?").WithArguments("A").WithLocation(7, 18), - // (8,23): error CS1003: Syntax error, ':' expected - // if (o is A? b1) { } // error 2 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(8, 23), - // (8,23): error CS1525: Invalid expression term ')' - // if (o is A? b1) { } // error 2 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(8, 23), - // (9,28): error CS1003: Syntax error, ':' expected + // 0.cs(8,18): error CS8116: It is not legal to use nullable reference type 'A?' in an is-type expression; use the underlying type 'A' instead. + // if (o is A? b1) { } // error 2 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A?").WithArguments("A").WithLocation(8, 18), + // 0.cs(9,28): error CS1003: Syntax error, ':' expected // if (o is A? b2 && c) { } // error 3 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(9, 28), // (9,28): error CS1525: Invalid expression term ')' // if (o is A? b2 && c) { } // error 3 (missing :) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(9, 28), - // (10,25): error CS1003: Syntax error, ':' expected - // if (o is A[]? b5) { } // error 4 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(10, 25), - // (10,25): error CS1525: Invalid expression term ')' - // if (o is A[]? b5) { } // error 4 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(10, 25), + // (10,18): error CS8116: It is not legal to use nullable reference type 'A[]?' in an is-type expression; use the underlying type 'A[]' instead. + // if (o is A[]? b5) { } // error 4 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A[]?").WithArguments("A[]").WithLocation(10, 18), // (11,30): error CS1003: Syntax error, ':' expected // if (o is A[]? b6 && c) { } // error 5 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(11, 30), // (11,30): error CS1525: Invalid expression term ')' // if (o is A[]? b6 && c) { } // error 5 (missing :) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(11, 30), - // (12,27): error CS1003: Syntax error, ':' expected - // if (o is A[][]? b7) { } // error 6 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(12, 27), - // (12,27): error CS1525: Invalid expression term ')' - // if (o is A[][]? b7) { } // error 6 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(12, 27), + // (12,18): error CS8116: It is not legal to use nullable reference type 'A[][]?' in an is-type expression; use the underlying type 'A[][]' instead. + // if (o is A[][]? b7) { } // error 6 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A[][]?").WithArguments("A[][]").WithLocation(12, 18), // (13,32): error CS1003: Syntax error, ':' expected // if (o is A[][]? b8 && c) { } // error 7 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(13, 32), diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs new file mode 100644 index 0000000000000..ecfa3b7758616 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs @@ -0,0 +1,675 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public class PatternMatchingTests_NullableTypes : PatternMatchingTestBase +{ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternCombinations() + { + var source = """ + #nullable enable + + class C + { + void M(object obj) + { + if (obj is int? or string?) { } + if (obj is int? i1 or string? s) { } + if (obj is int? and { }) { } + if (obj is int? i2 and { }) { } + if (obj is int? i3 and (1, 2)) { } + if (obj is int? i4 and []) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? or string?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(7, 20), + // (7,28): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (obj is int? or string?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(7, 28), + // (8,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(8, 20), + // (8,25): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i1").WithLocation(8, 25), + // (8,31): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(8, 31), + // (8,39): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s").WithLocation(8, 39), + // (9,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(9, 20), + // (10,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i2 and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(10, 20), + // (11,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(11, 20), + // (11,32): error CS1061: 'int' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "(1, 2)").WithArguments("int", "Deconstruct").WithLocation(11, 32), + // (11,32): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("int", "2").WithLocation(11, 32), + // (12,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 20), + // (12,32): error CS8985: List patterns may not be used for a value of type 'int'. No suitable 'Length' or 'Count' property was found. + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("int").WithLocation(12, 32), + // (12,32): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(12, 32), + // (12,32): error CS0021: Cannot apply indexing with [] to an expression of type 'int' + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("int").WithLocation(12, 32)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternCombinations_Arrays() + { + var source = """ + #nullable enable + + class C + { + void M(object obj) + { + if (obj is int[]? or string[]?) { } + if (obj is int[]? i1 or string[]? s) { } + if (obj is int[]? and { }) { } + if (obj is int[]? i2 and { }) { } + if (obj is int[]? i3 and (1, 2)) { } + if (obj is int[]? i4 and []) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? or string[]?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(7, 20), + // (7,30): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (obj is int[]? or string[]?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(7, 30), + // (8,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(8, 20), + // (8,27): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i1").WithLocation(8, 27), + // (8,33): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(8, 33), + // (8,43): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s").WithLocation(8, 43), + // (9,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(9, 20), + // (10,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i2 and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(10, 20), + // (11,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(11, 20), + // (11,34): error CS1061: 'int[]' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "(1, 2)").WithArguments("int[]", "Deconstruct").WithLocation(11, 34), + // (11,34): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int[]', with 2 out parameters and a void return type. + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("int[]", "2").WithLocation(11, 34), + // (12,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i4 and []) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 20), + // (12,34): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (obj is int[]? i4 and []) { } + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(12, 34)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchStatement() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + switch (obj) + { + case int?: + break; + case Task?: + break; + case int? i1: + break; + case Task? t1: + break; + case int? when x > 0: + break; + case Task? when x > 0: + break; + case int? i2 when x > 0: + break; + case Task? t2 when x > 0: + break; + case (int? when) when x > 0: + break; + case (Task? when) when x > 0: + break; + } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 18), + // (14,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(14, 18), + // (16,21): error CS1003: Syntax error, ':' expected + // case int? i1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(16, 21), + // (16,21): error CS1513: } expected + // case int? i1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(16, 21), + // (16,23): warning CS0164: This label has not been referenced + // case int? i1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "i1").WithLocation(16, 23), + // (18,18): error CS0119: 'Task' is a type, which is not valid in the given context + // case Task? t1: + Diagnostic(ErrorCode.ERR_BadSKunknown, "Task").WithArguments("System.Threading.Tasks.Task", "type").WithLocation(18, 18), + // (18,24): error CS0103: The name 't1' does not exist in the current context + // case Task? t1: + Diagnostic(ErrorCode.ERR_NameNotInContext, "t1").WithArguments("t1").WithLocation(18, 24), + // (18,27): error CS1525: Invalid expression term 'break' + // case Task? t1: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(18, 27), + // (18,27): error CS1003: Syntax error, ':' expected + // case Task? t1: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(18, 27), + // (20,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(20, 18), + // (22,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(22, 18), + // (24,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int? i2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(24, 18), + // (26,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task? t2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(26, 18), + // (28,19): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case (int? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(28, 19), + // (30,19): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case (Task? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(30, 19)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchStatement_Arrays() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + switch (obj) + { + case int[]?: + break; + case Task[]?: + break; + case int[]? i1: + break; + case Task[]? t1: + break; + case int[]? when x > 0: + break; + case Task[]? when x > 0: + break; + case int[]? i2 when x > 0: + break; + case Task[]? t2 when x > 0: + break; + case (int[]? when) when x > 0: + break; + case (Task[]? when) when x > 0: + break; + } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 18), + // (14,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(14, 18), + // (16,23): error CS1003: Syntax error, ':' expected + // case int[]? i1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(16, 23), + // (16,23): error CS1513: } expected + // case int[]? i1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(16, 23), + // (16,25): warning CS0164: This label has not been referenced + // case int[]? i1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "i1").WithLocation(16, 25), + // (18,24): error CS1003: Syntax error, ':' expected + // case Task[]? t1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(18, 24), + // (18,24): error CS1513: } expected + // case Task[]? t1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(18, 24), + // (18,26): warning CS0164: This label has not been referenced + // case Task[]? t1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "t1").WithLocation(18, 26), + // (20,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(20, 18), + // (22,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(22, 18), + // (24,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]? i2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(24, 18), + // (26,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]? t2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(26, 18), + // (28,19): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case (int[]? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(28, 19), + // (30,19): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case (Task[]? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(30, 19)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchExpression() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + _ = obj switch + { + int? => 1, + Task? => 2, + int? i1 => 3, + Task? t1 => 4, + int? when x > 0 => 5, + Task? when x > 0 => 6, + int? i2 when x > 0 => 7, + Task? t2 when x > 0 => 8, + (int? when) when x > 0 => 9, + (Task? when) when x > 0 => 10 + }; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? => 1, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 13), + // (13,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? => 2, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(13, 13), + // (14,16): error CS1003: Syntax error, '=>' expected + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(14, 16), + // (14,16): error CS1525: Invalid expression term '?' + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(14, 16), + // (14,25): error CS1003: Syntax error, ':' expected + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(14, 25), + // (14,25): error CS1525: Invalid expression term ',' + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(14, 25), + // (15,17): error CS1003: Syntax error, '=>' expected + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(15, 17), + // (15,17): error CS1525: Invalid expression term '?' + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(15, 17), + // (15,26): error CS1003: Syntax error, ':' expected + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(15, 26), + // (15,26): error CS1525: Invalid expression term ',' + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(15, 26), + // (16,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? when x > 0 => 5, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(16, 13), + // (17,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? when x > 0 => 6, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(17, 13), + // (18,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? i2 when x > 0 => 7, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(18, 13), + // (19,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? t2 when x > 0 => 8, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(19, 13), + // (20,14): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // (int? when) when x > 0 => 9, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(20, 14), + // (21,14): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // (Task? when) when x > 0 => 10 + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(21, 14)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchExpression_Arrays() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + _ = obj switch + { + int[]? => 1, + Task[]? => 2, + int[]? i1 => 3, + Task[]? t1 => 4, + int[]? when x > 0 => 5, + Task[]? when x > 0 => 6, + int[]? i2 when x > 0 => 7, + Task[]? t2 when x > 0 => 8, + (int[]? when) when x > 0 => 9, + (Task[]? when) when x > 0 => 10 + }; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? => 1, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 13), + // (13,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? => 2, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(13, 13), + // (14,18): error CS1003: Syntax error, '=>' expected + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(14, 18), + // (14,18): error CS1525: Invalid expression term '?' + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(14, 18), + // (14,27): error CS1003: Syntax error, ':' expected + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(14, 27), + // (14,27): error CS1525: Invalid expression term ',' + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(14, 27), + // (15,19): error CS1003: Syntax error, '=>' expected + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(15, 19), + // (15,19): error CS1525: Invalid expression term '?' + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(15, 19), + // (15,28): error CS1003: Syntax error, ':' expected + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(15, 28), + // (15,28): error CS1525: Invalid expression term ',' + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(15, 28), + // (16,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? when x > 0 => 5, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(16, 13), + // (17,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? when x > 0 => 6, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(17, 13), + // (18,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? i2 when x > 0 => 7, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(18, 13), + // (19,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? t2 when x > 0 => 8, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(19, 13), + // (20,14): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // (int[]? when) when x > 0 => 9, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(20, 14), + // (21,14): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // (Task[]? when) when x > 0 => 10 + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(21, 14)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PropertySubpatterns() + { + var source = """ + #nullable enable + + class E + { + public object? Prop { get; set; } + public object? Prop2 { get; set; } + } + + class C + { + void M(E e) + { + if (e is { Prop: int? }) { } + if (e is { Prop: int? i1 }) { } + if (e is { Prop: int? or string? }) { } + if (e is { Prop: int? i2 or string? s1 }) { } + if (e is { Prop: int?, Prop2: string? }) { } + if (e is { Prop: int? i3, Prop2: string? s2 }) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (13,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(13, 26), + // (14,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(14, 26), + // (15,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? or string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(15, 26), + // (15,34): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? or string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(15, 34), + // (16,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(16, 26), + // (16,31): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i2").WithLocation(16, 31), + // (16,37): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(16, 37), + // (16,45): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s1").WithLocation(16, 45), + // (17,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int?, Prop2: string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(17, 26), + // (17,39): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int?, Prop2: string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(17, 39), + // (18,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i3, Prop2: string? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(18, 26), + // (18,42): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? i3, Prop2: string? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(18, 42)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PropertySubpatterns_Arrays() + { + var source = """ + #nullable enable + + class E + { + public object? Prop { get; set; } + public object? Prop2 { get; set; } + } + + class C + { + void M(E e) + { + if (e is { Prop: int[]? }) { } + if (e is { Prop: int[]? i1 }) { } + if (e is { Prop: int[]? or string[]? }) { } + if (e is { Prop: int[]? i2 or string[]? s1 }) { } + if (e is { Prop: int[]?, Prop2: string[]? }) { } + if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (13,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(13, 26), + // (14,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(14, 26), + // (15,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? or string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(15, 26), + // (15,36): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? or string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(15, 36), + // (16,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(16, 26), + // (16,33): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i2").WithLocation(16, 33), + // (16,39): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(16, 39), + // (16,49): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s1").WithLocation(16, 49), + // (17,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]?, Prop2: string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(17, 26), + // (17,41): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]?, Prop2: string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(17, 41), + // (18,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(18, 26), + // (18,44): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(18, 44)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ListPatterns() + { + var source = """ + #nullable enable + + class C + { + void M2(string s) + { + if (s is [.. string?]) { } + if (s is [.. string? slice1]) { } + if (s is [.. string? slice2, ')']) { } + } + } + """; + + var comp = CreateCompilationWithIndexAndRange(source); + comp.VerifyDiagnostics( + // (7,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string?]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(7, 22), + // (8,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string? slice1]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(8, 22), + // (9,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string? slice2, ')']) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(9, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ListPatterns_Arrays() + { + var source = """ + #nullable enable + + class C + { + void M(string[] s) + { + if (s is [.. string[]?]) { } + if (s is [.. string[]? slice1]) { } + if (s is [.. string[]? slice2, ")"]) { } + } + } + """; + + var comp = CreateCompilationWithIndexAndRange([source, TestSources.GetSubArray]); + comp.VerifyDiagnostics( + // (7,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]?]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(7, 22), + // (8,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]? slice1]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(8, 22), + // (9,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]? slice2, ')']) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(9, 22)); + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs index 73ba7b40eccba..602da008a3af4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs @@ -6,11 +6,7 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Text; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -2133,7 +2129,7 @@ struct S ); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void TestIsNullable1() { var source = @" @@ -2149,15 +2145,9 @@ void M(object o) "; CreateCompilation(source).VerifyDiagnostics( - // (6,23): error CS0103: The name 'i' does not exist in the current context - // if (o is int? i) - Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 23), - // (6,24): error CS1003: Syntax error, ':' expected - // if (o is int? i) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(6, 24), - // (6,24): error CS1525: Invalid expression term ')' + // (6,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. // if (o is int? i) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 24)); + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(6, 18)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs index 91a5bf720bf4b..90944462784b9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs @@ -4,9 +4,9 @@ #nullable disable +using System; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; -using System; using Xunit; using Xunit.Abstractions; @@ -2658,5 +2658,197 @@ public void AsyncAsType_Property_ExplicitInterface() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern1() + { + UsingExpression("x is A ? async b => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern2() + { + UsingExpression("x is A a ? async b => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern3() + { + UsingExpression("x is A ? async (b) => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern4() + { + UsingExpression("x is A a ? async (b) => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs index 56c40064345ad..3479451b547ab 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs @@ -4,6 +4,7 @@ #nullable disable +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -1147,6 +1148,364 @@ public void AwaitUsingTest() } } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern1() + { + UsingExpression("x is int ? await y : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern2() + { + UsingExpression("x is int ? await this.SomeMethodAsync() : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.ThisExpression); + { + N(SyntaxKind.ThisKeyword); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "SomeMethodAsync"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern3() + { + UsingExpression("x is int ? await base.SomeMethodAsync() : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.BaseExpression); + { + N(SyntaxKind.BaseKeyword); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "SomeMethodAsync"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern4() + { + UsingExpression("x is int ? await (myTask) : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "myTask"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional1() + { + UsingExpression("f(x is int? await)", + // (1,18): error CS1003: Syntax error, ':' expected + // f(x is int? await) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(1, 18), + // (1,18): error CS1525: Invalid expression term ')' + // f(x is int? await) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 18)); + + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "f"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional2() + { + UsingExpression("dict[x is int? await]", + // (1,21): error CS1003: Syntax error, ':' expected + // dict[x is int? await] + Diagnostic(ErrorCode.ERR_SyntaxError, "]").WithArguments(":").WithLocation(1, 21), + // (1,21): error CS1525: Invalid expression term ']' + // dict[x is int? await] + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "]").WithArguments("]").WithLocation(1, 21)); + + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dict"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional3() + { + UsingExpression("x is { Prop: int? await }", + // (1,17): error CS1003: Syntax error, ',' expected + // x is { Prop: int? await } + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(",").WithLocation(1, 17), + // (1,19): error CS1003: Syntax error, ',' expected + // x is { Prop: int? await } + Diagnostic(ErrorCode.ERR_SyntaxError, "await").WithArguments(",").WithLocation(1, 19)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + #region AwaitExpressionInSyncContext [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs index a8dc74a200040..cce90916c45e4 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs @@ -5,6 +5,7 @@ #nullable disable using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -134,17 +135,53 @@ public void NullableTypeTest_02() EOF(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void NullableTypeTest_03() { - UsingStatement("if (e is int? x) {}", - // (1,16): error CS1003: Syntax error, ':' expected - // if (e is int? x) {} - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(1, 16), - // (1,16): error CS1525: Invalid expression term ')' - // if (e is int? x) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 16) - ); + UsingStatement("if (e is int? x) {}"); + + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void NullableTypeTest_03_2() + { + UsingStatement("if (e is int ? x : y) {}"); + N(SyntaxKind.IfStatement); { N(SyntaxKind.IfKeyword); @@ -168,10 +205,10 @@ public void NullableTypeTest_03() { N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.ColonToken); - M(SyntaxKind.IdentifierName); + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "y"); } } N(SyntaxKind.CloseParenToken); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs index aaacbfe16dcc9..be7321a78f231 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs @@ -688,6 +688,7 @@ public void DeclarationPattern_NullableType() // (1,25): error CS1003: Syntax error, ':' expected // switch (e) { case T? t: break; } Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":").WithLocation(1, 25)); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -836,6 +837,7 @@ public void DeclarationPattern_NullableArray() // (1,22): error CS1513: } expected // switch (e) { case T[]? t: break; } Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(1, 22)); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -1774,5 +1776,585 @@ public void CreateNullableArray_07() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void IsExpressionOfNullableTypeInStatement() + { + UsingStatement("_ = x is Type?;"); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DeclarationPatternOfNullableTypeInStatement() + { + UsingStatement("_ = x is Type? t;"); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DisjunctivePattern_NullableType1() + { + UsingExpression("x is int? or string?"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DisjunctivePattern_NullableType2() + { + UsingExpression("x is int? i or string? s"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "s"); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType1() + { + UsingExpression("x is Type? and { }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType2() + { + UsingExpression("x is Type? t and { }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType3() + { + UsingExpression("x is Type? and (1, 2)", + // (1,22): error CS1003: Syntax error, ':' expected + // x is Type? and (1, 2) + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(1, 22), + // (1,22): error CS1733: Expected expression + // x is Type? and (1, 2) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 22)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "and"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType3_2() + { + UsingExpression("x is Type ? f(1, 2) : 0"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "f"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType4() + { + UsingExpression("x is Type? t and (1, 2)"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PositionalPatternClause); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType5() + { + UsingExpression("x is Type? and []", + // (1,17): error CS0443: Syntax error; value expected + // x is Type? and [] + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 17), + // (1,18): error CS1003: Syntax error, ':' expected + // x is Type? and [] + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(1, 18), + // (1,18): error CS1733: Expected expression + // x is Type? and [] + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 18)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "and"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.Argument); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType5_2() + { + UsingExpression("x is Type ? dict[key] : default"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dict"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "key"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType6() + { + UsingExpression("x is Type? t and []"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs index ddc8174b9dbe3..df04aa31a3fa9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -1361,5 +1362,388 @@ public void MissingClosingAngleBracket07() } #endregion + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType1() + { + UsingExpression("e is { Prop: Type? }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType2() + { + UsingExpression("e is { Prop: Type? propVal }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType3() + { + UsingExpression("e is { Prop: Type? propVal, Prop2: int? val2 }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop2"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "val2"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType4() + { + UsingExpression("e is { Prop: Type? propVal Prop2: int? val2 }", + // (1,28): error CS1003: Syntax error, ',' expected + // e is { Prop: Type? propVal Prop2: int? val2 } + Diagnostic(ErrorCode.ERR_SyntaxError, "Prop2").WithArguments(",").WithLocation(1, 28)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop2"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "val2"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType5() + { + UsingExpression("e is { Prop: Type? or AnotherType? }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "AnotherType"); + } + N(SyntaxKind.QuestionToken); + } + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType6() + { + UsingExpression("e is { Prop: Type? t or AnotherType? a }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "AnotherType"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs index 0bcd4870ba5af..422e1f3cb456f 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -767,7 +768,7 @@ public void SlicePattern_11() { UsingExpression(@"c is [var x ..]", // (1,13): error CS1003: Syntax error, ',' expected - // c is {var x ..} + // c is [var x ..] Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",").WithLocation(1, 13)); N(SyntaxKind.IsPatternExpression); @@ -835,7 +836,7 @@ public void SlicePattern_13() { UsingExpression(@"c is [[]..]", // (1,9): error CS1003: Syntax error, ',' expected - // c is {{}..} + // c is [[]..] Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",").WithLocation(1, 9)); N(SyntaxKind.IsPatternExpression); @@ -1098,6 +1099,508 @@ public void SlicePattern_19() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_20() + { + UsingExpression(@"c is [.. string?]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_21() + { + UsingExpression(@"c is [.. string? slice]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_22() + { + UsingExpression(@"c is [.. string? slice, ')']"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.CharacterLiteralExpression); + { + N(SyntaxKind.CharacterLiteralToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_23() + { + UsingExpression(@"c is [.. string? slice ')']", + // (1,24): error CS1003: Syntax error, ',' expected + // c is [.. string? slice ')'] + Diagnostic(ErrorCode.ERR_SyntaxError, "')'").WithArguments(",").WithLocation(1, 24)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.CharacterLiteralExpression); + { + N(SyntaxKind.CharacterLiteralToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_24() + { + UsingExpression(@"c is [.. string[]? slice "")""]", + // (1,26): error CS1003: Syntax error, ',' expected + // c is [.. string[]? slice ")"] + Diagnostic(ErrorCode.ERR_SyntaxError, @""")""").WithArguments(",").WithLocation(1, 26)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.StringLiteralExpression); + { + N(SyntaxKind.StringLiteralToken, "\")\""); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_25() + { + UsingExpression(@"c is [.. int[]? slice 5]", + // (1,23): error CS1003: Syntax error, ',' expected + // c is [.. int[]? slice 5] + Diagnostic(ErrorCode.ERR_SyntaxError, "5").WithArguments(",").WithLocation(1, 23)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "5"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_26() + { + UsingExpression(@"c is [.. int[]? slice int i]", + // (1,23): error CS1003: Syntax error, ',' expected + // c is [.. int[]? slice int i] + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 23)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_27() + { + UsingExpression(@"c is [.. string[]? slice string s]", + // (1,26): error CS1003: Syntax error, ',' expected + // c is [.. string[]? slice string s] + Diagnostic(ErrorCode.ERR_SyntaxError, "string").WithArguments(",").WithLocation(1, 26)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "s"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_28() + { + UsingExpression(@"c is [.. char[]? slice char ch]", + // (1,24): error CS1003: Syntax error, ',' expected + // c is [.. char[]? slice char ch] + Diagnostic(ErrorCode.ERR_SyntaxError, "char").WithArguments(",").WithLocation(1, 24)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.CharKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.CharKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "ch"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index d94f42dd3cfed..1ca8465151f27 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -5488,6 +5488,329 @@ public void ParseSwitchStatementWithUnclosedPatternAndArrow() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern1() + { + UsingStatement(""" + switch (obj) + { + case Type?: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern2() + { + UsingStatement(""" + switch (obj) + { + case Type? varName: + break; + } + """, + // (3,24): error CS1525: Invalid expression term 'break' + // case Type? varName: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(3, 24), + // (3,24): error CS1003: Syntax error, ':' expected + // case Type? varName: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 24)); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern3() + { + UsingStatement(""" + switch (obj) + { + case Type? when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern4() + { + UsingStatement(""" + switch (obj) + { + case Type? varName when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern5() + { + UsingStatement(""" + switch (obj) + { + case (Type? when) when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs index 0f33ab868b05d..77f90db34eb36 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs @@ -4231,4 +4231,658 @@ public void TestIncompleteSwitchExpression() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern1() + { + UsingExpression(""" + obj switch + { + Type? => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern1_Colon() + { + UsingExpression(""" + obj switch + { + Type? : 1 + } + """, + // (3,11): error CS1003: Syntax error, '=>' expected + // Type? : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 11)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern2() + { + UsingExpression(""" + obj switch + { + Type? varName => 1 + } + """, + // (3,9): error CS1003: Syntax error, '=>' expected + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(3, 9), + // (3,9): error CS1525: Invalid expression term '?' + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(3, 9), + // (3,23): error CS1003: Syntax error, ':' expected + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 23), + // (3,23): error CS1525: Invalid expression term '}' + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(3, 23)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.ConditionalExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern2_Colon() + { + UsingExpression(""" + obj switch + { + Type? varName : 1 + } + """, + // (3,9): error CS1003: Syntax error, '=>' expected + // Type? varName : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(3, 9), + // (3,9): error CS1525: Invalid expression term '?' + // Type? varName : 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(3, 9)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.ConditionalExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern3() + { + UsingExpression(""" + obj switch + { + Type? when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern3_Colon() + { + UsingExpression(""" + obj switch + { + Type? when x > 0 : 1 + } + """, + // (3,22): error CS1003: Syntax error, '=>' expected + // Type? when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 22)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern4() + { + UsingExpression(""" + obj switch + { + Type? varName when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern4_Colon() + { + UsingExpression(""" + obj switch + { + Type? varName when x > 0 : 1 + } + """, + // (3,30): error CS1003: Syntax error, '=>' expected + // Type? varName when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 30)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern5() + { + UsingExpression(""" + obj switch + { + (Type? when) when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern5_Colon() + { + UsingExpression(""" + obj switch + { + (Type? when) when x > 0 : 1 + } + """, + // (3,29): error CS1003: Syntax error, '=>' expected + // (Type? when) when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 29)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestConditionalExpressionAsPattern() + { + UsingExpression(""" + obj switch + { + (flag ? a : b) => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "flag"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } }