diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index aeb22d38d17cdb5df9841129bdaa29df1b69d0cc..efbab3eab8e5c956f2c0df8c797b2de9b4572279 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Text; @@ -7146,35 +7147,44 @@ private bool IsEndOfFixedStatement() private StatementSyntax ParseEmbeddedStatement() { - // 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 error empty Statement and return it to the caller. - StatementSyntax statement = this.ParseStatementCore() ?? SyntaxFactory.EmptyStatement(EatToken(SyntaxKind.SemicolonToken)); + // ParseEmbeddedStatement is called through many recursive statement parsing cases. We + // keep the body exceptionally simple, and we optimize for the common case, to ensure it + // is inlined into the callers. Otherwise the overhead of this single method can have a + // deep impact on the number of recursive calls we can make (more than a hundred during + // empirical testing). - switch (statement.Kind) + return parseEmbeddedStatementRest(this.ParseStatementCore()); + + StatementSyntax parseEmbeddedStatementRest(StatementSyntax statement) { + if (statement == null) + { + // 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 error empty Statement and return it to the caller. + return SyntaxFactory.EmptyStatement(EatToken(SyntaxKind.SemicolonToken)); + } + // In scripts, stand-alone expression statements may not be followed by semicolons. // ParseExpressionStatement hides the error. // However, embedded expression statements are required to be followed by semicolon. - case SyntaxKind.ExpressionStatement: - if (IsScript) - { - var expressionStatementSyntax = (ExpressionStatementSyntax)statement; - var semicolonToken = expressionStatementSyntax.SemicolonToken; + if (statement.Kind == SyntaxKind.ExpressionStatement && + IsScript) + { + var expressionStatementSyntax = (ExpressionStatementSyntax)statement; + var semicolonToken = expressionStatementSyntax.SemicolonToken; - // Do not add a new error if the same error was already added. - if (semicolonToken.IsMissing && - !semicolonToken.GetDiagnostics().Contains(diagnosticInfo => (ErrorCode)diagnosticInfo.Code == ErrorCode.ERR_SemicolonExpected)) - { - semicolonToken = this.AddError(semicolonToken, ErrorCode.ERR_SemicolonExpected); - statement = expressionStatementSyntax.Update(expressionStatementSyntax.Expression, semicolonToken); - } + // Do not add a new error if the same error was already added. + if (semicolonToken.IsMissing && + !semicolonToken.GetDiagnostics().Contains(diagnosticInfo => (ErrorCode)diagnosticInfo.Code == ErrorCode.ERR_SemicolonExpected)) + { + semicolonToken = this.AddError(semicolonToken, ErrorCode.ERR_SemicolonExpected); + return expressionStatementSyntax.Update(expressionStatementSyntax.Expression, semicolonToken); } + } - break; + return statement; } - - return statement; } private BreakStatementSyntax ParseBreakStatement()