Skip to content
51 changes: 42 additions & 9 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9343,7 +9343,7 @@ private CatchClauseSyntax ParseCatchClause()

_termState |= TerminatorState.IsEndOfFilterClause;
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var filterExpression = this.ParseExpressionCore();
var filterExpression = this.ParseExpressionForParenthesizedConstruct();

_termState = saveTerm;
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
Expand Down Expand Up @@ -9408,7 +9408,7 @@ private DoStatementSyntax ParseDoStatement(SyntaxList<AttributeListSyntax> attri

var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfDoWhileExpression;
var expression = this.ParseExpressionCore();
var expression = this.ParseExpressionForParenthesizedConstruct();
_termState = saveTerm;

return _syntaxFactory.DoStatement(
Expand Down Expand Up @@ -9854,6 +9854,39 @@ private GotoStatementSyntax ParseGotoStatement(SyntaxList<AttributeListSyntax> a
kind, attributes, @goto, caseOrDefault, arg, this.EatToken(SyntaxKind.SemicolonToken));
}

private ExpressionSyntax ParseExpressionForParenthesizedConstruct()
=> ParseErrantExpressionWhenNoCloseParenToken(this.ParseExpressionCore());

private ExpressionSyntax ParseErrantExpressionWhenNoCloseParenToken(ExpressionSyntax expression)
{
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
// Look for common case of `(a b)` where the user is in the middle of updating the expression to
// something like `(a && b)`. We want to recover well here as otherwise the subsequent close paren will
// cause problems for higher level constructs.
//
// Note if we see `(a b) =>` we don't do this as it's much more likely this is some incomplete lambda
// expression.
using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false);
var nextExpression = this.ParseExpressionCore();

if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken &&
this.PeekToken(1).Kind != SyntaxKind.EqualsGreaterThanToken &&
!nextExpression.GetLastToken().IsMissing)
{
expression = AddTrailingSkippedSyntax(
expression,
AddErrorToFirstToken(nextExpression, ErrorCode.ERR_UnexpectedToken, nextExpression.GetFirstToken().Text));
}
else
{
resetPoint.Reset();
}
}

return expression;
}

private IfStatementSyntax ParseIfStatement(SyntaxList<AttributeListSyntax> attributes)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IfKeyword);
Expand All @@ -9865,7 +9898,7 @@ private IfStatementSyntax ParseIfStatement(SyntaxList<AttributeListSyntax> attri
{
var ifKeyword = this.EatToken(SyntaxKind.IfKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var condition = this.ParseExpressionCore();
var condition = this.ParseExpressionForParenthesizedConstruct();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var consequence = this.ParseEmbeddedStatement();

Expand Down Expand Up @@ -9949,7 +9982,7 @@ private LockStatementSyntax ParseLockStatement(SyntaxList<AttributeListSyntax> a
attributes,
this.EatToken(SyntaxKind.LockKeyword),
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseExpressionCore(),
this.ParseExpressionForParenthesizedConstruct(),
this.EatToken(SyntaxKind.CloseParenToken),
this.ParseEmbeddedStatement());
}
Expand Down Expand Up @@ -10302,7 +10335,7 @@ private WhileStatementSyntax ParseWhileStatement(SyntaxList<AttributeListSyntax>
attributes,
this.EatToken(SyntaxKind.WhileKeyword),
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseExpressionCore(),
this.ParseExpressionForParenthesizedConstruct(),
this.EatToken(SyntaxKind.CloseParenToken),
this.ParseEmbeddedStatement());
}
Expand Down Expand Up @@ -12480,7 +12513,7 @@ private MakeRefExpressionSyntax ParseMakeRefExpression()
return _syntaxFactory.MakeRefExpression(
this.EatToken(),
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseSubExpression(Precedence.Expression),
this.ParseExpressionForParenthesizedConstruct(),
this.EatToken(SyntaxKind.CloseParenToken));
}

Expand All @@ -12489,7 +12522,7 @@ private RefTypeExpressionSyntax ParseRefTypeExpression()
return _syntaxFactory.RefTypeExpression(
this.EatToken(),
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseSubExpression(Precedence.Expression),
this.ParseExpressionForParenthesizedConstruct(),
this.EatToken(SyntaxKind.CloseParenToken));
}

Expand All @@ -12503,7 +12536,7 @@ private CheckedExpressionSyntax ParseCheckedOrUncheckedExpression()
kind,
checkedOrUnchecked,
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseSubExpression(Precedence.Expression),
this.ParseExpressionForParenthesizedConstruct(),
this.EatToken(SyntaxKind.CloseParenToken));
}

Expand Down Expand Up @@ -12685,7 +12718,7 @@ private ExpressionSyntax ParseCastOrParenExpressionOrTuple()

return _syntaxFactory.ParenthesizedExpression(
openParen,
expression,
this.ParseErrantExpressionWhenNoCloseParenToken(expression),
this.EatToken(SyntaxKind.CloseParenToken));
}

Expand Down
37 changes: 6 additions & 31 deletions src/Compilers/CSharp/Test/Semantic/Semantics/LockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

Expand Down Expand Up @@ -313,21 +312,10 @@ class Res
{
}
";
CreateCompilation(source).VerifyDiagnostics(// (6,19): error CS1026: ) expected
// lock (Res d = new Res ())// Invalid
Diagnostic(ErrorCode.ERR_CloseParenExpected, "d"),
// (6,33): error CS1002: ; expected
// lock (Res d = new Res ())// Invalid
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"),
// (6,33): error CS1513: } expected
// lock (Res d = new Res ())// Invalid
Diagnostic(ErrorCode.ERR_RbraceExpected, ")"),
// (6,15): error CS0119: 'Res' is a type, which is not valid in the given context
// lock (Res d = new Res ())// Invalid
Diagnostic(ErrorCode.ERR_BadSKunknown, "Res").WithArguments("Res", "type"),
// (6,19): error CS0103: The name 'd' does not exist in the current context
CreateCompilation(source).VerifyDiagnostics(
// (6,19): error CS1073: Unexpected token 'd'
// lock (Res d = new Res ())// Invalid
Diagnostic(ErrorCode.ERR_NameNotInContext, "d").WithArguments("d"));
Diagnostic(ErrorCode.ERR_UnexpectedToken, "d").WithArguments("d").WithLocation(6, 19));
}

[Fact]
Expand Down Expand Up @@ -447,25 +435,12 @@ public void goo()
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,26): error CS1026: ) expected
// (6,26): error CS1525: Invalid expression term 'object'
// lock (varnew object)
Diagnostic(ErrorCode.ERR_CloseParenExpected, "object").WithLocation(6, 26),
// (6,32): error CS1001: Identifier expected
// lock (varnew object)
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(6, 32),
// (6,32): error CS1003: Syntax error, ',' expected
// lock (varnew object)
Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",").WithLocation(6, 32),
// (6,33): error CS1002: ; expected
// lock (varnew object)
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 33),
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "object").WithArguments("object").WithLocation(6, 26),
// (6,19): error CS0103: The name 'varnew' does not exist in the current context
// lock (varnew object)
Diagnostic(ErrorCode.ERR_NameNotInContext, "varnew").WithArguments("varnew").WithLocation(6, 19),
// (6,26): error CS1023: Embedded statement cannot be a declaration or labeled statement
// lock (varnew object)
Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, @"object)
").WithLocation(6, 26));
Diagnostic(ErrorCode.ERR_NameNotInContext, "varnew").WithArguments("varnew").WithLocation(6, 19));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13467,15 +13467,9 @@ public void LambdaAttributeVersusCollectionLookahead5()
public void LambdaAttributeVersusCollectionLookahead5A()
{
UsingExpression("[A][B](C, D) ? ([e] f) : g",
// (1,1): error CS1073: Unexpected token ')'
// (1,21): error CS1073: Unexpected token 'f'
// [A][B](C, D) ? ([e] f) : g
Diagnostic(ErrorCode.ERR_UnexpectedToken, "[A][B](C, D) ? ([e] f").WithArguments(")").WithLocation(1, 1),
// (1,21): error CS1026: ) expected
// [A][B](C, D) ? ([e] f) : g
Diagnostic(ErrorCode.ERR_CloseParenExpected, "f").WithLocation(1, 21),
// (1,21): error CS1003: Syntax error, ':' expected
// [A][B](C, D) ? ([e] f) : g
Diagnostic(ErrorCode.ERR_SyntaxError, "f").WithArguments(":").WithLocation(1, 21));
Diagnostic(ErrorCode.ERR_UnexpectedToken, "f").WithArguments("f").WithLocation(1, 21));

N(SyntaxKind.ConditionalExpression);
{
Expand Down Expand Up @@ -13545,12 +13539,12 @@ public void LambdaAttributeVersusCollectionLookahead5A()
}
N(SyntaxKind.CloseBracketToken);
}
M(SyntaxKind.CloseParenToken);
N(SyntaxKind.CloseParenToken);
}
M(SyntaxKind.ColonToken);
N(SyntaxKind.ColonToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "f");
N(SyntaxKind.IdentifierToken, "g");
}
}
EOF();
Expand Down Expand Up @@ -13653,15 +13647,9 @@ public void LambdaAttributeVersusCollectionLookahead6()
public void LambdaAttributeVersusCollectionLookahead6A()
{
UsingExpression("[A][B](C, D) ? ((e,f) g) : h",
// (1,1): error CS1073: Unexpected token ')'
// [A][B](C, D) ? ((e,f) g) : h
Diagnostic(ErrorCode.ERR_UnexpectedToken, "[A][B](C, D) ? ((e,f) g").WithArguments(")").WithLocation(1, 1),
// (1,23): error CS1026: ) expected
// [A][B](C, D) ? ((e,f) g) : h
Diagnostic(ErrorCode.ERR_CloseParenExpected, "g").WithLocation(1, 23),
// (1,23): error CS1003: Syntax error, ':' expected
// (1,23): error CS1073: Unexpected token 'g'
// [A][B](C, D) ? ((e,f) g) : h
Diagnostic(ErrorCode.ERR_SyntaxError, "g").WithArguments(":").WithLocation(1, 23));
Diagnostic(ErrorCode.ERR_UnexpectedToken, "g").WithArguments("g").WithLocation(1, 23));

N(SyntaxKind.ConditionalExpression);
{
Expand Down Expand Up @@ -13739,12 +13727,12 @@ public void LambdaAttributeVersusCollectionLookahead6A()
}
N(SyntaxKind.CloseParenToken);
}
M(SyntaxKind.CloseParenToken);
N(SyntaxKind.CloseParenToken);
}
M(SyntaxKind.ColonToken);
N(SyntaxKind.ColonToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "g");
N(SyntaxKind.IdentifierToken, "h");
}
}
EOF();
Expand Down Expand Up @@ -13859,18 +13847,12 @@ public void LambdaAttributeVersusCollectionLookahead7()
public void LambdaAttributeVersusCollectionLookahead7A()
{
UsingExpression("[A][B](C, D) ? ((e,f)[] g) : h",
// (1,1): error CS1073: Unexpected token ')'
// [A][B](C, D) ? ((e,f)[] g) : h
Diagnostic(ErrorCode.ERR_UnexpectedToken, "[A][B](C, D) ? ((e,f)[] g").WithArguments(")").WithLocation(1, 1),
// (1,23): error CS0443: Syntax error; value expected
// [A][B](C, D) ? ((e,f)[] g) : h
Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 23),
// (1,25): error CS1026: ) expected
// [A][B](C, D) ? ((e,f)[] g) : h
Diagnostic(ErrorCode.ERR_CloseParenExpected, "g").WithLocation(1, 25),
// (1,25): error CS1003: Syntax error, ':' expected
// (1,25): error CS1073: Unexpected token 'g'
// [A][B](C, D) ? ((e,f)[] g) : h
Diagnostic(ErrorCode.ERR_SyntaxError, "g").WithArguments(":").WithLocation(1, 25));
Diagnostic(ErrorCode.ERR_UnexpectedToken, "g").WithArguments("g").WithLocation(1, 25));

N(SyntaxKind.ConditionalExpression);
{
Expand Down Expand Up @@ -13963,12 +13945,12 @@ public void LambdaAttributeVersusCollectionLookahead7A()
N(SyntaxKind.CloseBracketToken);
}
}
M(SyntaxKind.CloseParenToken);
N(SyntaxKind.CloseParenToken);
}
M(SyntaxKind.ColonToken);
N(SyntaxKind.ColonToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "g");
N(SyntaxKind.IdentifierToken, "h");
}
}
EOF();
Expand Down
Loading
Loading