From e371b40223b5e746bbae899cf6be72f9bd4d671d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jun 2023 10:26:21 -0700 Subject: [PATCH] Fix issue parsing collection literal containing lambdas --- .../CSharp/Portable/Parser/LanguageParser.cs | 13 +- .../Parsing/CollectionLiteralParsingTests.cs | 599 ++++++++++++++++++ 2 files changed, 611 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 1bcb019769573..8e82bab31e4c2 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -11782,9 +11782,20 @@ private bool IsPossibleLambdaExpression(Precedence precedence) using var _ = this.GetDisposableResetPoint(resetOnDispose: true); + // A lambda could be starting with attributes, attempt to skip past them and check after that point. if (CurrentToken.Kind == SyntaxKind.OpenBracketToken) { - ParseAttributeDeclarations(inExpressionContext: true); + // Subtle case to deal with. Consider: + // + // [X, () => {} vs: + // [X] () => {} + // + // The former is a collection expression, the latter an attributed-lambda. However, we will likely + // successfully parse out `[X,` as an incomplete attribute, and thus think the former is the latter. So, + // to ensure proper parsing of the collection expressions, bail out if the attribute is not complete. + var attributeDeclarations = ParseAttributeDeclarations(inExpressionContext: true); + if (attributeDeclarations is [.., { CloseBracketToken.IsMissing: true }]) + return false; } bool seenStatic; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionLiteralParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionLiteralParsingTests.cs index 7cacceaf5f2f7..60adf13e3cd2b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionLiteralParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionLiteralParsingTests.cs @@ -6307,4 +6307,603 @@ public void New3() } EOF(); } + + [Fact] + public void LiteralContainingLambda1() + { + UsingExpression("_ = [Main, () => { }]"); + + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact] + public void LiteralContainingLambda2() + { + UsingExpression("_ = [() => { }, () => { }]"); + + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact] + public void LiteralContainingLambda3() + { + UsingExpression("_ = [() => { }, Main]"); + + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact] + public void LiteralContainingLambda4() + { + UsingTree(""" + using System; + class Program + { + static void F(Action[] a) { } + static void Main() + { + F([Main, () => { }]); + } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "System"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Program"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Main"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LiteralContainingLambda5() + { + UsingTree(""" + using System; + class Program + { + static void F(Action[] a) { } + static void Main() + { + F([Main, Main, () => { }]); + } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "System"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Program"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Main"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LiteralContainingLambda6() + { + UsingTree(""" + using System; + class Program + { + static void F(Action[] a) { } + static void Main() + { + F([Main(), () => { }]); + } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "System"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Program"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Main"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CollectionCreationExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Main"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ExpressionElement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } }