未验证 提交 4cd280b5 编写于 作者: N Neal Gafter 提交者: GitHub

Diagnose parsing precedence inversions with left operand that do not end in an expression (#40089)

Fixes #10492
上级 e8fe1af8
......@@ -86,3 +86,9 @@ could be different than the one that compiler used to find.
extern event System.Action E = null; // error
}
```
13. https://github.com/dotnet/roslyn/issues/10492 Under some circumstances, the compiler would accept an expression that does not obey the rules of the language grammar. Examples include
- `e is {} + c`
- `e is T t + c`
These all have in common that the left operand is of looser precedence than the `+` operator, but the left operand does not end in an expression so it cannot "consume" the addition. Such expressions will no longer be permitted in Visual Studio 2019 version 16.5 and later.
......@@ -8662,7 +8662,7 @@ internal static bool IsRightAssociative(SyntaxKind op)
enum Precedence : uint
{
Expression = 0, // Loosest possible precedence, used to accept all expressions
Assignment,
Assignment = Expression,
Lambda = Assignment, // "The => operator has the same precedence as assignment (=) and is right-associative."
Conditional,
Coalescing,
......@@ -8682,13 +8682,19 @@ enum Precedence : uint
Cast,
PointerIndirection,
AddressOf,
Primary_UNUSED, // Primaries are parsed in an ad-hoc manner.
Primary,
}
private static Precedence GetPrecedence(SyntaxKind op)
{
switch (op)
{
case SyntaxKind.QueryExpression:
return Precedence.Expression;
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
return Precedence.Lambda;
case SyntaxKind.SimpleAssignmentExpression:
case SyntaxKind.AddAssignmentExpression:
case SyntaxKind.SubtractAssignmentExpression:
......@@ -8703,6 +8709,7 @@ private static Precedence GetPrecedence(SyntaxKind op)
case SyntaxKind.CoalesceAssignmentExpression:
return Precedence.Assignment;
case SyntaxKind.CoalesceExpression:
case SyntaxKind.ThrowExpression:
return Precedence.Coalescing;
case SyntaxKind.LogicalOrExpression:
return Precedence.ConditionalOr;
......@@ -8763,6 +8770,41 @@ private static Precedence GetPrecedence(SyntaxKind op)
return Precedence.Range;
case SyntaxKind.ConditionalExpression:
return Precedence.Expression;
case SyntaxKind.AliasQualifiedName:
case SyntaxKind.AnonymousObjectCreationExpression:
case SyntaxKind.ArgListExpression:
case SyntaxKind.ArrayCreationExpression:
case SyntaxKind.BaseExpression:
case SyntaxKind.CharacterLiteralExpression:
case SyntaxKind.ConditionalAccessExpression:
case SyntaxKind.DeclarationExpression:
case SyntaxKind.DefaultExpression:
case SyntaxKind.DefaultLiteralExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.FalseLiteralExpression:
case SyntaxKind.GenericName:
case SyntaxKind.IdentifierName:
case SyntaxKind.ImplicitArrayCreationExpression:
case SyntaxKind.ImplicitStackAllocArrayCreationExpression:
case SyntaxKind.InterpolatedStringExpression:
case SyntaxKind.InvocationExpression:
case SyntaxKind.NullLiteralExpression:
case SyntaxKind.NumericLiteralExpression:
case SyntaxKind.ObjectCreationExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.PointerMemberAccessExpression:
case SyntaxKind.PostDecrementExpression:
case SyntaxKind.PostIncrementExpression:
case SyntaxKind.PredefinedType:
case SyntaxKind.RefExpression:
case SyntaxKind.SimpleMemberAccessExpression:
case SyntaxKind.StackAllocArrayCreationExpression:
case SyntaxKind.StringLiteralExpression:
case SyntaxKind.SuppressNullableWarningExpression:
case SyntaxKind.ThisExpression:
case SyntaxKind.TrueLiteralExpression:
case SyntaxKind.TupleExpression:
return Precedence.Primary;
default:
throw ExceptionUtilities.UnexpectedValue(op);
}
......@@ -8841,7 +8883,10 @@ private ExpressionSyntax ParseSubExpression(Precedence precedence)
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
var result = ParseSubExpressionCore(precedence);
#if DEBUG
// Ensure every expression kind is handled in GetPrecedence
_ = GetPrecedence(result.Kind);
#endif
_recursionDepth--;
return result;
}
......@@ -8965,8 +9010,6 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax leftOperand,
var newPrecedence = GetPrecedence(opKind);
Debug.Assert(newPrecedence > 0); // All binary operators must have precedence > 0!
// check for >> or >>=
bool doubleOp = false;
if (tk == SyntaxKind.GreaterThanToken
......@@ -9001,8 +9044,29 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax leftOperand,
break;
}
// Precedence is okay, so we'll "take" this operator.
// We'll "take" this operator, as precedence is tentatively OK.
var opToken = this.EatContextualToken(tk);
if (leftOperand.Kind == SyntaxKind.IsPatternExpression || IsStrict)
{
var leftPrecedence = GetPrecedence(leftOperand.Kind);
if (newPrecedence > leftPrecedence)
{
// Normally, a left operand with a looser precedence will consume all right operands that
// have a tighter precedence. For example, in the expression `a + b * c`, the `* c` part
// will be consumed as part of the right operand of the addition. However, there are a
// few circumstances in which a tighter precedence is not consumed: that occurs when the
// left hand operator does not have an expression as its right operand. This occurs for
// the is-type operator and the is-pattern operator. Source text such as
// `a is {} + b` should produce a syntax error, as parsing the `+` with an `is`
// expression as its left operand would be a precedence inversion. Similarly, it occurs
// with an anonymous method expression or a lambda expression with a block body. No
// further parsing will find a way to fix things up, so we accept the operator but issue
// an error.
opToken = this.AddError(opToken, ErrorCode.ERR_UnexpectedToken, opToken.Text);
}
}
if (doubleOp)
{
// combine tokens into a single token
......
......@@ -805,36 +805,36 @@ static unsafe void M(dynamic d1, System.TypedReference tr)
var comp = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.UnsafeReleaseDll);
comp.VerifyDiagnostics(
// (11,13): error CS0019: Operator '%' cannot be applied to operands of type 'method group' and 'dynamic'
// (10,13): error CS0019: Operator '%' cannot be applied to operands of type 'method group' and 'dynamic'
// M % d1,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "M % d1").WithArguments("%", "method group", "dynamic"),
// (12,13): error CS0019: Operator '+' cannot be applied to operands of type 'dynamic' and 'method group'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "M % d1").WithArguments("%", "method group", "dynamic").WithLocation(10, 13),
// (11,13): error CS0019: Operator '+' cannot be applied to operands of type 'dynamic' and 'method group'
// d1 + M,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 + M").WithArguments("+", "dynamic", "method group"),
// (13,13): error CS0019: Operator '-' cannot be applied to operands of type 'lambda expression' and 'dynamic'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 + M").WithArguments("+", "dynamic", "method group").WithLocation(11, 13),
// (12,13): error CS0019: Operator '-' cannot be applied to operands of type 'lambda expression' and 'dynamic'
// ( ()=>{} ) - d1,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "( ()=>{} ) - d1").WithArguments("-", "lambda expression", "dynamic"),
// (14,13): error CS0019: Operator '>>' cannot be applied to operands of type 'dynamic' and 'lambda expression'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "( ()=>{} ) - d1").WithArguments("-", "lambda expression", "dynamic").WithLocation(12, 13),
// (13,13): error CS0019: Operator '>>' cannot be applied to operands of type 'dynamic' and 'lambda expression'
// d1 >> ( ()=>{} ),
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 >> ( ()=>{} )").WithArguments(">>", "dynamic", "lambda expression"),
// (15,13): error CS0019: Operator '<<' cannot be applied to operands of type 'anonymous method' and 'dynamic'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 >> ( ()=>{} )").WithArguments(">>", "dynamic", "lambda expression").WithLocation(13, 13),
// (14,13): error CS0019: Operator '<<' cannot be applied to operands of type 'anonymous method' and 'dynamic'
// delegate {} << d1,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "delegate {} << d1").WithArguments("<<", "anonymous method", "dynamic"),
// (16,13): error CS0019: Operator '<<' cannot be applied to operands of type 'dynamic' and 'anonymous method'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "delegate {} << d1").WithArguments("<<", "anonymous method", "dynamic").WithLocation(14, 13),
// (15,13): error CS0019: Operator '<<' cannot be applied to operands of type 'dynamic' and 'anonymous method'
// d1 << delegate {},
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 << delegate {}").WithArguments("<<", "dynamic", "anonymous method"),
// (17,13): error CS0019: Operator '>' cannot be applied to operands of type 'int*' and 'dynamic'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 << delegate {}").WithArguments("<<", "dynamic", "anonymous method").WithLocation(15, 13),
// (16,13): error CS0019: Operator '>' cannot be applied to operands of type 'int*' and 'dynamic'
// (int*)null > d1,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(int*)null > d1").WithArguments(">", "int*", "dynamic"),
// (18,13): error CS0019: Operator '<' cannot be applied to operands of type 'dynamic' and 'int*'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(int*)null > d1").WithArguments(">", "int*", "dynamic").WithLocation(16, 13),
// (17,13): error CS0019: Operator '<' cannot be applied to operands of type 'dynamic' and 'int*'
// d1 < (int*)null,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 < (int*)null").WithArguments("<", "dynamic", "int*"),
// (19,13): error CS0019: Operator '>' cannot be applied to operands of type 'dynamic' and 'System.TypedReference'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 < (int*)null").WithArguments("<", "dynamic", "int*").WithLocation(17, 13),
// (18,13): error CS0019: Operator '>' cannot be applied to operands of type 'dynamic' and 'TypedReference'
// d1 > tr,
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 > tr").WithArguments(">", "dynamic", "System.TypedReference"),
// (20,13): error CS0019: Operator '>' cannot be applied to operands of type 'System.TypedReference' and 'dynamic'
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 > tr").WithArguments(">", "dynamic", "System.TypedReference").WithLocation(18, 13),
// (19,13): error CS0019: Operator '>' cannot be applied to operands of type 'TypedReference' and 'dynamic'
// tr > d1
Diagnostic(ErrorCode.ERR_BadBinaryOps, "tr > d1").WithArguments(">", "System.TypedReference", "dynamic"));
Diagnostic(ErrorCode.ERR_BadBinaryOps, "tr > d1").WithArguments(">", "System.TypedReference", "dynamic").WithLocation(19, 13));
}
[Fact]
......
......@@ -276,10 +276,17 @@ static void Main(string[] args)
}
}
";
CreateCompilation(text).
VerifyDiagnostics(Diagnostic(ErrorCode.ERR_BadBinaryOps, "p.bar + far").WithArguments("+", "method group", "method group"),
Diagnostic(ErrorCode.ERR_BadBinaryOps, @"(x) => { System.Console.WriteLine(""Lambda:{0}"", x); } + far").WithArguments("+", "lambda expression", "method group"),
Diagnostic(ErrorCode.ERR_BadBinaryOps, @"delegate (int x) { System.Console.WriteLine(""Anonymous:{0}"", x); } + far").WithArguments("+", "anonymous method", "method group"));
CreateCompilation(text).VerifyDiagnostics(
// (11,16): error CS0019: Operator '+' cannot be applied to operands of type 'method group' and 'method group'
// goo += p.bar + far;// Invalid
Diagnostic(ErrorCode.ERR_BadBinaryOps, "p.bar + far").WithArguments("+", "method group", "method group").WithLocation(11, 16),
// (12,16): error CS0019: Operator '+' cannot be applied to operands of type 'lambda expression' and 'method group'
// goo += (x) => { System.Console.WriteLine("Lambda:{0}", x); } + far;// Invalid
Diagnostic(ErrorCode.ERR_BadBinaryOps, @"(x) => { System.Console.WriteLine(""Lambda:{0}"", x); } + far").WithArguments("+", "lambda expression", "method group").WithLocation(12, 16),
// (13,16): error CS0019: Operator '+' cannot be applied to operands of type 'anonymous method' and 'method group'
// goo += delegate (int x) { System.Console.WriteLine("Anonymous:{0}", x); } + far;// Invalid
Diagnostic(ErrorCode.ERR_BadBinaryOps, @"delegate (int x) { System.Console.WriteLine(""Anonymous:{0}"", x); } + far").WithArguments("+", "anonymous method", "method group").WithLocation(13, 16)
);
}
// Removal or concatenation for the delegate on Variance
......@@ -12362,11 +12369,20 @@ static int Main()
}
}
";
DiagnosticsUtils.VerifyErrorsAndGetCompilationWithMscorlib(text,
new ErrorDescription[] { new ErrorDescription { Code = (int)ErrorCode.ERR_LambdaInIsAs, Line = 10, Column = 23 },
new ErrorDescription { Code = (int)ErrorCode.ERR_LambdaInIsAs, Line = 11, Column = 23 },
new ErrorDescription { Code = (int)ErrorCode.ERR_LambdaInIsAs, Line = 12, Column = 22 },
new ErrorDescription { Code = (int)ErrorCode.ERR_LambdaInIsAs, Line = 13, Column = 22 }});
CreateCompilation(text).VerifyDiagnostics(
// (10,23): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group.
// bool b1 = (() => { }) is Del; // CS0837
Diagnostic(ErrorCode.ERR_LambdaInIsAs, "(() => { }) is Del").WithLocation(10, 23),
// (11,23): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group.
// bool b2 = delegate() { } is Del;// CS0837
Diagnostic(ErrorCode.ERR_LambdaInIsAs, "delegate() { } is Del").WithLocation(11, 23),
// (12,22): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group.
// Del d1 = () => { } as Del; // CS0837
Diagnostic(ErrorCode.ERR_LambdaInIsAs, "() => { } as Del").WithLocation(12, 22),
// (13,22): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group.
// Del d2 = delegate() { } as Del; // CS0837
Diagnostic(ErrorCode.ERR_LambdaInIsAs, "delegate() { } as Del").WithLocation(13, 22)
);
}
[Fact]
......
......@@ -7966,5 +7966,177 @@ public void GenericPropertyPattern()
}
EOF();
}
[Fact, WorkItem(10492, "https://github.com/dotnet/roslyn/issues/10492")]
public void PrecedenceInversionWithDeclarationPattern()
{
UsingExpression("o is C c + d",
// (1,10): error CS1073: Unexpected token '+'
// o is C c + d
Diagnostic(ErrorCode.ERR_UnexpectedToken, "+").WithArguments("+").WithLocation(1, 10)
);
N(SyntaxKind.AddExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "o");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.DeclarationPattern);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "C");
}
N(SyntaxKind.SingleVariableDesignation);
{
N(SyntaxKind.IdentifierToken, "c");
}
}
}
N(SyntaxKind.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "d");
}
}
EOF();
}
[Fact, WorkItem(10492, "https://github.com/dotnet/roslyn/issues/10492")]
public void PrecedenceInversionWithRecursivePattern()
{
UsingExpression("o is {} + d",
// (1,9): error CS1073: Unexpected token '+'
// o is {} + d
Diagnostic(ErrorCode.ERR_UnexpectedToken, "+").WithArguments("+").WithLocation(1, 9)
);
N(SyntaxKind.AddExpression);
{
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "o");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.RecursivePattern);
{
N(SyntaxKind.PropertyPatternClause);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
}
}
N(SyntaxKind.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "d");
}
}
EOF();
}
[Fact, WorkItem(10492, "https://github.com/dotnet/roslyn/issues/10492")]
public void PrecedenceInversionWithTypeTest()
{
UsingExpression("o is int + d",
// (1,6): error CS1525: Invalid expression term 'int'
// o is int + d
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(1, 6)
);
N(SyntaxKind.IsPatternExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "o");
}
N(SyntaxKind.IsKeyword);
N(SyntaxKind.ConstantPattern);
{
N(SyntaxKind.AddExpression);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "d");
}
}
}
}
EOF();
}
[Fact, WorkItem(10492, "https://github.com/dotnet/roslyn/issues/10492")]
public void PrecedenceInversionWithBlockLambda()
{
UsingExpression("() => {} + d",
TestOptions.Regular.WithStrictFeature(),
// (1,10): error CS1073: Unexpected token '+'
// () => {} + d
Diagnostic(ErrorCode.ERR_UnexpectedToken, "+").WithArguments("+").WithLocation(1, 10)
);
UsingExpression("()=>{} + d");
N(SyntaxKind.AddExpression);
{
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.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "d");
}
}
EOF();
}
[Fact, WorkItem(10492, "https://github.com/dotnet/roslyn/issues/10492")]
public void PrecedenceInversionWithAnonymousMethod()
{
UsingExpression("delegate {} + d",
TestOptions.Regular.WithStrictFeature(),
// (1,13): error CS1073: Unexpected token '+'
// delegate {} + d
Diagnostic(ErrorCode.ERR_UnexpectedToken, "+").WithArguments("+").WithLocation(1, 13)
);
UsingExpression("delegate {} + d");
N(SyntaxKind.AddExpression);
{
N(SyntaxKind.AnonymousMethodExpression);
{
N(SyntaxKind.DelegateKeyword);
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
}
N(SyntaxKind.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "d");
}
}
EOF();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册