From d689f37b73dcce4c6f3e18308a75e1718c33e38e Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Mon, 19 Sep 2016 17:07:24 -0700 Subject: [PATCH] Harden ParseEmbeddedStatement The consumers of ParseEmbeddedStatement depend on it having a non-null return. Yet it directly returns the result of ParseStatementCore which can validly return null values in error conditions. In the case that does happen return an empty statement with a diagnostic. --- .../CSharp/Portable/Parser/LanguageParser.cs | 11 +++++- .../Syntax/Parsing/StatementParsingTests.cs | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 5e059cca2db44..1dcb3a9849a72 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7457,10 +7457,19 @@ private StatementSyntax ParseEmbeddedStatement(bool complexCheck) statement = this.ParseStatementCore(); } + // The consumers of embedded statements are expecting to receive a non-null statement yet there are + // several error conditions that can lead ParseStatementCore to return null. When that occurs + // create an empty error Statement and return it to the caller. + if (statement == null) + { + Debug.Assert(CurrentToken.Kind != SyntaxKind.SemicolonToken); + statement = SyntaxFactory.EmptyStatement(EatToken(SyntaxKind.SemicolonToken)); + } + // An "embedded" statement is simply a statement that is not a labelled // statement or a declaration statement. Parse a normal statement and post- // check for the error case. - switch (statement?.Kind) + switch (statement.Kind) { case SyntaxKind.LabeledStatement: case SyntaxKind.LocalDeclarationStatement: diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 8fc82d40735ae..09e934e643064 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -2623,6 +2623,40 @@ static void Test(int arg1, (byte, byte) arg2) Assert.Equal(false, tree.GetRoot().ContainsDiagnostics); } + [Fact] + [WorkItem(684860, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/266237")] + public void DevDiv266237() + { + var source = @" +class Program +{ + static void Go() + { + using (var p = new P + { + + } + + protected override void M() + { + + } +} +"; + + var tree = SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.Regular); + tree.GetDiagnostics(tree.GetRoot()).Verify( + // (9,10): error CS1026: ) expected + // } + CSharpTestBase.Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(9, 10), + // (9,10): error CS1002: ; expected + // } + CSharpTestBase.Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(9, 10), + // (9,10): error CS1513: } expected + // } + CSharpTestBase.Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(9, 10)); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens;