From 41d70bf1f3462e46b515df6c5e5508c288719756 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Fri, 29 Mar 2024 20:54:03 +0300 Subject: [PATCH] Improve parser recovery around nullable types in patterns --- .../CSharp/Portable/Parser/LanguageParser.cs | 60 +- .../Parser/LanguageParser_Patterns.cs | 2 +- .../Parsing/DeclarationExpressionTests.cs | 45 +- .../Syntax/Parsing/NullableParsingTests.cs | 470 +++++++++++++-- .../Syntax/Parsing/PatternParsingTests.cs | 101 ++-- .../Syntax/Parsing/PatternParsingTests2.cs | 384 ++++++++++++ .../PatternParsingTests_ListPatterns.cs | 180 +++++- .../Syntax/Parsing/StatementParsingTests.cs | 315 ++++++++++ .../Parsing/SwitchExpressionParsingTests.cs | 565 ++++++++++++++++++ 9 files changed, 1975 insertions(+), 147 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 0d94193097554..07f4e835a7ef9 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6916,20 +6916,20 @@ private enum ParseTypeMode FirstElementOfPossibleTupleLiteral, } - private TypeSyntax ParseType(ParseTypeMode mode = ParseTypeMode.Normal) + private TypeSyntax ParseType(ParseTypeMode mode = ParseTypeMode.Normal, bool whenIsKeyword = false) { if (this.CurrentToken.Kind == SyntaxKind.RefKeyword) { return _syntaxFactory.RefType( this.EatToken(), this.CurrentToken.Kind == SyntaxKind.ReadOnlyKeyword ? this.EatToken() : null, - ParseTypeCore(ParseTypeMode.AfterRef)); + ParseTypeCore(ParseTypeMode.AfterRef, whenIsKeyword)); } - return ParseTypeCore(mode); + return ParseTypeCore(mode, whenIsKeyword); } - private TypeSyntax ParseTypeCore(ParseTypeMode mode) + private TypeSyntax ParseTypeCore(ParseTypeMode mode, bool whenIsKeyword) { NameOptions nameOptions; switch (mode) @@ -6970,7 +6970,7 @@ private TypeSyntax ParseTypeCore(ParseTypeMode mode) { case SyntaxKind.QuestionToken when canBeNullableType(): { - var question = EatNullableQualifierIfApplicable(mode); + var question = EatNullableQualifierIfApplicable(mode, whenIsKeyword); if (question != null) { type = _syntaxFactory.NullableType(type, question); @@ -7043,7 +7043,7 @@ bool canBeNullableType() return type; } - private SyntaxToken EatNullableQualifierIfApplicable(ParseTypeMode mode) + private SyntaxToken EatNullableQualifierIfApplicable(ParseTypeMode mode, bool whenIsKeyword) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.QuestionToken); using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); @@ -7065,12 +7065,48 @@ 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. - // - // Similarly, if we have `T?[]` we do want to treat that as an array of nullables (following + // These contexts might be a type that is at the end of an expression. + // For backward compatibility we want to consider a `?` token as part of the `?:` operator if possible. + // However, if current token is an identifier, it might be beneficial to allow `?` + // and treat this identifier as a designation. Nullable types in patterns are semantically invalid, + // so we will get a nice error about that during binding. + if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + { + // Given that we are deciding whether we take `?:` operator or not while looking at state after `?`, + // our focus is on `:` token as well. But `:` have special meaning in context of switch statements + // and expressions (since `:` in switch expressions is recovered to `=>`). So consider the following case: + // `case T? t:`. On its own `T ? t :` can be treated as a conditional expression with condition `T`, + // 'whenTrue' `t` and 'whenFalse' not yet parsed. But it is way better to take `:` as case label end and + // thus parse label condition as a declaration pattern `T? t`. `whenIsKeyword` flag can conveniently tell + // whether we are in a 'top-level' state of a label, so if condition get parenthesized like `case (a ? b : c):` + // we don't take the first `:` as label end and parse label condition as a parenthesized conditional expression + if ((_termState.HasFlag(TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement) || + _termState.HasFlag(TerminatorState.IsPatternInSwitchExpressionArm)) && whenIsKeyword) + { + return true; + } + + using var _ = GetDisposableResetPoint(resetOnDispose: true); + this.EatToken(); + + // If token after identifier starts an expression it is probably an error case, e.g. missing a `,` in a pattern + if (CanStartExpression()) + { + return true; + } + + // These token either 100% end a pattern or start a new one (again, in cases with missing `,` and so on). + // Note, that some cases of 'token starts a new pattern' are handled by condition above, e.g. starting of type patterns + return this.CurrentToken.Kind is SyntaxKind.OpenParenToken or SyntaxKind.CloseParenToken + or SyntaxKind.OpenBraceToken or SyntaxKind.CloseBraceToken + or SyntaxKind.OpenBracketToken or SyntaxKind.CloseBracketToken + or SyntaxKind.CommaToken + or SyntaxKind.EndOfFileToken; + } + + // 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: diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index e9b3e9a56cb83..2ea122b9b258a 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -214,7 +214,7 @@ 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, whenIsKeyword); if (type.IsMissing || !CanTokenFollowTypeInPattern(precedence)) { // either it is not shaped like a type, or it is a constant expression. diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs index a8dc74a200040..413ec76ce8c02 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,45 +135,37 @@ 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.ConditionalExpression); + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.IsExpression); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); { - N(SyntaxKind.IdentifierToken, "e"); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); } - N(SyntaxKind.IsKeyword); - N(SyntaxKind.PredefinedType); + N(SyntaxKind.SingleVariableDesignation); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierToken, "x"); } } - N(SyntaxKind.QuestionToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.ColonToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } } N(SyntaxKind.CloseParenToken); N(SyntaxKind.Block); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs index aaacbfe16dcc9..5ca23e49ac2c9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs @@ -681,13 +681,8 @@ public void NullCoalescingOperator_NullableArray() [Fact] public void DeclarationPattern_NullableType() { - UsingStatement("switch (e) { case T? t: break; }", - // (1,25): error CS1525: Invalid expression term 'break' - // switch (e) { case T? t: break; } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "break").WithArguments("break").WithLocation(1, 25), - // (1,25): error CS1003: Syntax error, ':' expected - // switch (e) { case T? t: break; } - Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":").WithLocation(1, 25)); + UsingStatement("switch (e) { case T? t: break; }"); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -700,27 +695,25 @@ public void DeclarationPattern_NullableType() N(SyntaxKind.OpenBraceToken); N(SyntaxKind.SwitchSection); { - N(SyntaxKind.CaseSwitchLabel); + N(SyntaxKind.CasePatternSwitchLabel); { N(SyntaxKind.CaseKeyword); - N(SyntaxKind.ConditionalExpression); + N(SyntaxKind.DeclarationPattern); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.NullableType); { - N(SyntaxKind.IdentifierToken, "T"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); } - N(SyntaxKind.QuestionToken); - N(SyntaxKind.IdentifierName); + N(SyntaxKind.SingleVariableDesignation); { N(SyntaxKind.IdentifierToken, "t"); } - N(SyntaxKind.ColonToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } } - M(SyntaxKind.ColonToken); + N(SyntaxKind.ColonToken); } N(SyntaxKind.BreakStatement); { @@ -813,29 +806,15 @@ public void DeclarationPattern_NullableArray() // (1,9): error CS0103: The name 'e' does not exist in the current context // switch (e) { case T[]? t: break; } Diagnostic(ErrorCode.ERR_NameNotInContext, "e").WithArguments("e").WithLocation(1, 9), - // (1,19): error CS8400: Feature 'type pattern' is not available in C# 8.0. Please use language version 9.0 or greater. - // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "T[]").WithArguments("type pattern", "9.0").WithLocation(1, 19), // (1,19): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) // switch (e) { case T[]? t: break; } Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(1, 19), - // (1,22): error CS1003: Syntax error, ':' expected - // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(1, 22), - // (1,22): error CS1513: } expected - // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(1, 22), - // (1,24): warning CS0164: This label has not been referenced + // (1,22): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.WRN_UnreferencedLabel, "t").WithLocation(1, 24)); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(1, 22)); + + UsingStatement(test, options: TestOptions.Regular8); - UsingStatement(test, options: TestOptions.Regular8, - // (1,22): error CS1003: Syntax error, ':' expected - // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(1, 22), - // (1,22): error CS1513: } expected - // switch (e) { case T[]? t: break; } - Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(1, 22)); N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -851,36 +830,39 @@ public void DeclarationPattern_NullableArray() N(SyntaxKind.CasePatternSwitchLabel); { N(SyntaxKind.CaseKeyword); - N(SyntaxKind.TypePattern); + N(SyntaxKind.DeclarationPattern); { - N(SyntaxKind.ArrayType); + N(SyntaxKind.NullableType); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "T"); - } - N(SyntaxKind.ArrayRankSpecifier); + N(SyntaxKind.ArrayType); { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.OmittedArraySizeExpression); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.OmittedArraySizeExpressionToken); + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); } - N(SyntaxKind.CloseBracketToken); } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); } } - M(SyntaxKind.ColonToken); + N(SyntaxKind.ColonToken); } - N(SyntaxKind.LabeledStatement); + N(SyntaxKind.BreakStatement); { - N(SyntaxKind.IdentifierToken, "t"); - N(SyntaxKind.ColonToken); - N(SyntaxKind.BreakStatement); - { - N(SyntaxKind.BreakKeyword); - N(SyntaxKind.SemicolonToken); - } + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); } } N(SyntaxKind.CloseBraceToken); @@ -1774,5 +1756,379 @@ public void CreateNullableArray_07() } 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_NullableType3() + { + UsingExpression("x is Type? and (1, 2)"); + + 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.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_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_NullableType5() + { + 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.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + } + } + 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/PatternParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs index 29899976828fa..cedef07c1d2ad 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs @@ -2550,26 +2550,21 @@ public void SwitchExpression02() // (1,32): error CS8370: Feature 'recursive patterns' is not available in C# 7.3. Please use language version 8.0 or greater. // class C { void M() { var v = 1 switch { a?b:c => d }; } } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "switch").WithArguments("recursive patterns", "8.0").WithLocation(1, 32), - // (1,41): error CS0103: The name 'a' does not exist in the current context + // (1,41): error CS0246: The type or namespace name 'a' could not be found (are you missing a using directive or an assembly reference?) // class C { void M() { var v = 1 switch { a?b:c => d }; } } - Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(1, 41), - // (1,42): error CS1003: Syntax error, '=>' expected + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "a").WithArguments("a").WithLocation(1, 41), + // (1,41): error CS8116: It is not legal to use nullable type 'a?' in a pattern; use the underlying type 'a' instead. // class C { void M() { var v = 1 switch { a?b:c => d }; } } - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(1, 42), - // (1,42): error CS1525: Invalid expression term '?' + Diagnostic(ErrorCode.ERR_PatternNullableType, "a?").WithArguments("a").WithLocation(1, 41), + // (1,44): error CS1003: Syntax error, '=>' expected // class C { void M() { var v = 1 switch { a?b:c => d }; } } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(1, 42), - // (1,43): error CS0103: The name 'b' does not exist in the current context - // class C { void M() { var v = 1 switch { a?b:c => d }; } } - Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(1, 43)); + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(1, 44)); UsingExpression(test, TestOptions.RegularWithoutRecursivePatterns, - // (1,13): error CS1003: Syntax error, '=>' expected - // 1 switch { a?b:c => d } - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(1, 13), - // (1,13): error CS1525: Invalid expression term '?' + // (1,15): error CS1003: Syntax error, '=>' expected // 1 switch { a?b:c => d } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(1, 13)); + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(1, 15)); + N(SyntaxKind.SwitchExpression); { N(SyntaxKind.NumericLiteralExpression); @@ -2580,37 +2575,32 @@ public void SwitchExpression02() N(SyntaxKind.OpenBraceToken); N(SyntaxKind.SwitchExpressionArm); { - N(SyntaxKind.ConstantPattern); + N(SyntaxKind.DeclarationPattern); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.NullableType); { - N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); } } M(SyntaxKind.EqualsGreaterThanToken); - N(SyntaxKind.ConditionalExpression); + N(SyntaxKind.SimpleLambdaExpression); { - M(SyntaxKind.IdentifierName); + N(SyntaxKind.Parameter); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "c"); } - N(SyntaxKind.QuestionToken); + N(SyntaxKind.EqualsGreaterThanToken); N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken, "b"); - } - N(SyntaxKind.ColonToken); - N(SyntaxKind.SimpleLambdaExpression); - { - N(SyntaxKind.Parameter); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.EqualsGreaterThanToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "d"); - } + N(SyntaxKind.IdentifierToken, "d"); } } } @@ -12588,6 +12578,15 @@ public void ConditionalAsConstantPatternInSwitchStatement() UsingStatement(@"switch(a) { case a?x:y: break; }", TestOptions.RegularWithPatternCombinators ); + + // Despite the test name being about conditional expression (and looking at code you will probably think about the conditional expression first as well) + // there are 2 valid parse strategies here: + // 1. Parse `a?x:y` as conditional expression and then `:` as case label end + // 2. Parse `a?x` as a declaration pattern with nullable type `a?` and variable `x`, then `:` as case label end and then `y:` as a label + // From the error recovery perspective these cases are equivalent: they are both syntactically valid and semantically invalid. + // With current error recovery state parser is more greedy regarding the `:` tokens, so it treats the first `:` as a case statement end, + // thus the second strategy is used. However, slight changes to the code like adding parentheses around `a?x:y` may change that. + // For the `(a?x:y)` case see test below. N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -12600,32 +12599,35 @@ public void ConditionalAsConstantPatternInSwitchStatement() N(SyntaxKind.OpenBraceToken); N(SyntaxKind.SwitchSection); { - N(SyntaxKind.CaseSwitchLabel); + N(SyntaxKind.CasePatternSwitchLabel); { N(SyntaxKind.CaseKeyword); - N(SyntaxKind.ConditionalExpression); + N(SyntaxKind.DeclarationPattern); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.NullableType); { - N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); } - N(SyntaxKind.QuestionToken); - N(SyntaxKind.IdentifierName); + N(SyntaxKind.SingleVariableDesignation); { N(SyntaxKind.IdentifierToken, "x"); } - N(SyntaxKind.ColonToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "y"); - } } N(SyntaxKind.ColonToken); } - N(SyntaxKind.BreakStatement); + N(SyntaxKind.LabeledStatement); { - N(SyntaxKind.BreakKeyword); - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.IdentifierToken, "y"); + N(SyntaxKind.ColonToken); + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } } } N(SyntaxKind.CloseBraceToken); @@ -12639,6 +12641,7 @@ public void ConditionalAsConstantPatternInSwitchStatement_Parenthesized() UsingStatement(@"switch(a) { case (a?x:y): break; }", TestOptions.RegularWithPatternCombinators ); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs index 51fd79f19312e..d98776de73724 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; @@ -945,5 +946,388 @@ public void ExtendedPropertySubpattern_InPositionalPattern() } EOF(); } + + [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..a570ee4c2144f 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,181 @@ 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(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index d94f42dd3cfed..1c3ebb4856686 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -5488,6 +5488,321 @@ 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; + } + """); + + 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.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..24e9d35911053 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs @@ -4231,4 +4231,569 @@ 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 + } + """); + + 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.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern2_Colon() + { + UsingExpression(""" + obj switch + { + Type? varName : 1 + } + """, + // (3,19): error CS1003: Syntax error, '=>' expected + // Type? varName : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 19)); + + 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"); + } + } + 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 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(); + } }