提交 db1f92d5 编写于 作者: N Neal Gafter 提交者: GitHub

Correct an off-by-one error in the precedence for is-pattern parsing. (#15917)

Fixes #15734
上级 e459e69d
......@@ -9208,6 +9208,9 @@ private bool IsAwaitExpression()
return false;
}
/// <summary>
/// Parse a subexpression of the enclosing operator of the given precedence.
/// </summary>
private ExpressionSyntax ParseSubExpression(Precedence precedence)
{
_recursionDepth++;
......@@ -9434,7 +9437,7 @@ private ExpressionSyntax ParseThrowExpression()
private ExpressionSyntax ParseIsExpression(ExpressionSyntax leftOperand, SyntaxToken opToken)
{
var node = this.ParseTypeOrPattern();
var node = this.ParseTypeOrPatternForIsOperator();
if (node is PatternSyntax)
{
var result = _syntaxFactory.IsPatternExpression(leftOperand, opToken, (PatternSyntax)node);
......
......@@ -7,13 +7,15 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax
{
internal partial class LanguageParser : SyntaxParser
{
// Priority is the TypeSyntax. It might return TypeSyntax which might be a constant pattern such as enum 'Days.Sunday'
// We handle such cases in the binder of is operator.
// It is used for parsing patterns in the is operators.
private CSharpSyntaxNode ParseTypeOrPattern()
/// <summary>
/// Parses the type, or pattern, right-hand operand of an is expression.
/// Priority is the TypeSyntax. It may return a TypeSyntax which turns out in binding to
/// be a constant pattern such as enum 'Days.Sunday'. We handle such cases in the binder of the is operator.
/// </summary>
private CSharpSyntaxNode ParseTypeOrPatternForIsOperator()
{
var tk = this.CurrentToken.Kind;
CSharpSyntaxNode node = null;
Precedence precedence = GetPrecedence(SyntaxKind.IsPatternExpression);
switch (tk)
{
......@@ -39,67 +41,40 @@ private CSharpSyntaxNode ParseTypeOrPattern()
{
TypeSyntax type = this.ParseType(ParseTypeMode.AfterIsOrCase);
tk = this.CurrentToken.ContextualKind;
if (!type.IsMissing)
if (!type.IsMissing && this.IsTrueIdentifier())
{
if (this.IsTrueIdentifier())
{
var designation = ParseSimpleDesignation();
node = _syntaxFactory.DeclarationPattern(type, designation);
}
var designation = ParseSimpleDesignation();
return _syntaxFactory.DeclarationPattern(type, designation);
}
if (node == null)
tk = this.CurrentToken.ContextualKind;
if ((!IsExpectedBinaryOperator(tk) || GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) <= precedence) &&
// member selection is not formally a binary operator but has higher precedence than relational
tk != SyntaxKind.DotToken)
{
Debug.Assert(Precedence.Shift == Precedence.Relational + 1);
if ((IsExpectedBinaryOperator(tk) && GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) > Precedence.Relational) ||
tk == SyntaxKind.DotToken) // member selection is not formally a binary operator but has higher precedence than relational
{
this.Reset(ref resetPoint);
// We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression
// So x is y < z should be parsed as (x is y) < z
// But x is y << z should be parsed as x is (y << z)
node = _syntaxFactory.ConstantPattern(this.ParseSubExpression(Precedence.Shift));
}
// it is a typical "is Type" operator
else
{
// Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X--
// as those are never semantically valid constant expressions for a pattern
node = type;
}
// it is a typical "is Type" operator.
// Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X--
// as those are never semantically valid constant expressions for a pattern
return type;
}
this.Reset(ref resetPoint);
}
finally
{
this.Release(ref resetPoint);
}
}
else
{
// In places where a pattern is supported, we do not support tuple types
// due to both syntactic and semantic ambiguities between tuple types and positional patterns.
// But it still might be a pattern such as (operand is 3) or (operand is nameof(x))
node = _syntaxFactory.ConstantPattern(this.ParseExpressionCore());
}
return node;
}
// This method is used when we always want a pattern as a result.
// For instance, it is used in parsing recursivepattern and propertypattern.
// SubPatterns in these (recursivepattern, propertypattern) must be a type of Pattern.
private PatternSyntax ParsePattern()
{
var node = this.ParseExpressionOrPattern(whenIsKeyword: false);
if (node is PatternSyntax)
{
return (PatternSyntax)node;
}
// We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression
// So x is y < z should be parsed as (x is y) < z
// But x is y << z should be parsed as x is (y << z)
Debug.Assert(Precedence.Shift == precedence + 1);
Debug.Assert(node is ExpressionSyntax);
return _syntaxFactory.ConstantPattern((ExpressionSyntax)node);
// In places where a pattern is supported, we do not support tuple types
// due to both syntactic and semantic ambiguities between tuple types and positional patterns.
// But it still might be a pattern such as (operand is 3) or (operand is nameof(x))
return _syntaxFactory.ConstantPattern(this.ParseSubExpressionCore(precedence));
}
//
......
......@@ -56,6 +56,16 @@ internal void UsingStatement(string text, params DiagnosticDescription[] expecte
UsingNode(node);
}
internal void UsingExpression(string text, params DiagnosticDescription[] expectedErrors)
{
var node = SyntaxFactory.ParseExpression(text);
// we validate the text roundtrips
Assert.Equal(text, node.ToFullString());
var actualErrors = node.GetDiagnostics();
actualErrors.Verify(expectedErrors);
UsingNode(node);
}
/// <summary>
/// Parses given string and initializes a depth-first preorder enumerator.
/// </summary>
......
......@@ -532,5 +532,379 @@ public void TypeDisambiguation_03()
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence00()
{
UsingExpression("A is B << C");
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.LeftShiftExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
N(SyntaxKind.LessThanLessThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "C");
}
}
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence01()
{
UsingExpression("A is 1 << 2");
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.LeftShiftExpression);
{
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "1");
}
N(SyntaxKind.LessThanLessThanToken);
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "2");
}
}
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence02()
{
UsingExpression("A is null < B");
N(SyntaxKind.LessThanExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
}
}
N(SyntaxKind.LessThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence02b()
{
UsingExpression("A is B < C");
N(SyntaxKind.LessThanExpression);
{
N(SyntaxKind.IsExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
N(SyntaxKind.LessThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "C");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence03()
{
UsingExpression("A is null == B");
N(SyntaxKind.EqualsExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
}
}
N(SyntaxKind.EqualsEqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence04()
{
UsingExpression("A is null & B");
N(SyntaxKind.BitwiseAndExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
}
}
N(SyntaxKind.AmpersandToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence05()
{
UsingExpression("A is null && B");
N(SyntaxKind.LogicalAndExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
}
}
N(SyntaxKind.AmpersandAmpersandToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence05b()
{
UsingExpression("A is null || B");
N(SyntaxKind.LogicalOrExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "A");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
}
}
N(SyntaxKind.BarBarToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
EOF();
}
[Fact, WorkItem(15734, "https://github.com/dotnet/roslyn/issues/15734")]
public void PatternExpressionPrecedence06()
{
UsingStatement(@"switch (e) {
case 1 << 2:
case B << C:
case null < B:
case null == B:
case null & B:
case null && B:
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.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.LeftShiftExpression);
{
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "1");
}
N(SyntaxKind.LessThanLessThanToken);
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "2");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.LeftShiftExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
N(SyntaxKind.LessThanLessThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "C");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.LessThanExpression);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
N(SyntaxKind.LessThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.EqualsExpression);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
N(SyntaxKind.EqualsEqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.BitwiseAndExpression);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
N(SyntaxKind.AmpersandToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.CaseSwitchLabel);
{
N(SyntaxKind.CaseKeyword);
N(SyntaxKind.LogicalAndExpression);
{
N(SyntaxKind.NullLiteralExpression);
{
N(SyntaxKind.NullKeyword);
}
N(SyntaxKind.AmpersandAmpersandToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "B");
}
}
N(SyntaxKind.ColonToken);
}
N(SyntaxKind.BreakStatement);
{
N(SyntaxKind.BreakKeyword);
N(SyntaxKind.SemicolonToken);
}
}
N(SyntaxKind.CloseBraceToken);
}
EOF();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册