From 222b8363aaf540884f61e889f6e9b85a77c8c055 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Thu, 12 Sep 2024 14:51:19 +0300 Subject: [PATCH 1/5] Fix regression of parsing `with` expression in conditional expression --- .../CSharp/Portable/Parser/LanguageParser.cs | 8 +- .../Test/Syntax/Parsing/RecordParsing.cs | 150 ++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index d68834aefdfe3..dd824f9239f5c 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7434,7 +7434,13 @@ bool canFollowNullableType() if (this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.AwaitKeyword) return false; - var nextTokenKind = PeekToken(1).Kind; + var nextToken = PeekToken(1); + + // Cases like `x is Y ? someRecord with { } : ...` + if (nextToken.ContextualKind == SyntaxKind.WithKeyword) + return false; + + var nextTokenKind = nextToken.Kind; // These token either 100% end a pattern or start a new one: diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs index 9c0cd03df2e9d..de6dde7df3b40 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs @@ -2099,6 +2099,156 @@ public void WithParsing19() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void WithParsingInConditionalExpression1() + { + var text = "x is X ? record with { } : record with { }"; + + UsingExpression(text); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void WithParsingInConditionalExpression2() + { + var text = "x is X.Y ? record with { } : record with { }"; + + UsingExpression(text); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void WithParsingInConditionalExpression_Incomplete() + { + var text = "x is X ? record with"; + + UsingExpression(text, + // (1,17): error CS1003: Syntax error, ':' expected + // x is X ? record with + Diagnostic(ErrorCode.ERR_SyntaxError, "with").WithArguments(":").WithLocation(1, 17)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + M(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "with"); + } + } + EOF(); + } + [Fact] public void ParameterListAndBaseListOnClass() { From edb7723b5f854644dc7f8e0e5f717926ce4ac8ff Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Thu, 12 Sep 2024 21:49:39 +0300 Subject: [PATCH 2/5] Fix linq query parsing --- .../CSharp/Portable/Parser/LanguageParser.cs | 9 +- .../Syntax/Parsing/ExpressionParsingTests.cs | 201 ++++++++++++++++++ 2 files changed, 206 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index dd824f9239f5c..c3d579552b52f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7425,13 +7425,14 @@ bool canFollowNullableType() // nullable-typed pattern if (IsTrueIdentifier(this.CurrentToken)) { - // In a non-async method, `await` is a simple identifier. However, if we see `x ? await` + // 1. `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 + // 2. 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) + // 3. `from` most likely starts a linq query: `x is Y ? from item in collection select item : ...` + if (this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.AwaitKeyword or SyntaxKind.FromKeyword) return false; var nextToken = PeekToken(1); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 2633665e833b8..35c676b2131ec 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -6822,5 +6822,206 @@ public void UnsignedRightShiftAssignment_04() EOF(); } } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void LinqQueryInConditionalExpression1() + { + var text = "x is X ? from item in collection select item : null"; + + UsingExpression(text); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.QueryExpression); + { + N(SyntaxKind.FromClause); + { + N(SyntaxKind.FromKeyword); + N(SyntaxKind.IdentifierToken, "item"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "collection"); + } + } + N(SyntaxKind.QueryBody); + { + N(SyntaxKind.SelectClause); + { + N(SyntaxKind.SelectKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "item"); + } + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void LinqQueryInConditionalExpression2() + { + var text = "x is X.Y ? from item in collection select item : null"; + + UsingExpression(text); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.QueryExpression); + { + N(SyntaxKind.FromClause); + { + N(SyntaxKind.FromKeyword); + N(SyntaxKind.IdentifierToken, "item"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "collection"); + } + } + N(SyntaxKind.QueryBody); + { + N(SyntaxKind.SelectClause); + { + N(SyntaxKind.SelectKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "item"); + } + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75074")] + public void LinqQueryInConditionalExpression_Incomplete() + { + var text = "x is X.Y ? from item"; + + UsingExpression(text, + // (1,21): error CS1001: Identifier expected + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 21), + // (1,21): error CS1003: Syntax error, 'in' expected + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("in").WithLocation(1, 21), + // (1,21): error CS1733: Expected expression + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 21), + // (1,21): error CS0742: A query body must end with a select clause or a group clause + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_ExpectedSelectOrGroup, "").WithLocation(1, 21), + // (1,21): error CS1003: Syntax error, ':' expected + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(1, 21), + // (1,21): error CS1733: Expected expression + // x is X.Y ? from item + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 21)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.QueryExpression); + { + N(SyntaxKind.FromClause); + { + N(SyntaxKind.FromKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "item"); + } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.InKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.QueryBody); + { + M(SyntaxKind.SelectClause); + { + M(SyntaxKind.SelectKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + EOF(); + } } } From 11ab036af6fcb84678cd3adf8f46e88034b1a7c0 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 21 Sep 2024 09:52:35 +0300 Subject: [PATCH 3/5] More `await` tests --- .../Test/Syntax/Parsing/AwaitParsingTests.cs | 334 ++++++++++++++++++ 1 file changed, 334 insertions(+) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs index 3479451b547ab..41a096017dc4b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs @@ -1339,6 +1339,340 @@ public void AwaitInConditionalExpressionAfterPattern4() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern5() + { + UsingDeclaration(""" + void M() + { + var c = x is X ? await y : z; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + 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"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern6() + { + UsingDeclaration(""" + async void M() + { + var c = x is X ? await y : z; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + 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"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern7() + { + UsingDeclaration(""" + void M() + { + var c = x is X ? await(y) : z; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + 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, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern8() + { + UsingDeclaration(""" + async void M() + { + var c = x is X ? await (y) : z; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void AwaitAsStartOfExpressionInConditional1() { From af0525604c15c533872e09e1a61bf502d6aa0aff Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 21 Sep 2024 10:42:47 +0300 Subject: [PATCH 4/5] Add `global` test --- .../Test/Syntax/Parsing/NameParsingTests.cs | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NameParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NameParsingTests.cs index ca6fe6ca83e6d..92adda1188dbb 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NameParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NameParsingTests.cs @@ -4,10 +4,8 @@ #nullable disable -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -1435,5 +1433,62 @@ void M() // M, Y<,>>(); Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Y<,>").WithLocation(13, 16)); } + + [Fact] + public void ParseGlobalAliasQualifiedNameAfterConditionalExpression() + { + UsingExpression("x is X ? global::X.Y.Z : default"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.GlobalKeyword); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Z"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } } } From af02bf240cea1c2d3c69abbe87a369e2ca0f1690 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 21 Sep 2024 11:03:45 +0300 Subject: [PATCH 5/5] Add `yield` tests --- .../Syntax/Parsing/StatementParsingTests.cs | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 2501a88a97d8a..d3e3fbbbd47b5 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -5809,6 +5809,190 @@ public void TestSwitchStatementWithNullableTypeInPattern5() EOF(); } + [Fact] + public void TestYieldReturnTokensAfterPattern() + { + UsingDeclaration(""" + void M() + { + var res = x is X? yield + return res; + } + """, options: null, + // (3,28): error CS1003: Syntax error, ':' expected + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 28), + // (3,28): error CS1525: Invalid expression term 'return' + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("return").WithLocation(3, 28), + // (3,28): error CS1002: ; expected + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 28)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "res"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "yield"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ReturnStatement); + { + N(SyntaxKind.ReturnKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "res"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestYieldBreakTokensAfterPattern() + { + UsingDeclaration(""" + void M() + { + var res = x is X? yield + break; + } + """, options: null, + // (3,28): error CS1003: Syntax error, ':' expected + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 28), + // (3,28): error CS1525: Invalid expression term 'break' + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(3, 28), + // (3,28): error CS1002: ; expected + // var res = x is X? yield + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 28)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "res"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "yield"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens;