diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 765521177162d..04c78a591d55d 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -4485,7 +4485,7 @@ private VariableDeclaratorSyntax ParseVariableDeclarator( case SyntaxKind.OpenBracketToken: bool sawNonOmittedSize; _termState |= TerminatorState.IsPossibleEndOfVariableDeclaration; - var specifier = this.ParseArrayRankSpecifier(isArrayCreation: false, expectSizes: flags == VariableFlags.Fixed, allowQuestionToken: false, sawNonOmittedSize: out sawNonOmittedSize); + var specifier = this.ParseArrayRankSpecifier(isArrayCreation: false, expectSizes: flags == VariableFlags.Fixed, questionTokenModeOpt: null, sawNonOmittedSize: out sawNonOmittedSize); _termState = saveTerm; var open = specifier.OpenBracketToken; var sizes = specifier.Sizes; @@ -6175,7 +6175,6 @@ private TypeSyntax ParseTypeCore( ParseTypeMode mode, bool expectSizes) { - var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.AfterIs; NameOptions nameOptions; switch (mode) { @@ -6205,31 +6204,15 @@ private TypeSyntax ParseTypeCore( } var type = this.ParseUnderlyingType(parentIsParameter: mode == ParseTypeMode.Parameter, options: nameOptions); + Debug.Assert(type != null); - if (this.CurrentToken.Kind == SyntaxKind.QuestionToken && - // we do not permit nullable types in a declaration pattern - (mode != ParseTypeMode.AfterIs && mode != ParseTypeMode.AfterCase || !IsTrueIdentifier(this.PeekToken(1)))) + if (this.CurrentToken.Kind == SyntaxKind.QuestionToken) { - var resetPoint = this.GetResetPoint(); - try + var question = EatNullableQualifierIfApplicable(mode); + if (question != null) { - var question = this.EatToken(); - - if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind))) - { - this.Reset(ref resetPoint); - - Debug.Assert(type != null); - return type; - } - - question = CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable); type = _syntaxFactory.NullableType(type, question); } - finally - { - this.Release(ref resetPoint); - } } switch (mode) @@ -6262,7 +6245,7 @@ private TypeSyntax ParseTypeCore( while (this.IsPossibleRankAndDimensionSpecifier()) { bool unused; - var rank = this.ParseArrayRankSpecifier(mode == ParseTypeMode.ArrayCreation, expectSizes, allowQuestionToken: true, out unused); + var rank = this.ParseArrayRankSpecifier(mode == ParseTypeMode.ArrayCreation, expectSizes, questionTokenModeOpt: mode, out unused); ranks.Add(rank); expectSizes = false; } @@ -6279,6 +6262,36 @@ private TypeSyntax ParseTypeCore( return type; } + private SyntaxToken EatNullableQualifierIfApplicable(ParseTypeMode mode) + { + Debug.Assert(this.CurrentToken.Kind == SyntaxKind.QuestionToken); + + // we do not permit nullable types in a declaration pattern + if (mode != ParseTypeMode.AfterIs && mode != ParseTypeMode.AfterCase || !IsTrueIdentifier(this.PeekToken(1))) + { + var resetPoint = this.GetResetPoint(); + try + { + var question = this.EatToken(); + + var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.AfterIs; + if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind))) + { + this.Reset(ref resetPoint); + return null; + } + + return CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable); + } + finally + { + this.Release(ref resetPoint); + } + } + + return null; + } + private bool PointerTypeModsFollowedByRankAndDimensionSpecifier() { // Are pointer specifiers (if any) followed by an array specifier? @@ -6301,7 +6314,7 @@ private bool IsPossibleRankAndDimensionSpecifier() return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken; } - private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, bool expectSizes, bool allowQuestionToken, out bool sawNonOmittedSize) + private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, bool expectSizes, ParseTypeMode? questionTokenModeOpt, out bool sawNonOmittedSize) { sawNonOmittedSize = false; bool sawOmittedSize = false; @@ -6368,9 +6381,9 @@ private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, b var close = this.EatToken(SyntaxKind.CloseBracketToken); SyntaxToken questionToken = null; - if (allowQuestionToken && this.CurrentToken.Kind == SyntaxKind.QuestionToken) + if (questionTokenModeOpt != null && this.CurrentToken.Kind == SyntaxKind.QuestionToken) { - questionToken = this.EatToken(); + questionToken = EatNullableQualifierIfApplicable(questionTokenModeOpt.GetValueOrDefault()); } return _syntaxFactory.ArrayRankSpecifier(open, list, close, questionToken); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 44496496f0b02..4da14a4914939 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -456,6 +456,72 @@ static void F2(object? w) Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(14, 26)); } + [Fact] + public void NullableAndConditionalOperators() + { + var source = +@"class Program +{ + static void F1(object x) + { + _ = x is string? 1 : 2; + _ = x is string? ? 1 : 2; + _ = x is string ? ? 1 : 2; + _ = x as string?? x; + _ = x as string ? ?? x; + } + static void F2(object y) + { + _ = y is object[]? 1 : 2; + _ = y is object[]? ? 1 : 2; + _ = y is object[] ? ? 1 : 2; + _ = y as object[]?? y; + _ = y as object[] ? ?? y; + } + static void F3(object z) + { + _ = z is T[][]? 1 : 2; + _ = z is T[]?[] ? 1 : 2; + _ = z is T[] ? [] ? 1 : 2; + _ = z as T[][]?? z; + _ = z as T[] ? [] ?? z; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); + comp.VerifyDiagnostics( + // (6,24): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x is string? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(6, 24), + // (7,25): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x is string ? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(7, 25), + // (9,25): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x as string ? ?? x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(9, 25), + // (14,26): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y is object[]? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(14, 26), + // (15,27): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y is object[] ? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(15, 27), + // (17,27): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y as object[] ? ?? y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(17, 27), + // (22,21): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z is T[]?[] ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(22, 21), + // (23,22): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z is T[] ? [] ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(23, 22), + // (25,22): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z as T[] ? [] ?? z; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(25, 22)); + + comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics(); + } + [Fact, WorkItem(29318, "https://github.com/dotnet/roslyn/issues/29318")] public void IsOperatorOnNonNullExpression() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs index e67f0dbcccd85..81f760b5fd19e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs @@ -291,6 +291,601 @@ public void NullableArray_Cast_05() EOF(); } + [Fact] + public void ConditionalOperator_NotNullableType() + { + UsingExpression("x is T ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NullableType() + { + UsingExpression("x is T ? ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NotNullableArray() + { + UsingExpression("x is T[] ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NullableArray() + { + UsingExpression("x is T[] ? ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NotNullableType() + { + UsingExpression("x as T?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableType() + { + UsingExpression("x as T? ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableType_Invalid() + { + UsingExpression("x as T??? y", + // (1,9): error CS1525: Invalid expression term '?' + // x as T??? y + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(1, 9), + // (1,12): error CS1003: Syntax error, ':' expected + // x as T??? y + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":", "").WithLocation(1, 12), + // (1,12): error CS1733: Expected expression + // x as T??? y + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 12)); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NotNullableArray() + { + UsingExpression("x as T[] ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableArray() + { + UsingExpression("x as T[] ? ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [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(":", "break").WithLocation(1, 25)); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_NullableArray() + { + UsingStatement("switch (e) { case T[]? t: break; }", + // (1,21): error CS0443: Syntax error; value expected + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 21), + // (1,27): error CS1525: Invalid expression term 'break' + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "break").WithArguments("break").WithLocation(1, 27), + // (1,27): error CS1003: Syntax error, ':' expected + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":", "break").WithLocation(1, 27)); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_ArrayOfNullableType() + { + UsingStatement("switch (e) { case T?[] t: break; }"); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_NullableArrayOfArray() + { + UsingStatement("switch (e) { case T[]?[] t: break; }"); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + [Fact] public void NullableArray_TypeArgument() {