Skip to content

Instantly share code, notes, and snippets.

@sudipto80
Created February 2, 2016 09:07
Show Gist options
  • Save sudipto80/c27710fed74dbd99de60 to your computer and use it in GitHub Desktop.
Save sudipto80/c27710fed74dbd99de60 to your computer and use it in GitHub Desktop.
Roslyn LanguageParser.cs
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax
{
internal partial class LanguageParser : SyntaxParser
{
// list pools - allocators for lists that are used to build sequences of nodes. The lists
// can be reused (hence pooled) since the syntax factory methods don't keep references to
// them
private readonly SyntaxListPool _pool = new SyntaxListPool(); // Don't need to reset this.
private readonly SyntaxFactoryContext _syntaxFactoryContext; // Fields are resettable.
private readonly ContextAwareSyntax _syntaxFactory; // Has context, the fields of which are resettable.
private int _recursionDepth;
private TerminatorState _termState; // Resettable
private bool _isInTry; // Resettable
// NOTE: If you add new state, you should probably add it to ResetPoint as well.
internal LanguageParser(
Lexer lexer,
CSharp.CSharpSyntaxNode oldTree,
IEnumerable<TextChangeRange> changes,
LexerMode lexerMode = LexerMode.Syntax,
CancellationToken cancellationToken = default(CancellationToken))
: base(lexer, lexerMode, oldTree, changes, allowModeReset: false,
preLexIfNotIncremental: true, cancellationToken: cancellationToken)
{
_syntaxFactoryContext = new SyntaxFactoryContext();
_syntaxFactory = new ContextAwareSyntax(_syntaxFactoryContext);
}
// Special Name checks
private static bool IsName(CSharpSyntaxNode node, SyntaxKind kind)
{
if (node.Kind == SyntaxKind.IdentifierToken)
{
return ((SyntaxToken)node).ContextualKind == kind;
}
else if (node.Kind == SyntaxKind.IdentifierName)
{
return ((IdentifierNameSyntax)node).Identifier.ContextualKind == kind;
}
else
{
return node.ToString() == SyntaxFacts.GetText(kind);
}
}
private static bool IsNameGlobal(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.GlobalKeyword);
}
private static bool IsNameAssembly(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.AssemblyKeyword);
}
private static bool IsNameModule(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.ModuleKeyword);
}
private static bool IsNameType(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.TypeKeyword);
}
private static bool IsNameGet(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.GetKeyword);
}
private static bool IsNameSet(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.SetKeyword);
}
private static bool IsNameAdd(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.AddKeyword);
}
private static bool IsNameRemove(CSharpSyntaxNode node)
{
return IsName(node, SyntaxKind.RemoveKeyword);
}
private static bool IsSomeWord(SyntaxKind kind)
{
return kind == SyntaxKind.IdentifierToken || SyntaxFacts.IsKeywordKind(kind);
}
// Parsing rule terminating conditions. This is how we know if it is
// okay to abort the current parsing rule when unexpected tokens occur.
[Flags]
internal enum TerminatorState
{
EndOfFile = 0,
IsNamespaceMemberStartOrStop = 1 << 0,
IsAttributeDeclarationTerminator = 1 << 1,
IsPossibleAggregateClauseStartOrStop = 1 << 2,
IsPossibleMemberStartOrStop = 1 << 3,
IsEndOfReturnType = 1 << 4,
IsEndOfParameterList = 1 << 5,
IsEndOfFieldDeclaration = 1 << 6,
IsPossibleEndOfVariableDeclaration = 1 << 7,
IsEndOfTypeArgumentList = 1 << 8,
IsPossibleStatementStartOrStop = 1 << 9,
IsEndOfFixedStatement = 1 << 10,
IsEndOfTryBlock = 1 << 11,
IsEndOfCatchClause = 1 << 12,
IsEndOfilterClause = 1 << 13,
IsEndOfCatchBlock = 1 << 14,
IsEndOfDoWhileExpression = 1 << 15,
IsEndOfForStatementArgument = 1 << 16,
IsEndOfDeclarationClause = 1 << 17,
IsEndOfArgumentList = 1 << 18,
IsSwitchSectionStart = 1 << 19,
IsEndOfTypeParameterList = 1 << 20,
IsEndOfMethodSignature = 1 << 21,
IsEndOfNameInExplicitInterface = 1 << 22,
}
private const int LastTerminatorState = (int)TerminatorState.IsEndOfNameInExplicitInterface;
private bool IsTerminator()
{
if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken)
{
return true;
}
for (int i = 1; i <= LastTerminatorState; i <<= 1)
{
TerminatorState isolated = _termState & (TerminatorState)i;
if (isolated != 0)
{
switch (isolated)
{
case TerminatorState.IsNamespaceMemberStartOrStop:
if (this.IsNamespaceMemberStartOrStop())
{
return true;
}
break;
case TerminatorState.IsAttributeDeclarationTerminator:
if (this.IsAttributeDeclarationTerminator())
{
return true;
}
break;
case TerminatorState.IsPossibleAggregateClauseStartOrStop:
if (this.IsPossibleAggregateClauseStartOrStop())
{
return true;
}
break;
case TerminatorState.IsPossibleMemberStartOrStop:
if (this.IsPossibleMemberStartOrStop())
{
return true;
}
break;
case TerminatorState.IsEndOfReturnType:
if (this.IsEndOfReturnType())
{
return true;
}
break;
case TerminatorState.IsEndOfParameterList:
if (this.IsEndOfParameterList())
{
return true;
}
break;
case TerminatorState.IsEndOfFieldDeclaration:
if (this.IsEndOfFieldDeclaration())
{
return true;
}
break;
case TerminatorState.IsPossibleEndOfVariableDeclaration:
if (this.IsPossibleEndOfVariableDeclaration())
{
return true;
}
break;
case TerminatorState.IsEndOfTypeArgumentList:
if (this.IsEndOfTypeArgumentList())
{
return true;
}
break;
case TerminatorState.IsPossibleStatementStartOrStop:
if (this.IsPossibleStatementStartOrStop())
{
return true;
}
break;
case TerminatorState.IsEndOfFixedStatement:
if (this.IsEndOfFixedStatement())
{
return true;
}
break;
case TerminatorState.IsEndOfTryBlock:
if (this.IsEndOfTryBlock())
{
return true;
}
break;
case TerminatorState.IsEndOfCatchClause:
if (this.IsEndOfCatchClause())
{
return true;
}
break;
case TerminatorState.IsEndOfilterClause:
if (this.IsEndOfFilterClause())
{
return true;
}
break;
case TerminatorState.IsEndOfCatchBlock:
if (this.IsEndOfCatchBlock())
{
return true;
}
break;
case TerminatorState.IsEndOfDoWhileExpression:
if (this.IsEndOfDoWhileExpression())
{
return true;
}
break;
case TerminatorState.IsEndOfForStatementArgument:
if (this.IsEndOfForStatementArgument())
{
return true;
}
break;
case TerminatorState.IsEndOfDeclarationClause:
if (this.IsEndOfDeclarationClause())
{
return true;
}
break;
case TerminatorState.IsEndOfArgumentList:
if (this.IsEndOfArgumentList())
{
return true;
}
break;
case TerminatorState.IsSwitchSectionStart:
if (this.IsPossibleSwitchSection())
{
return true;
}
break;
case TerminatorState.IsEndOfTypeParameterList:
if (this.IsEndOfTypeParameterList())
{
return true;
}
break;
case TerminatorState.IsEndOfMethodSignature:
if (this.IsEndOfMethodSignature())
{
return true;
}
break;
case TerminatorState.IsEndOfNameInExplicitInterface:
if (this.IsEndOfNameInExplicitInterface())
{
return true;
}
break;
}
}
}
return false;
}
private static CSharp.CSharpSyntaxNode GetOldParent(CSharp.CSharpSyntaxNode node)
{
return node != null ? node.Parent : null;
}
private struct NamespaceBodyBuilder
{
public SyntaxListBuilder<ExternAliasDirectiveSyntax> Externs;
public SyntaxListBuilder<UsingDirectiveSyntax> Usings;
public SyntaxListBuilder<AttributeListSyntax> Attributes;
public SyntaxListBuilder<MemberDeclarationSyntax> Members;
public NamespaceBodyBuilder(SyntaxListPool pool)
{
Externs = pool.Allocate<ExternAliasDirectiveSyntax>();
Usings = pool.Allocate<UsingDirectiveSyntax>();
Attributes = pool.Allocate<AttributeListSyntax>();
Members = pool.Allocate<MemberDeclarationSyntax>();
}
internal void Free(SyntaxListPool pool)
{
pool.Free(Members);
pool.Free(Attributes);
pool.Free(Usings);
pool.Free(Externs);
}
}
internal CompilationUnitSyntax ParseCompilationUnit()
{
return ParseWithStackGuard(
ParseCompilationUnitCore,
() => SyntaxFactory.CompilationUnit(
new SyntaxList<ExternAliasDirectiveSyntax>(),
new SyntaxList<UsingDirectiveSyntax>(),
new SyntaxList<AttributeListSyntax>(),
new SyntaxList<MemberDeclarationSyntax>(),
SyntaxFactory.Token(SyntaxKind.EndOfFileToken)));
}
internal CompilationUnitSyntax ParseCompilationUnitCore()
{
SyntaxToken tmp = null;
SyntaxListBuilder initialBadNodes = null;
var body = new NamespaceBodyBuilder(_pool);
try
{
this.ParseNamespaceBody(ref tmp, ref body, ref initialBadNodes, SyntaxKind.CompilationUnit);
var eof = this.EatToken(SyntaxKind.EndOfFileToken);
var result = _syntaxFactory.CompilationUnit(body.Externs, body.Usings, body.Attributes, body.Members, eof);
if (initialBadNodes != null)
{
// attach initial bad nodes as leading trivia on first token
result = AddLeadingSkippedSyntax(result, initialBadNodes.ToListNode());
_pool.Free(initialBadNodes);
}
return result;
}
finally
{
body.Free(_pool);
}
}
internal TNode ParseWithStackGuard<TNode>(Func<TNode> parseFunc, Func<TNode> createEmptyNodeFunc) where TNode : CSharpSyntaxNode
{
// If this value is non-zero then we are nesting calls to ParseWithStackGuard which should not be
// happening. It's not a bug but it's inefficient and should be changed.
Debug.Assert(_recursionDepth == 0);
try
{
return parseFunc();
}
catch (Exception ex) when (StackGuard.IsInsufficientExecutionStackException(ex))
{
return CreateForGlobalFailure(lexer.TextWindow.Position, createEmptyNodeFunc());
}
}
private TNode CreateForGlobalFailure<TNode>(int position, TNode node) where TNode : CSharpSyntaxNode
{
// Turn the complete input into a single skipped token. This avoids running the lexer, and therefore
// the preprocessor directive parser, which may itself run into the same problem that caused the
// original failure.
var builder = new SyntaxListBuilder(1);
builder.Add(SyntaxFactory.BadToken(null, lexer.TextWindow.Text.ToString(), null));
var fileAsTrivia = _syntaxFactory.SkippedTokensTrivia(builder.ToList<SyntaxToken>());
node = AddLeadingSkippedSyntax(node, fileAsTrivia);
ForceEndOfFile(); // force the scanner to report that it is at the end of the input.
return AddError(node, position, 0, ErrorCode.ERR_InsufficientStack);
}
private NamespaceDeclarationSyntax ParseNamespaceDeclaration()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.NamespaceDeclaration)
{
return (NamespaceDeclarationSyntax)this.EatNode();
}
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.NamespaceKeyword);
var namespaceToken = this.EatToken(SyntaxKind.NamespaceKeyword);
if (IsScript)
{
namespaceToken = this.AddError(namespaceToken, ErrorCode.ERR_NamespaceNotAllowedInScript);
}
var name = this.ParseQualifiedName();
if (ContainsGeneric(name))
{
// We're not allowed to have generics.
name = this.AddError(name, ErrorCode.ERR_UnexpectedGenericName);
}
if (ContainsAlias(name))
{
name = this.AddError(name, ErrorCode.ERR_UnexpectedAliasedName);
}
SyntaxToken openBrace;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || IsPossibleNamespaceMemberDeclaration())
{
//either we see the brace we expect here or we see something that could come after a brace
//so we insert a missing one
openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
}
else
{
//the next character is neither the brace we expect, nor a token that could follow the expected
//brace so we assume it's a mistake and replace it with a missing brace
openBrace = this.EatTokenWithPrejudice(SyntaxKind.OpenBraceToken);
openBrace = this.ConvertToMissingWithTrailingTrivia(openBrace, SyntaxKind.OpenBraceToken);
}
var body = new NamespaceBodyBuilder(_pool);
SyntaxListBuilder initialBadNodes = null;
try
{
this.ParseNamespaceBody(ref openBrace, ref body, ref initialBadNodes, SyntaxKind.NamespaceDeclaration);
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
SyntaxToken semicolon = null;
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatToken();
}
Debug.Assert(initialBadNodes == null); // init bad nodes should have been attached to open brace...
return _syntaxFactory.NamespaceDeclaration(namespaceToken, name, openBrace, body.Externs, body.Usings, body.Members, closeBrace, semicolon);
}
finally
{
body.Free(_pool);
}
}
private static bool ContainsAlias(NameSyntax name)
{
switch (name.Kind)
{
case SyntaxKind.GenericName:
return false;
case SyntaxKind.AliasQualifiedName:
return true;
case SyntaxKind.QualifiedName:
var qualifiedName = (QualifiedNameSyntax)name;
return ContainsAlias(qualifiedName.Left);
}
return false;
}
private static bool ContainsGeneric(NameSyntax name)
{
switch (name.Kind)
{
case SyntaxKind.GenericName:
return true;
case SyntaxKind.AliasQualifiedName:
return ContainsGeneric(((AliasQualifiedNameSyntax)name).Name);
case SyntaxKind.QualifiedName:
var qualifiedName = (QualifiedNameSyntax)name;
return ContainsGeneric(qualifiedName.Left) || ContainsGeneric(qualifiedName.Right);
}
return false;
}
private static bool IsPossibleStartOfTypeDeclaration(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.EnumKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.ClassKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.StructKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.InternalKeyword:
case SyntaxKind.NewKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.SealedKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.UnsafeKeyword:
case SyntaxKind.OpenBracketToken:
return true;
default:
return false;
}
}
private void AddSkippedNamespaceText(
ref SyntaxToken openBrace,
ref NamespaceBodyBuilder body,
ref SyntaxListBuilder initialBadNodes,
CSharpSyntaxNode skippedSyntax)
{
if (body.Members.Count > 0)
{
body.Members[body.Members.Count - 1] = AddTrailingSkippedSyntax(body.Members[body.Members.Count - 1], skippedSyntax);
}
else if (body.Attributes.Count > 0)
{
body.Attributes[body.Attributes.Count - 1] = AddTrailingSkippedSyntax(body.Attributes[body.Attributes.Count - 1], skippedSyntax);
}
else if (body.Usings.Count > 0)
{
body.Usings[body.Usings.Count - 1] = AddTrailingSkippedSyntax(body.Usings[body.Usings.Count - 1], skippedSyntax);
}
else if (body.Externs.Count > 0)
{
body.Externs[body.Externs.Count - 1] = AddTrailingSkippedSyntax(body.Externs[body.Externs.Count - 1], skippedSyntax);
}
else if (openBrace != null)
{
openBrace = AddTrailingSkippedSyntax(openBrace, skippedSyntax);
}
else
{
if (initialBadNodes == null)
{
initialBadNodes = _pool.Allocate();
}
initialBadNodes.AddRange(skippedSyntax);
}
}
// Parts of a namespace declaration in the order they can be defined.
private enum NamespaceParts
{
None = 0,
ExternAliases = 1,
Usings = 2,
GlobalAttributes = 3,
MembersAndStatements = 4,
}
private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes, SyntaxKind parentKind)
{
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
bool isGlobal = openBrace == null;
bool isGlobalScript = isGlobal && this.IsScript;
var saveTerm = _termState;
_termState |= TerminatorState.IsNamespaceMemberStartOrStop;
NamespaceParts seen = NamespaceParts.None;
var pendingIncompleteMembers = _pool.Allocate<MemberDeclarationSyntax>();
bool reportUnexpectedToken = true;
try
{
while (true)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.NamespaceKeyword:
// incomplete members must be processed before we add any nodes to the body:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
body.Members.Add(this.ParseNamespaceDeclaration());
seen = NamespaceParts.MembersAndStatements;
reportUnexpectedToken = true;
break;
case SyntaxKind.CloseBraceToken:
// A very common user error is to type an additional }
// somewhere in the file. This will cause us to stop parsing
// the root (global) namespace too early and will make the
// rest of the file unparseable and unusable by intellisense.
// We detect that case here and we skip the close curly and
// continue parsing as if we did not see the }
if (isGlobal)
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
var token = this.EatToken();
token = this.AddError(token,
IsScript ? ErrorCode.ERR_GlobalDefinitionOrStatementExpected : ErrorCode.ERR_EOFExpected);
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, token);
reportUnexpectedToken = true;
break;
}
else
{
// This token marks the end of a namespace body
return;
}
case SyntaxKind.EndOfFileToken:
// This token marks the end of a namespace body
return;
case SyntaxKind.ExternKeyword:
if (isGlobalScript && !ScanExternAliasDirective())
{
// extern member
goto default;
}
else
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
var @extern = ParseExternAliasDirective();
if (seen > NamespaceParts.ExternAliases)
{
@extern = this.AddErrorToFirstToken(@extern, ErrorCode.ERR_ExternAfterElements);
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, @extern);
}
else
{
body.Externs.Add(@extern);
seen = NamespaceParts.ExternAliases;
}
reportUnexpectedToken = true;
break;
}
case SyntaxKind.UsingKeyword:
if (isGlobalScript && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)
{
// incomplete members must be processed before we add any nodes to the body:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
body.Members.Add(_syntaxFactory.GlobalStatement(ParseUsingStatement()));
seen = NamespaceParts.MembersAndStatements;
}
else
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
var @using = this.ParseUsingDirective();
if (seen > NamespaceParts.Usings)
{
@using = this.AddError(@using, ErrorCode.ERR_UsingAfterElements);
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, @using);
}
else
{
body.Usings.Add(@using);
seen = NamespaceParts.Usings;
}
}
reportUnexpectedToken = true;
break;
case SyntaxKind.OpenBracketToken:
if (this.IsPossibleGlobalAttributeDeclaration())
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
var attribute = this.ParseAttributeDeclaration();
if (!isGlobal || seen > NamespaceParts.GlobalAttributes)
{
attribute = this.AddError(attribute, attribute.Target.Identifier, ErrorCode.ERR_GlobalAttributesNotFirst);
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, attribute);
}
else
{
body.Attributes.Add(attribute);
seen = NamespaceParts.GlobalAttributes;
}
reportUnexpectedToken = true;
break;
}
goto default;
default:
var memberOrStatement = this.ParseMemberDeclarationOrStatement(parentKind);
if (memberOrStatement == null)
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
// eat one token and try to parse declaration or statement again:
var skippedToken = EatToken();
if (reportUnexpectedToken && !skippedToken.ContainsDiagnostics)
{
skippedToken = this.AddError(skippedToken,
IsScript ? ErrorCode.ERR_GlobalDefinitionOrStatementExpected : ErrorCode.ERR_EOFExpected);
// do not report the error multiple times for subsequent tokens:
reportUnexpectedToken = false;
}
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, skippedToken);
}
else if (memberOrStatement.Kind == SyntaxKind.IncompleteMember && seen < NamespaceParts.MembersAndStatements)
{
pendingIncompleteMembers.Add(memberOrStatement);
reportUnexpectedToken = true;
}
else
{
// incomplete members must be processed before we add any nodes to the body:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
body.Members.Add(memberOrStatement);
seen = NamespaceParts.MembersAndStatements;
reportUnexpectedToken = true;
}
break;
}
}
}
finally
{
_termState = saveTerm;
// adds pending incomplete nodes:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
_pool.Free(pendingIncompleteMembers);
}
}
private static void AddIncompleteMembers(ref SyntaxListBuilder<MemberDeclarationSyntax> incompleteMembers, ref NamespaceBodyBuilder body)
{
if (incompleteMembers.Count > 0)
{
body.Members.AddRange(incompleteMembers);
incompleteMembers.Clear();
}
}
private void ReduceIncompleteMembers(ref SyntaxListBuilder<MemberDeclarationSyntax> incompleteMembers,
ref SyntaxToken openBrace, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes)
{
for (int i = 0; i < incompleteMembers.Count; i++)
{
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, incompleteMembers[i]);
}
incompleteMembers.Clear();
}
private bool IsPossibleNamespaceMemberDeclaration()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.ExternKeyword:
case SyntaxKind.UsingKeyword:
case SyntaxKind.NamespaceKeyword:
return true;
case SyntaxKind.IdentifierToken:
return IsPartialInNamespaceMemberDeclaration();
default:
return IsPossibleStartOfTypeDeclaration(this.CurrentToken.Kind);
}
}
private bool IsPartialInNamespaceMemberDeclaration()
{
if (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)
{
if (this.IsPartialType())
{
return true;
}
else if (this.PeekToken(1).Kind == SyntaxKind.NamespaceKeyword)
{
return true;
}
}
return false;
}
public bool IsEndOfNamespace()
{
return this.CurrentToken.Kind == SyntaxKind.CloseBraceToken;
}
public bool IsGobalAttributesTerminator()
{
return this.IsEndOfNamespace()
|| this.IsPossibleNamespaceMemberDeclaration();
}
private bool IsNamespaceMemberStartOrStop()
{
return this.IsEndOfNamespace()
|| this.IsPossibleNamespaceMemberDeclaration();
}
/// <summary>
/// Returns true if the lookahead tokens compose extern alias directive.
/// </summary>
private bool ScanExternAliasDirective()
{
// The check also includes the ending semicolon so that we can disambiguate among:
// extern alias foo;
// extern alias foo();
// extern alias foo { get; }
return this.CurrentToken.Kind == SyntaxKind.ExternKeyword
&& this.PeekToken(1).Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).ContextualKind == SyntaxKind.AliasKeyword
&& this.PeekToken(2).Kind == SyntaxKind.IdentifierToken
&& this.PeekToken(3).Kind == SyntaxKind.SemicolonToken;
}
private ExternAliasDirectiveSyntax ParseExternAliasDirective()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.ExternAliasDirective)
{
return (ExternAliasDirectiveSyntax)this.EatNode();
}
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ExternKeyword);
var externToken = this.EatToken(SyntaxKind.ExternKeyword);
var aliasToken = this.EatContextualToken(SyntaxKind.AliasKeyword);
externToken = CheckFeatureAvailability(externToken, MessageID.IDS_FeatureExternAlias);
var name = this.ParseIdentifierToken();
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.ExternAliasDirective(externToken, aliasToken, name, semicolon);
}
private NameEqualsSyntax ParseNameEquals(bool warnOnGlobal = false)
{
Debug.Assert(this.IsNamedAssignment());
var id = this.ParseIdentifierToken();
var equals = this.EatToken(SyntaxKind.EqualsToken);
// Warn on "using global = X".
if (warnOnGlobal && IsNameGlobal(id))
{
id = this.AddError(id, ErrorCode.WRN_GlobalAliasDefn);
}
return _syntaxFactory.NameEquals(_syntaxFactory.IdentifierName(id), equals);
}
private UsingDirectiveSyntax ParseUsingDirective()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.UsingDirective)
{
return (UsingDirectiveSyntax)this.EatNode();
}
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.UsingKeyword);
var usingToken = this.EatToken(SyntaxKind.UsingKeyword);
var staticToken = default(SyntaxToken);
if (this.CurrentToken.Kind == SyntaxKind.StaticKeyword)
{
staticToken = this.EatToken(SyntaxKind.StaticKeyword);
}
NameEqualsSyntax alias = null;
if (this.IsNamedAssignment())
{
alias = ParseNameEquals(warnOnGlobal: true);
}
NameSyntax name;
SyntaxToken semicolon;
if (IsPossibleNamespaceMemberDeclaration())
{
//We're worried about the case where someone already has a correct program
//and they've gone back to add a using directive, but have not finished the
//new directive. e.g.
//
// using
// namespace Foo {
// //...
// }
//
//If the token we see after "using" could be its own top-level construct, then
//we just want to insert a missing identifier and semicolon and then return to
//parsing at the top-level.
//
//NB: there's no way this could be true for a set of tokens that form a valid
//using directive, so there's no danger in checking the error case first.
name = WithAdditionalDiagnostics(CreateMissingIdentifierName(), GetExpectedTokenError(SyntaxKind.IdentifierToken, this.CurrentToken.Kind));
semicolon = SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken);
}
else
{
name = this.ParseQualifiedName();
if (name.IsMissing && this.PeekToken(1).Kind == SyntaxKind.SemicolonToken)
{
//if we can see a semicolon ahead, then the current token was
//probably supposed to be an identifier
name = AddTrailingSkippedSyntax(name, this.EatToken());
}
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
var usingDirective = _syntaxFactory.UsingDirective(usingToken, staticToken, alias, name, semicolon);
if (staticToken != default(SyntaxToken))
{
usingDirective = CheckFeatureAvailability(usingDirective, MessageID.IDS_FeatureUsingStatic);
}
return usingDirective;
}
private bool IsPossibleGlobalAttributeDeclaration()
{
return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken
&& IsGlobalAttributeTarget(this.PeekToken(1))
&& this.PeekToken(2).Kind == SyntaxKind.ColonToken;
}
private static bool IsGlobalAttributeTarget(SyntaxToken token)
{
switch (token.ToAttributeLocation())
{
case AttributeLocation.Assembly:
case AttributeLocation.Module:
return true;
default:
return false;
}
}
private bool IsPossibleAttributeDeclaration()
{
return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken;
}
private void ParseAttributeDeclarations(SyntaxListBuilder list, bool allowAttributes = true)
{
var saveTerm = _termState;
_termState |= TerminatorState.IsAttributeDeclarationTerminator;
while (this.IsPossibleAttributeDeclaration())
{
var section = this.ParseAttributeDeclaration();
if (!allowAttributes)
{
section = this.AddError(section, ErrorCode.ERR_AttributesNotAllowed);
}
list.Add(section);
}
_termState = saveTerm;
}
private bool IsAttributeDeclarationTerminator()
{
return this.CurrentToken.Kind == SyntaxKind.CloseBracketToken
|| this.IsPossibleAttributeDeclaration(); // start of a new one...
}
private AttributeListSyntax ParseAttributeDeclaration()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.AttributeList)
{
return (AttributeListSyntax)this.EatNode();
}
var openBracket = this.EatToken(SyntaxKind.OpenBracketToken);
// Check for optional location :
AttributeTargetSpecifierSyntax attrLocation = null;
if (IsSomeWord(this.CurrentToken.Kind) && this.PeekToken(1).Kind == SyntaxKind.ColonToken)
{
var id = ConvertToKeyword(this.EatToken());
var colon = this.EatToken(SyntaxKind.ColonToken);
attrLocation = _syntaxFactory.AttributeTargetSpecifier(id, colon);
}
var attributes = _pool.AllocateSeparated<AttributeSyntax>();
try
{
if (attrLocation != null && attrLocation.Identifier.ToAttributeLocation() == AttributeLocation.Module)
{
attrLocation = CheckFeatureAvailability(attrLocation, MessageID.IDS_FeatureModuleAttrLoc);
}
this.ParseAttributes(attributes);
var closeBracket = this.EatToken(SyntaxKind.CloseBracketToken);
var declaration = _syntaxFactory.AttributeList(openBracket, attrLocation, attributes, closeBracket);
return declaration;
}
finally
{
_pool.Free(attributes);
}
}
private void ParseAttributes(SeparatedSyntaxListBuilder<AttributeSyntax> nodes)
{
// always expect at least one attribute
nodes.Add(this.ParseAttribute());
// remaining attributes
while (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken)
{
if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// comma is optional, but if it is here it should be followed by another attribute
nodes.AddSeparator(this.EatToken());
// check for legal trailing comma
if (this.CurrentToken.Kind == SyntaxKind.CloseBracketToken)
{
break;
}
nodes.Add(this.ParseAttribute());
}
else if (this.IsPossibleAttribute())
{
nodes.AddSeparator(SyntaxFactory.MissingToken(SyntaxKind.CommaToken));
nodes.Add(this.ParseAttribute());
}
else if (this.SkipBadAttributeListTokens(nodes, SyntaxKind.IdentifierToken) == PostSkipAction.Abort)
{
break;
}
}
}
private PostSkipAction SkipBadAttributeListTokens(SeparatedSyntaxListBuilder<AttributeSyntax> list, SyntaxKind expected)
{
Debug.Assert(list.Count > 0);
SyntaxToken tmp = null;
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleAttribute(),
p => p.CurrentToken.Kind == SyntaxKind.CloseBracketToken || p.IsTerminator(),
expected);
}
private bool IsPossibleAttribute()
{
return this.IsTrueIdentifier();
}
private AttributeSyntax ParseAttribute()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.Attribute)
{
return (AttributeSyntax)this.EatNode();
}
var name = this.ParseQualifiedName();
var argList = this.ParseAttributeArgumentList();
return _syntaxFactory.Attribute(name, argList);
}
internal AttributeArgumentListSyntax ParseAttributeArgumentList()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.AttributeArgumentList)
{
return (AttributeArgumentListSyntax)this.EatNode();
}
AttributeArgumentListSyntax argList = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var argNodes = _pool.AllocateSeparated<AttributeArgumentSyntax>();
try
{
bool shouldHaveName = false;
tryAgain:
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
if (this.IsPossibleAttributeArgument() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first argument
argNodes.Add(this.ParseAttributeArgument(ref shouldHaveName));
// comma + argument or end?
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleAttributeArgument())
{
argNodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
argNodes.Add(this.ParseAttributeArgument(ref shouldHaveName));
}
else if (this.SkipBadAttributeArgumentTokens(ref openParen, argNodes, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadAttributeArgumentTokens(ref openParen, argNodes, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
argList = _syntaxFactory.AttributeArgumentList(openParen, argNodes, closeParen);
}
finally
{
_pool.Free(argNodes);
}
}
return argList;
}
private PostSkipAction SkipBadAttributeArgumentTokens(ref SyntaxToken openParen, SeparatedSyntaxListBuilder<AttributeArgumentSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref openParen, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleAttributeArgument(),
p => p.CurrentToken.Kind == SyntaxKind.CloseParenToken || p.IsTerminator(),
expected);
}
private bool IsPossibleAttributeArgument()
{
return this.IsPossibleExpression();
}
private AttributeArgumentSyntax ParseAttributeArgument(ref bool shouldHaveName)
{
// Need to parse both "real" named arguments and attribute-style named arguments.
// We track attribute-style named arguments only with fShouldHaveName.
NameEqualsSyntax nameEquals = null;
NameColonSyntax nameColon = null;
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
SyntaxKind nextTokenKind = this.PeekToken(1).Kind;
switch (nextTokenKind)
{
case SyntaxKind.EqualsToken:
{
var name = this.ParseIdentifierToken();
var equals = this.EatToken(SyntaxKind.EqualsToken);
nameEquals = _syntaxFactory.NameEquals(_syntaxFactory.IdentifierName(name), equals);
shouldHaveName = true;
}
break;
case SyntaxKind.ColonToken:
{
var name = this.ParseIdentifierName();
var colonToken = this.EatToken(SyntaxKind.ColonToken);
nameColon = _syntaxFactory.NameColon(name, colonToken);
nameColon = CheckFeatureAvailability(nameColon, MessageID.IDS_FeatureNamedArgument);
}
break;
}
}
var expr = this.ParseExpressionCore();
// Not named -- give an error if it's supposed to be
if (shouldHaveName && nameEquals == null)
{
expr = this.AddError(expr, ErrorCode.ERR_NamedArgumentExpected);
}
return _syntaxFactory.AttributeArgument(nameEquals, nameColon, expr);
}
[Flags]
private enum SyntaxModifier
{
None = 0,
Public = 0x0001,
Internal = 0x0002,
Protected = 0x0004,
Private = 0x0008,
Sealed = 0x0010,
Abstract = 0x0020,
Static = 0x0040,
Virtual = 0x0080,
Extern = 0x0100,
New = 0x0200,
Override = 0x0400,
ReadOnly = 0x0800,
Volatile = 0x1000,
Unsafe = 0x2000,
Partial = 0x4000,
Async = 0x8000
}
private const SyntaxModifier AccessModifiers = SyntaxModifier.Public | SyntaxModifier.Internal | SyntaxModifier.Protected | SyntaxModifier.Private;
private static SyntaxModifier GetModifier(SyntaxToken token)
{
switch (token.Kind)
{
case SyntaxKind.PublicKeyword:
return SyntaxModifier.Public;
case SyntaxKind.InternalKeyword:
return SyntaxModifier.Internal;
case SyntaxKind.ProtectedKeyword:
return SyntaxModifier.Protected;
case SyntaxKind.PrivateKeyword:
return SyntaxModifier.Private;
case SyntaxKind.SealedKeyword:
return SyntaxModifier.Sealed;
case SyntaxKind.AbstractKeyword:
return SyntaxModifier.Abstract;
case SyntaxKind.StaticKeyword:
return SyntaxModifier.Static;
case SyntaxKind.VirtualKeyword:
return SyntaxModifier.Virtual;
case SyntaxKind.ExternKeyword:
return SyntaxModifier.Extern;
case SyntaxKind.NewKeyword:
return SyntaxModifier.New;
case SyntaxKind.OverrideKeyword:
return SyntaxModifier.Override;
case SyntaxKind.ReadOnlyKeyword:
return SyntaxModifier.ReadOnly;
case SyntaxKind.VolatileKeyword:
return SyntaxModifier.Volatile;
case SyntaxKind.UnsafeKeyword:
return SyntaxModifier.Unsafe;
case SyntaxKind.IdentifierToken:
switch (token.ContextualKind)
{
case SyntaxKind.PartialKeyword:
return SyntaxModifier.Partial;
case SyntaxKind.AsyncKeyword:
return SyntaxModifier.Async;
}
goto default;
default:
return SyntaxModifier.None;
}
}
private static SyntaxModifier GetFieldModifier(SyntaxToken token)
{
switch (token.Kind)
{
case SyntaxKind.PublicKeyword:
return SyntaxModifier.Public;
case SyntaxKind.InternalKeyword:
return SyntaxModifier.Internal;
case SyntaxKind.ProtectedKeyword:
return SyntaxModifier.Protected;
case SyntaxKind.PrivateKeyword:
return SyntaxModifier.Private;
case SyntaxKind.StaticKeyword:
return SyntaxModifier.Static;
case SyntaxKind.NewKeyword:
return SyntaxModifier.New;
case SyntaxKind.ReadOnlyKeyword:
return SyntaxModifier.ReadOnly;
case SyntaxKind.VolatileKeyword:
return SyntaxModifier.Volatile;
default:
return SyntaxModifier.None;
}
}
private bool IsPossibleModifier()
{
return IsPossibleModifier(this.CurrentToken);
}
private bool IsPossibleModifier(SyntaxToken token)
{
return GetModifier(token) != SyntaxModifier.None;
}
private void ParseModifiers(SyntaxListBuilder tokens)
{
SyntaxModifier mods = 0;
bool seenNoDuplicates = true;
bool seenNoAccessibilityDuplicates = true;
while (true)
{
var newMod = GetModifier(this.CurrentToken);
if (newMod == SyntaxModifier.None)
{
break;
}
SyntaxToken modTok;
switch (newMod)
{
case SyntaxModifier.Partial:
{
var nextToken = PeekToken(1);
if (this.IsPartialType())
{
modTok = ConvertToKeyword(this.EatToken());
modTok = CheckFeatureAvailability(modTok, MessageID.IDS_FeaturePartialTypes);
}
else if (this.IsPartialMember())
{
modTok = ConvertToKeyword(this.EatToken());
modTok = CheckFeatureAvailability(modTok, MessageID.IDS_FeaturePartialMethod);
}
else if (nextToken.Kind == SyntaxKind.NamespaceKeyword)
{
goto default;
}
else if (nextToken.Kind == SyntaxKind.EnumKeyword || nextToken.Kind == SyntaxKind.DelegateKeyword)
{
modTok = ConvertToKeyword(this.EatToken());
modTok = this.AddError(modTok, ErrorCode.ERR_PartialMisplaced);
}
else if (!IsPossibleStartOfTypeDeclaration(nextToken.Kind) || GetModifier(nextToken) == SyntaxModifier.None)
{
return;
}
else
{
modTok = ConvertToKeyword(this.EatToken());
modTok = this.AddError(modTok, ErrorCode.ERR_PartialMisplaced);
}
break;
}
case SyntaxModifier.Async:
{
// Adapted from CParser::IsAsyncMethod.
var nextToken = PeekToken(1);
if (GetModifier(nextToken) != SyntaxModifier.None && !SyntaxFacts.IsContextualKeyword(nextToken.ContextualKind))
{
// If the next token is a (non-contextual) modifier keyword, then this token is
// definitely the async keyword
modTok = ConvertToKeyword(this.EatToken());
modTok = CheckFeatureAvailability(modTok, MessageID.IDS_FeatureAsync);
break;
}
bool isModifier = false;
// Some of our helpers start at the current token, so we'll have to advance for their
// sake and then backtrack when we're done. Don't leave this block without releasing
// the reset point.
{
ResetPoint resetPoint = GetResetPoint();
this.EatToken(); //move past "async"
if (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)
{
this.EatToken(); // "partial" doesn't affect our decision, so look past it.
}
// Comment directly from CParser::IsAsyncMethod.
// ... 'async' [partial] <typedecl> ...
// ... 'async' [partial] <event> ...
// ... 'async' [partial] <implicit> <operator> ...
// ... 'async' [partial] <explicit> <operator> ...
// ... 'async' [partial] <typename> <operator> ...
// ... 'async' [partial] <typename> <membername> ...
// DEVNOTE: Although we parse async user defined conversions, operators, etc. here,
// anything other than async methods are detected as erroneous later, during the define phase
SyntaxToken currToken = this.CurrentToken;
if (IsPossibleStartOfTypeDeclaration(currToken.Kind) ||
currToken.Kind == SyntaxKind.EventKeyword ||
((currToken.Kind == SyntaxKind.ExplicitKeyword || currToken.Kind == SyntaxKind.ImplicitKeyword) && PeekToken(1).Kind == SyntaxKind.OperatorKeyword) ||
(ScanType() != ScanTypeFlags.NotType && (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword || IsPossibleMemberName())))
{
isModifier = true;
}
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
if (isModifier)
{
modTok = ConvertToKeyword(this.EatToken());
modTok = CheckFeatureAvailability(modTok, MessageID.IDS_FeatureAsync);
break;
}
else
{
return;
}
}
default:
{
modTok = this.EatToken();
break;
}
}
ReportDuplicateModifiers(ref modTok, newMod, mods, ref seenNoDuplicates, ref seenNoAccessibilityDuplicates);
mods |= newMod;
tokens.Add(modTok);
}
}
private void ReportDuplicateModifiers(ref SyntaxToken modTok, SyntaxModifier newMod, SyntaxModifier mods, ref bool seenNoDuplicates, ref bool seenNoAccessibilityDuplicates)
{
if ((mods & newMod) != 0)
{
if (seenNoDuplicates)
{
modTok = this.AddError(modTok, ErrorCode.ERR_DuplicateModifier, SyntaxFacts.GetText(modTok.Kind));
seenNoDuplicates = false;
}
}
else
{
if ((mods & AccessModifiers) != 0 && (newMod & AccessModifiers) != 0)
{
// Can't have two different access modifiers.
// Exception: "internal protected" or "protected internal" is allowed.
if (!(((newMod == SyntaxModifier.Protected) && (mods & SyntaxModifier.Internal) != 0) ||
((newMod == SyntaxModifier.Internal) && (mods & SyntaxModifier.Protected) != 0)))
{
if (seenNoAccessibilityDuplicates)
{
modTok = this.AddError(modTok, ErrorCode.ERR_BadMemberProtection);
}
seenNoAccessibilityDuplicates = false;
}
}
}
}
private bool IsPartialType()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword);
switch (this.PeekToken(1).Kind)
{
case SyntaxKind.StructKeyword:
case SyntaxKind.ClassKeyword:
case SyntaxKind.InterfaceKeyword:
return true;
}
return false;
}
private bool IsPartialMember()
{
// note(cyrusn): this could have been written like so:
//
// return
// this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword &&
// this.PeekToken(1).Kind == SyntaxKind.VoidKeyword;
//
// However, we want to be lenient and allow the user to write
// 'partial' in most modifier lists. We will then provide them with
// a more specific message later in binding that they are doing
// something wrong.
//
// Some might argue that the simple check would suffice.
// However, we'd like to maintain behavior with
// previously shipped versions, and so we're keeping this code.
// Here we check for:
// partial ReturnType MemberName
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword);
var point = this.GetResetPoint();
try
{
this.EatToken(); // partial
if (this.ScanType() == ScanTypeFlags.NotType)
{
return false;
}
return IsPossibleMemberName();
}
finally
{
this.Reset(ref point);
this.Release(ref point);
}
}
private bool IsPossibleMemberName()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.IdentifierToken:
case SyntaxKind.ThisKeyword:
return true;
default:
return false;
}
}
private static bool CanReuseTypeDeclaration(CSharp.Syntax.MemberDeclarationSyntax member)
{
if (member != null)
{
// on reuse valid type declaration (not bad namespace members)
switch (member.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.DelegateDeclaration:
return true;
}
}
return false;
}
private MemberDeclarationSyntax ParseTypeDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
cancellationToken.ThrowIfCancellationRequested();
switch (this.CurrentToken.Kind)
{
case SyntaxKind.ClassKeyword:
// report use of static class
for (int i = 0, n = modifiers.Count; i < n; i++)
{
if (modifiers[i].Kind == SyntaxKind.StaticKeyword)
{
modifiers[i] = CheckFeatureAvailability(modifiers[i], MessageID.IDS_FeatureStaticClasses);
}
}
return this.ParseClassOrStructOrInterfaceDeclaration(attributes, modifiers);
case SyntaxKind.StructKeyword:
case SyntaxKind.InterfaceKeyword:
return this.ParseClassOrStructOrInterfaceDeclaration(attributes, modifiers);
case SyntaxKind.DelegateKeyword:
return this.ParseDelegateDeclaration(attributes, modifiers);
case SyntaxKind.EnumKeyword:
return this.ParseEnumDeclaration(attributes, modifiers);
default:
throw ExceptionUtilities.UnexpectedValue(this.CurrentToken.Kind);
}
}
private static bool IsMissingName(NameSyntax name)
{
return name.Kind == SyntaxKind.IdentifierName && ((IdentifierNameSyntax)name).Identifier.IsMissing;
}
private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ClassKeyword || this.CurrentToken.Kind == SyntaxKind.StructKeyword || this.CurrentToken.Kind == SyntaxKind.InterfaceKeyword);
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
var classOrStructOrInterface = this.EatToken();
var saveTerm = _termState;
_termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop;
var name = this.ParseIdentifierToken();
var typeParameters = this.ParseTypeParameterList(allowVariance: classOrStructOrInterface.Kind == SyntaxKind.InterfaceKeyword);
_termState = saveTerm;
bool hasTypeParams = typeParameters != null;
var baseList = this.ParseBaseList();
// Parse class body
bool parseMembers = true;
SyntaxListBuilder<MemberDeclarationSyntax> members = default(SyntaxListBuilder<MemberDeclarationSyntax>);
var constraints = default(SyntaxListBuilder<TypeParameterConstraintClauseSyntax>);
try
{
if (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
constraints = _pool.Allocate<TypeParameterConstraintClauseSyntax>();
this.ParseTypeParameterConstraintClauses(hasTypeParams, constraints);
}
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
// ignore members if missing type name or missing open curly
if (name.IsMissing || openBrace.IsMissing)
{
parseMembers = false;
}
// even if we saw a { or think we should parse members bail out early since
// we know namespaces can't be nested inside types
if (parseMembers)
{
members = _pool.Allocate<MemberDeclarationSyntax>();
while (true)
{
SyntaxKind kind = this.CurrentToken.Kind;
if (CanStartMember(kind))
{
// This token can start a member -- go parse it
var saveTerm2 = _termState;
_termState |= TerminatorState.IsPossibleMemberStartOrStop;
var memberOrStatement = this.ParseMemberDeclarationOrStatement(kind, name.ValueText);
if (memberOrStatement != null)
{
// statements are accepted here, a semantic error will be reported later
members.Add(memberOrStatement);
}
else
{
// we get here if we couldn't parse the lookahead as a statement or a declaration (we haven't consumed any tokens):
this.SkipBadMemberListTokens(ref openBrace, members);
}
_termState = saveTerm2;
}
else if (kind == SyntaxKind.CloseBraceToken || kind == SyntaxKind.EndOfFileToken || this.IsTerminator())
{
// This marks the end of members of this class
break;
}
else
{
// Error -- try to sync up with intended reality
this.SkipBadMemberListTokens(ref openBrace, members);
}
}
}
SyntaxToken closeBrace;
if (openBrace.IsMissing)
{
closeBrace = SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken);
closeBrace = WithAdditionalDiagnostics(closeBrace, this.GetExpectedTokenError(SyntaxKind.CloseBraceToken, this.CurrentToken.Kind));
}
else
{
closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
}
SyntaxToken semicolon = null;
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatToken();
}
switch (classOrStructOrInterface.Kind)
{
case SyntaxKind.ClassKeyword:
return _syntaxFactory.ClassDeclaration(
attributes,
modifiers.ToTokenList(),
classOrStructOrInterface,
name,
typeParameters,
baseList,
constraints,
openBrace,
members,
closeBrace,
semicolon);
case SyntaxKind.StructKeyword:
return _syntaxFactory.StructDeclaration(
attributes,
modifiers.ToTokenList(),
classOrStructOrInterface,
name,
typeParameters,
baseList,
constraints,
openBrace,
members,
closeBrace,
semicolon);
case SyntaxKind.InterfaceKeyword:
return _syntaxFactory.InterfaceDeclaration(
attributes,
modifiers.ToTokenList(),
classOrStructOrInterface,
name,
typeParameters,
baseList,
constraints,
openBrace,
members,
closeBrace,
semicolon);
default:
throw ExceptionUtilities.UnexpectedValue(classOrStructOrInterface.Kind);
}
}
finally
{
if (!members.IsNull)
{
_pool.Free(members);
}
if (!constraints.IsNull)
{
_pool.Free(constraints);
}
}
}
private void SkipBadMemberListTokens(ref SyntaxToken openBrace, SyntaxListBuilder members)
{
if (members.Count > 0)
{
CSharpSyntaxNode tmp = members[members.Count - 1];
this.SkipBadMemberListTokens(ref tmp);
members[members.Count - 1] = tmp;
}
else
{
CSharpSyntaxNode tmp = openBrace;
this.SkipBadMemberListTokens(ref tmp);
openBrace = (SyntaxToken)tmp;
}
}
private void SkipBadMemberListTokens(ref CSharpSyntaxNode previousNode)
{
int curlyCount = 0;
var tokens = _pool.Allocate();
try
{
bool done = false;
while (!done)
{
SyntaxKind kind = this.CurrentToken.Kind;
// If this token can start a member, we're done
if (CanStartMember(kind) &&
!(kind == SyntaxKind.DelegateKeyword && (this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken || this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)))
{
done = true;
continue;
}
// <UNDONE> UNDONE: Seems like this makes sense,
// but if this token can start a namespace element, but not a member, then
// perhaps we should bail back up to parsing a namespace body somehow...</UNDONE>
// Watch curlies and look for end of file/close curly
switch (kind)
{
case SyntaxKind.OpenBraceToken:
curlyCount++;
break;
case SyntaxKind.CloseBraceToken:
if (curlyCount-- == 0)
{
done = true;
continue;
}
break;
case SyntaxKind.EndOfFileToken:
done = true;
continue;
default:
break;
}
var token = this.EatToken();
if (tokens.Count == 0)
{
token = this.AddError(token, ErrorCode.ERR_InvalidMemberDecl, token.Text);
}
tokens.Add(token);
}
previousNode = AddTrailingSkippedSyntax(previousNode, tokens.ToListNode());
}
finally
{
_pool.Free(tokens);
}
}
private bool IsPossibleMemberStartOrStop()
{
return this.IsPossibleMemberStart() || this.CurrentToken.Kind == SyntaxKind.CloseBraceToken;
}
private bool IsPossibleAggregateClauseStartOrStop()
{
return this.CurrentToken.Kind == SyntaxKind.ColonToken
|| this.IsPossibleTypeParameterConstraintClauseStart()
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken;
}
private BaseListSyntax ParseBaseList()
{
if (this.CurrentToken.Kind != SyntaxKind.ColonToken)
{
return null;
}
var colon = this.EatToken();
var list = _pool.AllocateSeparated<BaseTypeSyntax>();
try
{
// first type
if (this.IsPossibleTypeParameterConstraintClauseStart())
{
list.Add(_syntaxFactory.SimpleBaseType(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected)));
}
else
{
TypeSyntax firstType = this.ParseDeclarationType(isConstraint: false, parentIsParameter: false);
list.Add(_syntaxFactory.SimpleBaseType(firstType));
// any additional types
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.IsPossibleTypeParameterConstraintClauseStart())
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
if (this.IsPossibleTypeParameterConstraintClauseStart())
{
list.Add(_syntaxFactory.SimpleBaseType(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected)));
}
else
{
list.Add(_syntaxFactory.SimpleBaseType(this.ParseDeclarationType(isConstraint: false, parentIsParameter: false)));
}
continue;
}
else if (this.SkipBadBaseListTokens(ref colon, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
return _syntaxFactory.BaseList(colon, list);
}
finally
{
_pool.Free(list);
}
}
private PostSkipAction SkipBadBaseListTokens(ref SyntaxToken colon, SeparatedSyntaxListBuilder<BaseTypeSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref colon, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleAttribute(),
p => p.CurrentToken.Kind == SyntaxKind.OpenBraceToken || p.IsPossibleTypeParameterConstraintClauseStart() || p.IsTerminator(),
expected);
}
private bool IsPossibleTypeParameterConstraintClauseStart()
{
return
this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword &&
this.PeekToken(1).Kind == SyntaxKind.IdentifierToken &&
this.PeekToken(2).Kind == SyntaxKind.ColonToken;
}
private void ParseTypeParameterConstraintClauses(bool isAllowed, SyntaxListBuilder list)
{
while (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
var constraint = this.ParseTypeParameterConstraintClause();
if (!isAllowed)
{
constraint = this.AddErrorToFirstToken(constraint, ErrorCode.ERR_ConstraintOnlyAllowedOnGenericDecl);
isAllowed = true; // silence any further errors
}
list.Add(constraint);
}
}
private TypeParameterConstraintClauseSyntax ParseTypeParameterConstraintClause()
{
var where = this.EatContextualToken(SyntaxKind.WhereKeyword);
var name = (this.IsPossibleTypeParameterConstraintClauseStart() || !IsTrueIdentifier())
? this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_IdentifierExpected)
: this.ParseIdentifierName();
var colon = this.EatToken(SyntaxKind.ColonToken);
var bounds = _pool.AllocateSeparated<TypeParameterConstraintSyntax>();
try
{
bool isStruct = false;
// first bound
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.IsPossibleTypeParameterConstraintClauseStart())
{
bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected)));
}
else
{
bounds.Add(this.ParseTypeParameterConstraint(true, ref isStruct));
// remaining bounds
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken
|| this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleTypeParameterConstraint())
{
bounds.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
if (this.IsPossibleTypeParameterConstraintClauseStart())
{
bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected)));
break;
}
else
{
bounds.Add(this.ParseTypeParameterConstraint(false, ref isStruct));
}
}
else if (this.SkipBadTypeParameterConstraintTokens(bounds, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
return _syntaxFactory.TypeParameterConstraintClause(where, name, colon, bounds);
}
finally
{
_pool.Free(bounds);
}
}
private bool IsPossibleTypeParameterConstraint()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.NewKeyword:
case SyntaxKind.ClassKeyword:
case SyntaxKind.StructKeyword:
return true;
case SyntaxKind.IdentifierToken:
return this.IsTrueIdentifier();
default:
return IsPredefinedType(this.CurrentToken.Kind);
}
}
private TypeParameterConstraintSyntax ParseTypeParameterConstraint(bool isFirst, ref bool isStruct)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.NewKeyword:
var newToken = this.EatToken();
if (isStruct)
{
newToken = this.AddError(newToken, ErrorCode.ERR_NewBoundWithVal);
}
var open = this.EatToken(SyntaxKind.OpenParenToken);
var close = this.EatToken(SyntaxKind.CloseParenToken);
if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
newToken = this.AddError(newToken, ErrorCode.ERR_NewBoundMustBeLast);
}
return _syntaxFactory.ConstructorConstraint(newToken, open, close);
case SyntaxKind.StructKeyword:
isStruct = true;
goto case SyntaxKind.ClassKeyword;
case SyntaxKind.ClassKeyword:
var token = this.EatToken();
if (!isFirst)
{
token = this.AddError(token, ErrorCode.ERR_RefValBoundMustBeFirst);
}
return _syntaxFactory.ClassOrStructConstraint(isStruct ? SyntaxKind.StructConstraint : SyntaxKind.ClassConstraint, token);
default:
var type = this.ParseDeclarationType(true, false);
return _syntaxFactory.TypeConstraint(type);
}
}
private PostSkipAction SkipBadTypeParameterConstraintTokens(SeparatedSyntaxListBuilder<TypeParameterConstraintSyntax> list, SyntaxKind expected)
{
CSharpSyntaxNode tmp = null;
Debug.Assert(list.Count > 0);
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => this.CurrentToken.Kind != SyntaxKind.CommaToken && !this.IsPossibleTypeParameterConstraint(),
p => this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.IsPossibleTypeParameterConstraintClauseStart() || this.IsTerminator(),
expected);
}
private TypeSyntax ParseDeclarationType(bool isConstraint, bool parentIsParameter)
{
var type = this.ParseType(parentIsParameter);
if (type.Kind != SyntaxKind.PredefinedType && !SyntaxFacts.IsName(type.Kind))
{
if (isConstraint)
{
type = this.AddError(type, ErrorCode.ERR_BadConstraintType);
}
else
{
type = this.AddError(type, ErrorCode.ERR_BadBaseType);
}
}
return type;
}
private bool IsPossibleMemberStart()
{
return CanStartMember(this.CurrentToken.Kind);
}
private static bool CanStartMember(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.AbstractKeyword:
case SyntaxKind.BoolKeyword:
case SyntaxKind.ByteKeyword:
case SyntaxKind.CharKeyword:
case SyntaxKind.ClassKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DecimalKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.DoubleKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.EventKeyword:
case SyntaxKind.ExternKeyword:
case SyntaxKind.FixedKeyword:
case SyntaxKind.FloatKeyword:
case SyntaxKind.IntKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.InternalKeyword:
case SyntaxKind.LongKeyword:
case SyntaxKind.NewKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.OverrideKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.SByteKeyword:
case SyntaxKind.SealedKeyword:
case SyntaxKind.ShortKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.StructKeyword:
case SyntaxKind.UIntKeyword:
case SyntaxKind.ULongKeyword:
case SyntaxKind.UnsafeKeyword:
case SyntaxKind.UShortKeyword:
case SyntaxKind.VirtualKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.VolatileKeyword:
case SyntaxKind.IdentifierToken:
case SyntaxKind.TildeToken:
case SyntaxKind.OpenBracketToken:
case SyntaxKind.ImplicitKeyword:
case SyntaxKind.ExplicitKeyword:
return true;
default:
return false;
}
}
private static bool CanStartTypeDeclaration(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.ClassKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.StructKeyword:
return true;
default:
return false;
}
}
private static bool CanReuseMemberDeclaration(
CSharp.Syntax.MemberDeclarationSyntax member,
string typeName)
{
if (member != null)
{
switch (member.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.FieldDeclaration:
case SyntaxKind.EventFieldDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.EventDeclaration:
case SyntaxKind.IndexerDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
return true;
}
var parent = GetOldParent(member);
var originalTypeDeclaration = parent as CSharp.Syntax.TypeDeclarationSyntax;
// originalTypeDeclaration can be null in the case of script code. In that case
// the member declaration can be a child of a namespace/compilation-unit instead of
// a type.
if (originalTypeDeclaration != null)
{
switch (member.Kind())
{
case SyntaxKind.MethodDeclaration:
// can reuse a method as long as it *doesn't* match the type name.
//
// TODO(cyrusn): Relax this in the case of generic methods?
var methodDeclaration = (CSharp.Syntax.MethodDeclarationSyntax)member;
return methodDeclaration.Identifier.ValueText != typeName;
case SyntaxKind.ConstructorDeclaration: // fall through
case SyntaxKind.DestructorDeclaration:
// can reuse constructors or destructors if the name and type name still
// match.
return originalTypeDeclaration.Identifier.ValueText == typeName;
}
}
}
return false;
}
// Returns null if we can't parse anything (even partially).
private MemberDeclarationSyntax ParseMemberDeclarationOrStatement(SyntaxKind parentKind, string typeName = null)
{
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
cancellationToken.ThrowIfCancellationRequested();
bool isGlobalScript = parentKind == SyntaxKind.CompilationUnit && this.IsScript;
bool acceptStatement = isGlobalScript;
// don't reuse members if they were previously declared under a different type keyword kind
// don't reuse existing constructors & destructors because they have to match typename errors
// don't reuse methods whose name matches the new type name (they now match as possible constructors)
if (this.IsIncrementalAndFactoryContextMatches)
{
var member = this.CurrentNode as CSharp.Syntax.MemberDeclarationSyntax;
if (CanReuseMemberDeclaration(member, typeName) || CanReuseTypeDeclaration(member))
{
return (MemberDeclarationSyntax)this.EatNode();
}
}
var attributes = _pool.Allocate<AttributeListSyntax>();
var modifiers = _pool.Allocate();
var saveTermState = _termState;
try
{
this.ParseAttributeDeclarations(attributes);
if (attributes.Count > 0)
{
acceptStatement = false;
}
//
// Check for the following cases to disambiguate between member declarations and expressions.
// Doing this before parsing modifiers simplifies further analysis since some of these keywords can act as modifiers as well.
//
// unsafe { ... }
// fixed (...) { ... }
// delegate (...) { ... }
// delegate { ... }
// new { ... }
// new[] { ... }
// new T (...)
// new T [...]
//
if (acceptStatement)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.UnsafeKeyword:
if (this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken)
{
return _syntaxFactory.GlobalStatement(ParseUnsafeStatement());
}
break;
case SyntaxKind.FixedKeyword:
if (this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)
{
return _syntaxFactory.GlobalStatement(ParseFixedStatement());
}
break;
case SyntaxKind.DelegateKeyword:
switch (this.PeekToken(1).Kind)
{
case SyntaxKind.OpenParenToken:
case SyntaxKind.OpenBraceToken:
return _syntaxFactory.GlobalStatement(ParseExpressionStatement());
}
break;
case SyntaxKind.NewKeyword:
if (IsPossibleNewExpression())
{
return _syntaxFactory.GlobalStatement(ParseExpressionStatement());
}
break;
}
}
// All modifiers that might start an expression are processed above.
this.ParseModifiers(modifiers);
if (modifiers.Count > 0)
{
acceptStatement = false;
}
// Check for constructor form
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)
{
// Script:
// Constructor definitions are not allowed. We parse them as method calls with semicolon missing error:
//
// Script(...) { ... }
// ^
// missing ';'
if (!isGlobalScript && this.CurrentToken.ValueText == typeName)
{
return this.ParseConstructorDeclaration(typeName, attributes, modifiers);
}
// Script:
// Unless there modifiers or attributes are present this is more likely to be a method call than a method definition.
if (!acceptStatement)
{
var token = SyntaxFactory.MissingToken(SyntaxKind.VoidKeyword);
token = this.AddError(token, ErrorCode.ERR_MemberNeedsType);
var voidType = _syntaxFactory.PredefinedType(token);
var identifier = this.EatToken();
return this.ParseMethodDeclaration(attributes, modifiers, voidType, explicitInterfaceOpt: null, identifier: identifier, typeParameterList: null);
}
}
// Check for destructor form
// TODO: better error messages for script
if (!isGlobalScript && this.CurrentToken.Kind == SyntaxKind.TildeToken)
{
return this.ParseDestructorDeclaration(typeName, attributes, modifiers);
}
// Check for constant (prefers const field over const local variable decl)
if (this.CurrentToken.Kind == SyntaxKind.ConstKeyword)
{
return this.ParseConstantFieldDeclaration(attributes, modifiers, parentKind);
}
// Check for event.
if (this.CurrentToken.Kind == SyntaxKind.EventKeyword)
{
return this.ParseEventDeclaration(attributes, modifiers, parentKind);
}
// check for fixed size buffers.
if (this.CurrentToken.Kind == SyntaxKind.FixedKeyword)
{
return this.ParseFixedSizeBufferDeclaration(attributes, modifiers, parentKind);
}
// Check for conversion operators (implicit/explicit)
if (this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword ||
this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword ||
(this.CurrentToken.Kind == SyntaxKind.OperatorKeyword && !SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind)))
{
return this.ParseConversionOperatorDeclaration(attributes, modifiers);
}
if (this.CurrentToken.Kind == SyntaxKind.NamespaceKeyword && parentKind == SyntaxKind.CompilationUnit)
{
// we found a namespace with modifier or an attribute: ignore the attribute/modifier and parse as namespace
if (attributes.Count > 0)
{
attributes[0] = this.AddError(attributes[0], ErrorCode.ERR_BadModifiersOnNamespace);
}
else
{
// if were no attributes and no modifiers we should have parsed it already in namespace body:
Debug.Assert(modifiers.Count > 0);
modifiers[0] = this.AddError(modifiers[0], ErrorCode.ERR_BadModifiersOnNamespace);
}
var namespaceDecl = ParseNamespaceDeclaration();
if (modifiers.Count > 0)
{
namespaceDecl = AddLeadingSkippedSyntax(namespaceDecl, modifiers.ToListNode());
}
if (attributes.Count > 0)
{
namespaceDecl = AddLeadingSkippedSyntax(namespaceDecl, attributes.ToListNode());
}
return namespaceDecl;
}
// It's valid to have a type declaration here -- check for those
if (CanStartTypeDeclaration(this.CurrentToken.Kind))
{
return this.ParseTypeDeclaration(attributes, modifiers);
}
if (acceptStatement &&
this.CurrentToken.Kind != SyntaxKind.CloseBraceToken &&
this.CurrentToken.Kind != SyntaxKind.EndOfFileToken &&
this.IsPossibleStatement())
{
var saveTerm = _termState;
_termState |= TerminatorState.IsPossibleStatementStartOrStop; // partial statements can abort if a new statement starts
// Any expression is allowed, not just expression statements:
var statement = this.ParseStatementNoDeclaration(allowAnyExpression: true);
_termState = saveTerm;
if (statement != null)
{
return _syntaxFactory.GlobalStatement(statement);
}
}
// Everything that's left -- methods, fields, properties,
// indexers, and non-conversion operators -- starts with a type
// (possibly void). Parse one.
var type = this.ParseReturnType();
// <UNDONE> UNDONE: should disallow non-methods with void type here</UNDONE>
// Check for misplaced modifiers. if we see any, then consider this member
// terminated and restart parsing.
if (GetModifier(this.CurrentToken) != SyntaxModifier.None &&
this.CurrentToken.ContextualKind != SyntaxKind.PartialKeyword &&
this.CurrentToken.ContextualKind != SyntaxKind.AsyncKeyword &&
IsComplete(type))
{
var misplacedModifier = this.CurrentToken;
type = this.AddError(
type,
type.FullWidth + misplacedModifier.GetLeadingTriviaWidth(),
misplacedModifier.Width,
ErrorCode.ERR_BadModifierLocation,
misplacedModifier.Text);
return _syntaxFactory.IncompleteMember(attributes, modifiers.ToTokenList(), type);
}
parse_member_name:;
// Check here for operators
// Allow old-style implicit/explicit casting operator syntax, just so we can give a better error
if (IsOperatorKeyword())
{
return this.ParseOperatorDeclaration(attributes, modifiers, type);
}
if (IsFieldDeclaration(isEvent: false))
{
if (acceptStatement)
{
// if we are script at top-level then statements can occur
_termState |= TerminatorState.IsPossibleStatementStartOrStop;
}
return this.ParseNormalFieldDeclaration(attributes, modifiers, type, parentKind);
}
// At this point we can either have indexers, methods, or
// properties (or something unknown). Try to break apart
// the following name and determine what to do from there.
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt;
SyntaxToken identifierOrThisOpt;
TypeParameterListSyntax typeParameterListOpt;
this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterListOpt, isEvent: false);
// First, check if we got absolutely nothing. If so, then
// We need to consume a bad member and try again.
if (explicitInterfaceOpt == null && identifierOrThisOpt == null && typeParameterListOpt == null)
{
if (attributes.Count == 0 && modifiers.Count == 0 && type.IsMissing)
{
// we haven't advanced, the caller needs to consume the tokens ahead
return null;
}
var incompleteMember = _syntaxFactory.IncompleteMember(attributes, modifiers.ToTokenList(), type.IsMissing ? null : type);
if (incompleteMember.ContainsDiagnostics)
{
return incompleteMember;
}
else if (parentKind == SyntaxKind.NamespaceDeclaration ||
parentKind == SyntaxKind.CompilationUnit && !IsScript)
{
return this.AddErrorToLastToken(incompleteMember, ErrorCode.ERR_NamespaceUnexpected);
}
else
{
//the error position should indicate CurrentToken
return this.AddError(
incompleteMember,
incompleteMember.FullWidth + this.CurrentToken.GetLeadingTriviaWidth(),
this.CurrentToken.Width,
ErrorCode.ERR_InvalidMemberDecl,
this.CurrentToken.Text);
}
}
// If the modifiers did not include "async", and the type we got was "async", and there was an
// error in the identifier or its type parameters, then the user is probably in the midst of typing
// an async method. In that case we reconsider "async" to be a modifier, and treat the identifier
// (with the type parameters) as the type (with type arguments). Then we go back to looking for
// the member name again.
// For example, if we get
// async Task<
// then we want async to be a modifier and Task<MISSING> to be a type.
if (identifierOrThisOpt != null &&
(typeParameterListOpt != null && typeParameterListOpt.ContainsDiagnostics
|| this.CurrentToken.Kind != SyntaxKind.OpenParenToken && this.CurrentToken.Kind != SyntaxKind.OpenBraceToken) &&
ReconsiderTypeAsAsyncModifier(ref modifiers, ref type, ref explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt))
{
goto parse_member_name;
}
Debug.Assert(identifierOrThisOpt != null);
if (identifierOrThisOpt.Kind == SyntaxKind.ThisKeyword)
{
return this.ParseIndexerDeclaration(attributes, modifiers, type, explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt);
}
else
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.OpenBraceToken:
case SyntaxKind.EqualsGreaterThanToken:
return this.ParsePropertyDeclaration(attributes, modifiers, type, explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt);
default:
// treat anything else as a method.
return this.ParseMethodDeclaration(attributes, modifiers, type, explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt);
}
}
}
finally
{
_pool.Free(modifiers);
_pool.Free(attributes);
_termState = saveTermState;
}
}
// if the modifiers do not contain async and the type is the identifier "async", then
// add async to the modifiers and assign a new type from the identifierOrThisOpt and the
// type parameter list
private bool ReconsiderTypeAsAsyncModifier(
ref SyntaxListBuilder modifiers,
ref TypeSyntax type,
ref ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt,
SyntaxToken identifierOrThisOpt,
TypeParameterListSyntax typeParameterListOpt)
{
if (modifiers.Any(SyntaxKind.AsyncKeyword)) return false;
if (type.Kind != SyntaxKind.IdentifierName) return false;
if ((((IdentifierNameSyntax)type).Identifier).ContextualKind != SyntaxKind.AsyncKeyword) return false;
if (identifierOrThisOpt.Kind != SyntaxKind.IdentifierToken) return false;
modifiers.Add(ConvertToKeyword(((IdentifierNameSyntax)type).Identifier));
SimpleNameSyntax newType = typeParameterListOpt == null
? (SimpleNameSyntax)_syntaxFactory.IdentifierName(identifierOrThisOpt)
: _syntaxFactory.GenericName(identifierOrThisOpt, TypeArgumentFromTypeParameters(typeParameterListOpt));
type = (explicitInterfaceOpt == null)
? (TypeSyntax)newType
: _syntaxFactory.QualifiedName(explicitInterfaceOpt.Name, explicitInterfaceOpt.DotToken, newType);
explicitInterfaceOpt = null;
identifierOrThisOpt = default(SyntaxToken);
typeParameterListOpt = default(TypeParameterListSyntax);
return true;
}
private TypeArgumentListSyntax TypeArgumentFromTypeParameters(TypeParameterListSyntax typeParameterList)
{
var types = _pool.AllocateSeparated<TypeSyntax>();
foreach (var p in typeParameterList.Parameters.GetWithSeparators())
{
switch (p.Kind)
{
case SyntaxKind.TypeParameter:
var typeParameter = (TypeParameterSyntax)p;
var typeArgument = _syntaxFactory.IdentifierName(typeParameter.Identifier);
// NOTE: reverse order of variance keyword and attributes list so they come out in the right order.
if (typeParameter.VarianceKeyword != null)
{
// This only happens in error scenarios, so don't bother to produce a diagnostic about
// having a variance keyword on a type argument.
typeArgument = AddLeadingSkippedSyntax(typeArgument, typeParameter.VarianceKeyword);
}
if (typeParameter.AttributeLists.Node != null)
{
// This only happens in error scenarios, so don't bother to produce a diagnostic about
// having an attribute on a type argument.
typeArgument = AddLeadingSkippedSyntax(typeArgument, typeParameter.AttributeLists.Node);
}
types.Add(typeArgument);
break;
case SyntaxKind.CommaToken:
types.AddSeparator((SyntaxToken)p);
break;
default:
throw ExceptionUtilities.UnexpectedValue(p.Kind);
}
}
var result = _syntaxFactory.TypeArgumentList(typeParameterList.LessThanToken, types.ToList(), typeParameterList.GreaterThanToken);
_pool.Free(types);
return result;
}
//private bool ReconsiderTypeAsAsyncModifier(ref SyntaxListBuilder modifiers, ref type, ref identifierOrThisOpt, ref typeParameterListOpt))
// {
// goto parse_member_name;
// }
private bool IsFieldDeclaration(bool isEvent)
{
if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken)
{
return false;
}
// Treat this as a field, unless we have anything following that
// makes us:
// a) explicit
// b) generic
// c) a property
// d) a method (unless we already know we're parsing an event)
var kind = this.PeekToken(1).Kind;
switch (kind)
{
case SyntaxKind.DotToken: // Foo. explicit
case SyntaxKind.ColonColonToken: // Foo:: explicit
case SyntaxKind.LessThanToken: // Foo< explicit or generic method
case SyntaxKind.OpenBraceToken: // Foo { property
case SyntaxKind.EqualsGreaterThanToken: // Foo => property
return false;
case SyntaxKind.OpenParenToken: // Foo( method
return isEvent;
default:
return true;
}
}
private bool IsOperatorKeyword()
{
return
this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword ||
this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword ||
this.CurrentToken.Kind == SyntaxKind.OperatorKeyword;
}
public static bool IsComplete(CSharpSyntaxNode node)
{
if (node == null)
{
return false;
}
foreach (var child in node.ChildNodesAndTokens().Reverse())
{
var token = child as SyntaxToken;
if (token == null)
{
return IsComplete((CSharpSyntaxNode)child);
}
if (token.IsMissing)
{
return false;
}
if (token.Kind != SyntaxKind.None)
{
return true;
}
// if token was optional, consider the next one..
}
return true;
}
private ConstructorDeclarationSyntax ParseConstructorDeclaration(string typeName, SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
var name = this.ParseIdentifierToken();
Debug.Assert(name.ValueText == typeName);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfMethodSignature;
try
{
var paramList = this.ParseParenthesizedParameterList(allowThisKeyword: false, allowDefaults: true, allowAttributes: true);
ConstructorInitializerSyntax initializer = null;
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
bool isStatic = modifiers != null && modifiers.Any(SyntaxKind.StaticKeyword);
initializer = this.ParseConstructorInitializer(name.ValueText, isStatic);
}
BlockSyntax body;
SyntaxToken semicolon;
this.ParseBodyOrSemicolon(out body, out semicolon);
return _syntaxFactory.ConstructorDeclaration(attributes, modifiers.ToTokenList(), name, paramList, initializer, body, semicolon);
}
finally
{
_termState = saveTerm;
}
}
private ConstructorInitializerSyntax ParseConstructorInitializer(string name, bool isStatic)
{
var colon = this.EatToken(SyntaxKind.ColonToken);
var reportError = true;
var kind = this.CurrentToken.Kind == SyntaxKind.BaseKeyword
? SyntaxKind.BaseConstructorInitializer
: SyntaxKind.ThisConstructorInitializer;
SyntaxToken token;
if (this.CurrentToken.Kind == SyntaxKind.BaseKeyword || this.CurrentToken.Kind == SyntaxKind.ThisKeyword)
{
token = this.EatToken();
}
else
{
token = this.EatToken(SyntaxKind.ThisKeyword, ErrorCode.ERR_ThisOrBaseExpected);
// No need to report further errors at this point:
reportError = false;
}
ArgumentListSyntax argumentList;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
argumentList = this.ParseParenthesizedArgumentList();
}
else
{
var openToken = this.EatToken(SyntaxKind.OpenParenToken, reportError);
var closeToken = this.EatToken(SyntaxKind.CloseParenToken, reportError);
argumentList = _syntaxFactory.ArgumentList(openToken, default(SeparatedSyntaxList<ArgumentSyntax>), closeToken);
}
if (isStatic)
{
// Static constructor can't have any base call
token = this.AddError(token, ErrorCode.ERR_StaticConstructorWithExplicitConstructorCall, name);
}
return _syntaxFactory.ConstructorInitializer(kind, colon, token, argumentList);
}
private DestructorDeclarationSyntax ParseDestructorDeclaration(string typeName, SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.TildeToken);
var tilde = this.EatToken(SyntaxKind.TildeToken);
var name = this.ParseIdentifierToken();
if (name.ValueText != typeName)
{
name = this.AddError(name, ErrorCode.ERR_BadDestructorName);
}
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
BlockSyntax body;
SyntaxToken semicolon;
this.ParseBodyOrSemicolon(out body, out semicolon);
var parameterList = _syntaxFactory.ParameterList(openParen, default(SeparatedSyntaxList<ParameterSyntax>), closeParen);
return _syntaxFactory.DestructorDeclaration(attributes, modifiers.ToTokenList(), tilde, name, parameterList, body, semicolon);
}
/// <summary>
/// Parses any block or expression bodies that are present. Also parses
/// the trailing semicolon if one is present.
/// </summary>
private void ParseBlockAndExpressionBodiesWithSemicolon(
out BlockSyntax blockBody,
out ArrowExpressionClauseSyntax expressionBody,
out SyntaxToken semicolon)
{
// Check for 'forward' declarations with no block of any kind
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
blockBody = null;
expressionBody = null;
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return;
}
blockBody = null;
expressionBody = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
blockBody = this.ParseBlock(isMethodBody: true);
}
if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken)
{
expressionBody = this.ParseArrowExpressionClause();
expressionBody = CheckFeatureAvailability(expressionBody, MessageID.IDS_FeatureExpressionBodiedMethod);
}
semicolon = null;
// Expression-bodies need semicolons and native behavior
// expects a semicolon if there is no body
if (expressionBody != null || blockBody == null)
{
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
// Check for bad semicolon after block body
else if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatTokenWithPrejudice(ErrorCode.ERR_UnexpectedSemicolon);
}
}
private T CheckForBlockAndExpressionBody<T>(
CSharpSyntaxNode block,
CSharpSyntaxNode expression,
T syntax)
where T : CSharpSyntaxNode
{
if (block != null && expression != null)
{
ErrorCode code;
if (syntax is BaseMethodDeclarationSyntax)
{
code = ErrorCode.ERR_BlockBodyAndExpressionBody;
}
else
{
Debug.Assert(syntax is BasePropertyDeclarationSyntax);
code = ErrorCode.ERR_AccessorListAndExpressionBody;
}
return AddError(syntax, code);
}
return syntax;
}
private void ParseBodyOrSemicolon(out BlockSyntax body, out SyntaxToken semicolon)
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
body = this.ParseBlock(isMethodBody: true);
semicolon = null;
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatTokenWithPrejudice(ErrorCode.ERR_UnexpectedSemicolon);
}
}
else
{
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
body = null;
}
}
private bool IsEndOfTypeParameterList()
{
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
// void Foo<T (
return true;
}
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
// class C<T :
return true;
}
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
// class C<T {
return true;
}
if (IsPossibleTypeParameterConstraintClauseStart())
{
// class C<T where T :
return true;
}
return false;
}
private bool IsEndOfMethodSignature()
{
return this.CurrentToken.Kind == SyntaxKind.SemicolonToken || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken;
}
private bool IsEndOfNameInExplicitInterface()
{
return this.CurrentToken.Kind == SyntaxKind.DotToken || this.CurrentToken.Kind == SyntaxKind.ColonColonToken;
}
private MethodDeclarationSyntax ParseMethodDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
TypeSyntax type,
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt,
SyntaxToken identifier,
TypeParameterListSyntax typeParameterList)
{
// Parse the name (it could be qualified)
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfMethodSignature;
var paramList = this.ParseParenthesizedParameterList(allowThisKeyword: true, allowDefaults: true, allowAttributes: true);
var constraints = default(SyntaxListBuilder<TypeParameterConstraintClauseSyntax>);
try
{
if (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
constraints = _pool.Allocate<TypeParameterConstraintClauseSyntax>();
this.ParseTypeParameterConstraintClauses(typeParameterList != null, constraints);
}
else if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
// Use else if, rather than if, because if we see both a constructor initializer and a constraint clause, we're too lost to recover.
var colonToken = this.CurrentToken;
// Set isStatic to false because pretending we're in a static constructor will just result in more errors.
ConstructorInitializerSyntax initializer = this.ParseConstructorInitializer(identifier.ValueText, isStatic: false);
initializer = this.AddErrorToFirstToken(initializer, ErrorCode.ERR_UnexpectedToken, colonToken.Text);
paramList = AddTrailingSkippedSyntax(paramList, initializer);
// CONSIDER: Parsing an invalid constructor initializer could, conceivably, get us way
// off track. If this becomes a problem, an alternative approach would be to generalize
// EatTokenWithPrejudice in such a way that we can just skip everything until we recognize
// our context again (perhaps an open brace).
}
// When a generic method overrides a generic method declared in a base
// class, or is an explicit interface member implementation of a method in
// a base interface, the method shall not specify any type-parameter-
// constraints-clauses. In these cases, the type parameters of the method
// inherit constraints from the method being overridden or implemented
if (!constraints.IsNull && constraints.Count > 0 &&
((explicitInterfaceOpt != null) || (modifiers != null && modifiers.Any(SyntaxKind.OverrideKeyword))))
{
constraints[0] = this.AddErrorToFirstToken(constraints[0], ErrorCode.ERR_OverrideWithConstraints);
}
_termState = saveTerm;
BlockSyntax blockBody;
ArrowExpressionClauseSyntax expressionBody;
SyntaxToken semicolon;
// Method declarations cannot be nested or placed inside async lambdas, and so cannot occur in an
// asynchronous context. Therefore the IsInAsync state of the parent scope is not saved and
// restored, just assumed to be false and reset accordingly after parsing the method body.
Debug.Assert(!IsInAsync);
IsInAsync = modifiers.Any(SyntaxKind.AsyncKeyword);
this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon);
IsInAsync = false;
var decl = _syntaxFactory.MethodDeclaration(
attributes,
modifiers.ToTokenList(),
type,
explicitInterfaceOpt,
identifier,
typeParameterList,
paramList,
constraints,
blockBody,
expressionBody,
semicolon);
return CheckForBlockAndExpressionBody(blockBody, expressionBody, decl);
}
finally
{
if (!constraints.IsNull)
{
_pool.Free(constraints);
}
}
}
private TypeSyntax ParseReturnType()
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfReturnType;
var type = this.ParseTypeOrVoid();
_termState = saveTerm;
return type;
}
private bool IsEndOfReturnType()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.OpenParenToken:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.SemicolonToken:
return true;
default:
return false;
}
}
private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
SyntaxToken style;
if (this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword)
{
style = this.EatToken();
}
else
{
style = this.EatToken(SyntaxKind.ExplicitKeyword);
}
SyntaxToken opKeyword = this.EatToken(SyntaxKind.OperatorKeyword);
var type = this.ParseType(parentIsParameter: false);
var paramList = this.ParseParenthesizedParameterList(allowThisKeyword: false, allowDefaults: true, allowAttributes: true);
if (paramList.Parameters.Count != 1)
{
paramList = this.AddErrorToFirstToken(paramList, ErrorCode.ERR_OvlUnaryOperatorExpected);
}
BlockSyntax blockBody;
ArrowExpressionClauseSyntax expressionBody;
SyntaxToken semicolon;
this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon);
var decl = _syntaxFactory.ConversionOperatorDeclaration(
attributes,
modifiers.ToTokenList(),
style,
opKeyword,
type,
paramList,
blockBody,
expressionBody,
semicolon);
return CheckForBlockAndExpressionBody(blockBody, expressionBody, decl);
}
private OperatorDeclarationSyntax ParseOperatorDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
TypeSyntax type)
{
var opKeyword = this.EatToken(SyntaxKind.OperatorKeyword);
SyntaxToken opToken;
int opTokenErrorOffset;
int opTokenErrorWidth;
if (SyntaxFacts.IsAnyOverloadableOperator(this.CurrentToken.Kind))
{
opToken = this.EatToken();
Debug.Assert(!opToken.IsMissing);
opTokenErrorOffset = opToken.GetLeadingTriviaWidth();
opTokenErrorWidth = opToken.Width;
}
else
{
if (this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword)
{
// Grab the offset and width before we consume the invalid keyword and change our position.
GetDiagnosticSpanForMissingToken(out opTokenErrorOffset, out opTokenErrorWidth);
opToken = this.ConvertToMissingWithTrailingTrivia(this.EatToken(), SyntaxKind.PlusToken);
Debug.Assert(opToken.IsMissing); //Which is why we used GetDiagnosticSpanForMissingToken above.
Debug.Assert(type != null); // How could it be? The only caller got it from ParseReturnType.
if (type.IsMissing)
{
SyntaxDiagnosticInfo diagInfo = MakeError(opTokenErrorOffset, opTokenErrorWidth, ErrorCode.ERR_BadOperatorSyntax, SyntaxFacts.GetText(SyntaxKind.PlusToken));
opToken = WithAdditionalDiagnostics(opToken, diagInfo);
}
else
{
// Dev10 puts this error on the type (if there is one).
type = this.AddError(type, ErrorCode.ERR_BadOperatorSyntax, SyntaxFacts.GetText(SyntaxKind.PlusToken));
}
}
else
{
//Consume whatever follows the operator keyword as the operator token. If it is not
//we'll add an error below (when we can guess the arity).
opToken = EatToken();
Debug.Assert(!opToken.IsMissing);
opTokenErrorOffset = opToken.GetLeadingTriviaWidth();
opTokenErrorWidth = opToken.Width;
}
}
// check for >>
var opKind = opToken.Kind;
var tk = this.CurrentToken;
if (opToken.Kind == SyntaxKind.GreaterThanToken && tk.Kind == SyntaxKind.GreaterThanToken)
{
// no trailing trivia and no leading trivia
if (opToken.GetTrailingTriviaWidth() == 0 && tk.GetLeadingTriviaWidth() == 0)
{
var opToken2 = this.EatToken();
opToken = SyntaxFactory.Token(opToken.GetLeadingTrivia(), SyntaxKind.GreaterThanGreaterThanToken, opToken2.GetTrailingTrivia());
}
}
var paramList = this.ParseParenthesizedParameterList(allowThisKeyword: false, allowDefaults: true, allowAttributes: true);
// ReportExtensionMethods(parameters, retval);
switch (paramList.Parameters.Count)
{
case 1:
if (opToken.IsMissing || !SyntaxFacts.IsOverloadableUnaryOperator(opKind))
{
SyntaxDiagnosticInfo diagInfo = MakeError(opTokenErrorOffset, opTokenErrorWidth, ErrorCode.ERR_OvlUnaryOperatorExpected);
opToken = WithAdditionalDiagnostics(opToken, diagInfo);
}
break;
case 2:
if (opToken.IsMissing || !SyntaxFacts.IsOverloadableBinaryOperator(opKind))
{
SyntaxDiagnosticInfo diagInfo = MakeError(opTokenErrorOffset, opTokenErrorWidth, ErrorCode.ERR_OvlBinaryOperatorExpected);
opToken = WithAdditionalDiagnostics(opToken, diagInfo);
}
break;
default:
if (opToken.IsMissing)
{
SyntaxDiagnosticInfo diagInfo = MakeError(opTokenErrorOffset, opTokenErrorWidth, ErrorCode.ERR_OvlOperatorExpected);
opToken = WithAdditionalDiagnostics(opToken, diagInfo);
}
else if (SyntaxFacts.IsOverloadableBinaryOperator(opKind))
{
opToken = this.AddError(opToken, ErrorCode.ERR_BadBinOpArgs, SyntaxFacts.GetText(opKind));
}
else if (SyntaxFacts.IsOverloadableUnaryOperator(opKind))
{
opToken = this.AddError(opToken, ErrorCode.ERR_BadUnOpArgs, SyntaxFacts.GetText(opKind));
}
else
{
opToken = this.AddError(opToken, ErrorCode.ERR_OvlOperatorExpected);
}
break;
}
BlockSyntax blockBody;
ArrowExpressionClauseSyntax expressionBody;
SyntaxToken semicolon;
this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon);
//if the operator is invalid, then switch it to plus (which will work either way) so that
//we can finish building the tree
if (!(SyntaxFacts.IsOverloadableUnaryOperator(opKind) || SyntaxFacts.IsOverloadableBinaryOperator(opKind)))
{
opToken = ConvertToMissingWithTrailingTrivia(opToken, SyntaxKind.PlusToken);
}
var decl = _syntaxFactory.OperatorDeclaration(
attributes,
modifiers.ToTokenList(),
type,
opKeyword,
opToken,
paramList,
blockBody,
expressionBody,
semicolon);
return CheckForBlockAndExpressionBody(blockBody, expressionBody, decl);
}
private MemberDeclarationSyntax ParseIndexerDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
TypeSyntax type,
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt,
SyntaxToken thisKeyword,
TypeParameterListSyntax typeParameterList)
{
Debug.Assert(thisKeyword.Kind == SyntaxKind.ThisKeyword);
// check to see if the user tried to create a generic indexer.
if (typeParameterList != null)
{
thisKeyword = AddTrailingSkippedSyntax(thisKeyword, typeParameterList);
thisKeyword = this.AddError(thisKeyword, ErrorCode.ERR_UnexpectedGenericName);
}
var parameterList = this.ParseBracketedParameterList();
// TODO: ReportExtensionMethods(parameters, retval);
if (parameterList.Parameters.Count == 0)
{
parameterList = this.AddErrorToLastToken(parameterList, ErrorCode.ERR_IndexerNeedsParam);
}
AccessorListSyntax accessorList = null;
ArrowExpressionClauseSyntax expressionBody = null;
SyntaxToken semicolon = null;
// Try to parse accessor list unless there is an expression
// body and no accessor list
if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken)
{
expressionBody = this.ParseArrowExpressionClause();
expressionBody = CheckFeatureAvailability(expressionBody, MessageID.IDS_FeatureExpressionBodiedIndexer);
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
else
{
accessorList = this.ParseAccessorList(isEvent: false);
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatTokenWithPrejudice(ErrorCode.ERR_UnexpectedSemicolon);
}
}
// If the user has erroneously provided both an accessor list
// and an expression body, but no semicolon, we want to parse
// the expression body and report the error (which is done later)
if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken
&& semicolon == null)
{
expressionBody = this.ParseArrowExpressionClause();
expressionBody = CheckFeatureAvailability(expressionBody, MessageID.IDS_FeatureExpressionBodiedIndexer);
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
var decl = _syntaxFactory.IndexerDeclaration(
attributes,
modifiers.ToTokenList(),
type,
explicitInterfaceOpt,
thisKeyword,
parameterList,
accessorList,
expressionBody,
semicolon);
return CheckForBlockAndExpressionBody(accessorList, expressionBody, decl);
}
private PropertyDeclarationSyntax ParsePropertyDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
TypeSyntax type,
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt,
SyntaxToken identifier,
TypeParameterListSyntax typeParameterList)
{
// check to see if the user tried to create a generic property.
if (typeParameterList != null)
{
identifier = AddTrailingSkippedSyntax(identifier, typeParameterList);
identifier = this.AddError(identifier, ErrorCode.ERR_UnexpectedGenericName);
}
// We know we are parsing a property because we have seen either an
// open brace or an arrow token
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken ||
this.CurrentToken.Kind == SyntaxKind.OpenBraceToken);
AccessorListSyntax accessorList = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
accessorList = this.ParseAccessorList(isEvent: false);
}
ArrowExpressionClauseSyntax expressionBody = null;
EqualsValueClauseSyntax initializer = null;
// Check for expression body
if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken)
{
expressionBody = this.ParseArrowExpressionClause();
expressionBody = CheckFeatureAvailability(expressionBody, MessageID.IDS_FeatureExpressionBodiedProperty);
}
// Check if we have an initializer
else if (this.CurrentToken.Kind == SyntaxKind.EqualsToken)
{
var equals = this.EatToken(SyntaxKind.EqualsToken);
var value = this.ParseVariableInitializer(allowStackAlloc: false);
initializer = _syntaxFactory.EqualsValueClause(equals, value);
initializer = CheckFeatureAvailability(initializer, MessageID.IDS_FeatureAutoPropertyInitializer);
}
SyntaxToken semicolon = null;
if (expressionBody != null || initializer != null)
{
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
else if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatTokenWithPrejudice(ErrorCode.ERR_UnexpectedSemicolon);
}
var decl = _syntaxFactory.PropertyDeclaration(
attributes,
modifiers.ToTokenList(),
type,
explicitInterfaceOpt,
identifier,
accessorList,
expressionBody,
initializer,
semicolon);
return CheckForBlockAndExpressionBody(accessorList, expressionBody, decl);
}
private AccessorListSyntax ParseAccessorList(bool isEvent)
{
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
var accessors = default(SyntaxList<AccessorDeclarationSyntax>);
if (!openBrace.IsMissing || !this.IsTerminator())
{
// parse property accessors
var builder = _pool.Allocate<AccessorDeclarationSyntax>();
try
{
bool hasGetOrAdd = false;
bool hasSetOrRemove = false;
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.IsPossibleAccessor())
{
var acc = this.ParseAccessorDeclaration(isEvent, ref hasGetOrAdd, ref hasSetOrRemove);
builder.Add(acc);
}
else if (this.SkipBadAccessorListTokens(ref openBrace, builder,
isEvent ? ErrorCode.ERR_AddOrRemoveExpected : ErrorCode.ERR_GetOrSetExpected) == PostSkipAction.Abort)
{
break;
}
}
accessors = builder.ToList();
}
finally
{
_pool.Free(builder);
}
}
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
return _syntaxFactory.AccessorList(openBrace, accessors, closeBrace);
}
private ArrowExpressionClauseSyntax ParseArrowExpressionClause()
{
var arrowToken = this.EatToken(SyntaxKind.EqualsGreaterThanToken);
return _syntaxFactory.ArrowExpressionClause(arrowToken, this.ParseExpressionCore());
}
private PostSkipAction SkipBadAccessorListTokens(ref SyntaxToken openBrace, SyntaxListBuilder<AccessorDeclarationSyntax> list, ErrorCode error)
{
return this.SkipBadListTokensWithErrorCode(ref openBrace, list,
p => p.CurrentToken.Kind != SyntaxKind.CloseBraceToken && !p.IsPossibleAccessor(),
p => p.IsTerminator(),
error);
}
private bool IsPossibleAccessor()
{
return this.CurrentToken.Kind == SyntaxKind.IdentifierToken
|| IsPossibleAttributeDeclaration()
|| SyntaxFacts.GetAccessorDeclarationKind(this.CurrentToken.ContextualKind) != SyntaxKind.None
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken // for accessor blocks w/ missing keyword
|| this.CurrentToken.Kind == SyntaxKind.SemicolonToken // for empty body accessors w/ missing keyword
|| IsPossibleAccessorModifier();
}
private bool IsPossibleAccessorModifier()
{
// We only want to accept a modifier as the start of an accessor if the modifiers are
// actually followed by "get/set/add/remove". Otherwise, we might thing think we're
// starting an accessor when we're actually starting a normal class member. For example:
//
// class C {
// public int Prop { get { this.
// private DateTime x;
//
// We don't want to think of the "private" in "private DateTime x" as starting an accessor
// here. If we do, we'll get totally thrown off in parsing the remainder and that will
// throw off the rest of the features that depend on a good syntax tree.
//
// Note: we allow all modifiers here. That's because we want to parse things like
// "abstract get" as an accessor. This way we can provide a good error message
// to the user that this is not allowed.
if (IsPossibleModifier())
{
var peekIndex = 1;
while (IsPossibleModifier(this.PeekToken(peekIndex)))
{
peekIndex++;
}
var token = this.PeekToken(peekIndex);
if (token.Kind == SyntaxKind.CloseBraceToken || token.Kind == SyntaxKind.EndOfFileToken)
{
// If we see "{ get { } public }
// then we will think that "public" likely starts an accessor.
return true;
}
switch (token.ContextualKind)
{
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.AddKeyword:
case SyntaxKind.RemoveKeyword:
return true;
}
}
return false;
}
private enum PostSkipAction
{
Continue,
Abort
}
private PostSkipAction SkipBadSeparatedListTokensWithExpectedKind<T, TNode>(
ref T startToken,
SeparatedSyntaxListBuilder<TNode> list,
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
SyntaxKind expected)
where T : CSharpSyntaxNode
where TNode : CSharpSyntaxNode
{
// We're going to cheat here and pass the underlying SyntaxListBuilder of "list" to the helper method so that
// it can append skipped trivia to the last element, regardless of whether that element is a node or a token.
CSharpSyntaxNode trailingTrivia;
var action = this.SkipBadListTokensWithExpectedKindHelper(list.UnderlyingBuilder, isNotExpectedFunction, abortFunction, expected, out trailingTrivia);
if (trailingTrivia != null)
{
startToken = AddTrailingSkippedSyntax(startToken, trailingTrivia);
}
return action;
}
private PostSkipAction SkipBadListTokensWithErrorCode<T, TNode>(
ref T startToken,
SyntaxListBuilder<TNode> list,
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
ErrorCode error)
where T : CSharpSyntaxNode
where TNode : CSharpSyntaxNode
{
CSharpSyntaxNode trailingTrivia;
var action = this.SkipBadListTokensWithErrorCodeHelper(list, isNotExpectedFunction, abortFunction, error, out trailingTrivia);
if (trailingTrivia != null)
{
startToken = AddTrailingSkippedSyntax(startToken, trailingTrivia);
}
return action;
}
/// <remarks>
/// WARNING: it is possible that "list" is really the underlying builder of a SeparateSyntaxListBuilder,
/// so it is important that we not add anything to the list.
/// </remarks>
private PostSkipAction SkipBadListTokensWithExpectedKindHelper(
SyntaxListBuilder list,
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
SyntaxKind expected,
out CSharpSyntaxNode trailingTrivia)
{
if (list.Count == 0)
{
return SkipBadTokensWithExpectedKind(isNotExpectedFunction, abortFunction, expected, out trailingTrivia);
}
else
{
CSharpSyntaxNode lastItemTrailingTrivia;
var action = SkipBadTokensWithExpectedKind(isNotExpectedFunction, abortFunction, expected, out lastItemTrailingTrivia);
if (lastItemTrailingTrivia != null)
{
list[list.Count - 1] = AddTrailingSkippedSyntax(list[list.Count - 1], lastItemTrailingTrivia);
}
trailingTrivia = null;
return action;
}
}
private PostSkipAction SkipBadListTokensWithErrorCodeHelper<TNode>(
SyntaxListBuilder<TNode> list,
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
ErrorCode error,
out CSharpSyntaxNode trailingTrivia) where TNode : CSharpSyntaxNode
{
if (list.Count == 0)
{
return SkipBadTokensWithErrorCode(isNotExpectedFunction, abortFunction, error, out trailingTrivia);
}
else
{
CSharpSyntaxNode lastItemTrailingTrivia;
var action = SkipBadTokensWithErrorCode(isNotExpectedFunction, abortFunction, error, out lastItemTrailingTrivia);
if (lastItemTrailingTrivia != null)
{
list[list.Count - 1] = AddTrailingSkippedSyntax(list[list.Count - 1], lastItemTrailingTrivia);
}
trailingTrivia = null;
return action;
}
}
private PostSkipAction SkipBadTokensWithExpectedKind(
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
SyntaxKind expected,
out CSharpSyntaxNode trailingTrivia)
{
var nodes = _pool.Allocate();
try
{
bool first = true;
var action = PostSkipAction.Continue;
while (isNotExpectedFunction(this))
{
if (abortFunction(this))
{
action = PostSkipAction.Abort;
break;
}
var token = (first && !this.CurrentToken.ContainsDiagnostics) ? this.EatTokenWithPrejudice(expected) : this.EatToken();
first = false;
nodes.Add(token);
}
trailingTrivia = (nodes.Count > 0) ? nodes.ToListNode() : null;
return action;
}
finally
{
_pool.Free(nodes);
}
}
private PostSkipAction SkipBadTokensWithErrorCode(
Func<LanguageParser, bool> isNotExpectedFunction,
Func<LanguageParser, bool> abortFunction,
ErrorCode errorCode,
out CSharpSyntaxNode trailingTrivia)
{
var nodes = _pool.Allocate();
try
{
bool first = true;
var action = PostSkipAction.Continue;
while (isNotExpectedFunction(this))
{
if (abortFunction(this))
{
action = PostSkipAction.Abort;
break;
}
var token = (first && !this.CurrentToken.ContainsDiagnostics) ? this.EatTokenWithPrejudice(errorCode) : this.EatToken();
first = false;
nodes.Add(token);
}
trailingTrivia = (nodes.Count > 0) ? nodes.ToListNode() : null;
return action;
}
finally
{
_pool.Free(nodes);
}
}
private AccessorDeclarationSyntax ParseAccessorDeclaration(
bool isEvent,
ref bool hasGetOrAdd,
ref bool hasSetOrRemove)
{
if (this.IsIncrementalAndFactoryContextMatches && CanReuseAccessorDeclaration(isEvent))
{
return (AccessorDeclarationSyntax)this.EatNode();
}
var accAttrs = _pool.Allocate<AttributeListSyntax>();
var accMods = _pool.Allocate();
try
{
this.ParseAttributeDeclarations(accAttrs);
this.ParseModifiers(accMods);
if (isEvent)
{
if (accMods != null && accMods.Count > 0)
{
accMods[0] = this.AddError(accMods[0], ErrorCode.ERR_NoModifiersOnAccessor);
}
}
else
{
if (accMods != null && accMods.Count > 0)
{
accMods[0] = CheckFeatureAvailability(accMods[0], MessageID.IDS_FeaturePropertyAccessorMods);
}
}
bool validAccName;
SyntaxToken accessorName;
SyntaxKind accessorKind;
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
accessorName = this.EatToken();
// Only convert the identifier to a keyword if it's a valid one. Otherwise any
// other contextual keyword (like 'partial') will be converted into a keyword
// and will be invalid.
switch (accessorName.ContextualKind)
{
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.AddKeyword:
case SyntaxKind.RemoveKeyword:
accessorName = ConvertToKeyword(accessorName);
break;
}
if (isEvent)
{
bool isAdd = IsNameAdd(accessorName);
bool isRemove = IsNameRemove(accessorName);
validAccName = isAdd || isRemove;
if (!validAccName)
{
accessorName = this.AddError(accessorName, ErrorCode.ERR_AddOrRemoveExpected);
accessorKind = SyntaxKind.UnknownAccessorDeclaration;
}
else
{
if ((isAdd && hasGetOrAdd) || (isRemove && hasSetOrRemove))
{
accessorName = this.AddError(accessorName, ErrorCode.ERR_DuplicateAccessor);
}
hasGetOrAdd |= isAdd;
hasSetOrRemove |= isRemove;
accessorKind = isRemove ? SyntaxKind.RemoveAccessorDeclaration : SyntaxKind.AddAccessorDeclaration;
}
}
else
{
// Regular property
bool isGet = IsNameGet(accessorName);
bool isSet = IsNameSet(accessorName);
validAccName = isGet || isSet;
if (!validAccName)
{
accessorName = this.AddError(accessorName, ErrorCode.ERR_GetOrSetExpected);
accessorKind = SyntaxKind.UnknownAccessorDeclaration;
}
else
{
if ((isGet && hasGetOrAdd) || (isSet && hasSetOrRemove))
{
accessorName = this.AddError(accessorName, ErrorCode.ERR_DuplicateAccessor);
}
hasGetOrAdd |= isGet;
hasSetOrRemove |= isSet;
accessorKind = isSet ? SyntaxKind.SetAccessorDeclaration : SyntaxKind.GetAccessorDeclaration;
}
}
}
else
{
validAccName = false;
accessorName = SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken);
accessorName = this.AddError(accessorName, isEvent ? ErrorCode.ERR_AddOrRemoveExpected : ErrorCode.ERR_GetOrSetExpected);
accessorKind = SyntaxKind.UnknownAccessorDeclaration;
}
BlockSyntax body = null;
SyntaxToken semicolon = null;
bool currentTokenIsSemicolon = this.CurrentToken.Kind == SyntaxKind.SemicolonToken;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || (validAccName && !currentTokenIsSemicolon && !IsTerminator()))
{
body = this.ParseBlock(isMethodBody: true, isAccessorBody: true);
}
else if (currentTokenIsSemicolon || validAccName)
{
semicolon = this.EatToken(SyntaxKind.SemicolonToken, ErrorCode.ERR_SemiOrLBraceExpected);
if (isEvent)
{
semicolon = this.AddError(semicolon, ErrorCode.ERR_AddRemoveMustHaveBody);
}
}
return _syntaxFactory.AccessorDeclaration(accessorKind, accAttrs, accMods.ToTokenList(), accessorName, body, semicolon);
}
finally
{
_pool.Free(accMods);
_pool.Free(accAttrs);
}
}
private bool CanReuseAccessorDeclaration(bool isEvent)
{
var parent = GetOldParent(this.CurrentNode);
switch (this.CurrentNodeKind)
{
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
if (isEvent && parent != null && parent.Kind() == SyntaxKind.EventDeclaration)
{
return true;
}
break;
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
if (!isEvent && parent != null && parent.Kind() == SyntaxKind.PropertyDeclaration)
{
return true;
}
break;
}
return false;
}
internal ParameterListSyntax ParseParenthesizedParameterList(bool allowThisKeyword, bool allowDefaults, bool allowAttributes)
{
if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameterList(this.CurrentNode as CSharp.Syntax.ParameterListSyntax))
{
return (ParameterListSyntax)this.EatNode();
}
var parameters = _pool.AllocateSeparated<ParameterSyntax>();
try
{
var openKind = SyntaxKind.OpenParenToken;
var closeKind = SyntaxKind.CloseParenToken;
SyntaxToken open;
SyntaxToken close;
this.ParseParameterList(out open, parameters, out close, openKind, closeKind, allowThisKeyword, allowDefaults, allowAttributes);
return _syntaxFactory.ParameterList(open, parameters, close);
}
finally
{
_pool.Free(parameters);
}
}
internal BracketedParameterListSyntax ParseBracketedParameterList(bool allowDefaults = true)
{
if (this.IsIncrementalAndFactoryContextMatches && CanReuseBracketedParameterList(this.CurrentNode as CSharp.Syntax.BracketedParameterListSyntax))
{
return (BracketedParameterListSyntax)this.EatNode();
}
var parameters = _pool.AllocateSeparated<ParameterSyntax>();
try
{
var openKind = SyntaxKind.OpenBracketToken;
var closeKind = SyntaxKind.CloseBracketToken;
SyntaxToken open;
SyntaxToken close;
this.ParseParameterList(out open, parameters, out close, openKind, closeKind, allowThisKeyword: false, allowDefaults: allowDefaults, allowAttributes: true);
return _syntaxFactory.BracketedParameterList(open, parameters, close);
}
finally
{
_pool.Free(parameters);
}
}
private static bool CanReuseParameterList(CSharp.Syntax.ParameterListSyntax list)
{
if (list == null)
{
return false;
}
if (list.OpenParenToken.IsMissing)
{
return false;
}
if (list.CloseParenToken.IsMissing)
{
return false;
}
foreach (var parameter in list.Parameters)
{
if (!CanReuseParameter(parameter))
{
return false;
}
}
return true;
}
private static bool CanReuseBracketedParameterList(CSharp.Syntax.BracketedParameterListSyntax list)
{
if (list == null)
{
return false;
}
if (list.OpenBracketToken.IsMissing)
{
return false;
}
if (list.CloseBracketToken.IsMissing)
{
return false;
}
foreach (var parameter in list.Parameters)
{
if (!CanReuseParameter(parameter))
{
return false;
}
}
return true;
}
private void ParseParameterList(
out SyntaxToken open,
SeparatedSyntaxListBuilder<ParameterSyntax> nodes,
out SyntaxToken close,
SyntaxKind openKind,
SyntaxKind closeKind,
bool allowThisKeyword,
bool allowDefaults,
bool allowAttributes)
{
open = this.EatToken(openKind);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfParameterList;
var attributes = _pool.Allocate<AttributeListSyntax>();
var modifiers = _pool.Allocate();
try
{
if (this.CurrentToken.Kind != closeKind)
{
tryAgain:
int mustBeLastIndex = -1;
bool mustBeLastHadParams = false;
bool hasParams = false;
bool hasArgList = false;
if (this.IsPossibleParameter(allowThisKeyword) || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first parameter
attributes.Clear();
modifiers.Clear();
var parameter = this.ParseParameter(attributes, modifiers, allowThisKeyword, allowDefaults, allowAttributes);
nodes.Add(parameter);
hasParams = modifiers.Any(SyntaxKind.ParamsKeyword);
hasArgList = parameter.Identifier.Kind == SyntaxKind.ArgListKeyword;
bool mustBeLast = hasParams || hasArgList;
if (mustBeLast && mustBeLastIndex == -1)
{
mustBeLastIndex = nodes.Count - 1;
mustBeLastHadParams = hasParams;
}
// additional parameters
while (true)
{
if (this.CurrentToken.Kind == closeKind)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleParameter(allowThisKeyword))
{
nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
attributes.Clear();
modifiers.Clear();
parameter = this.ParseParameter(attributes, modifiers, allowThisKeyword, allowDefaults, allowAttributes);
nodes.Add(parameter);
hasParams = modifiers.Any(SyntaxKind.ParamsKeyword);
hasArgList = parameter.Identifier.Kind == SyntaxKind.ArgListKeyword;
mustBeLast = hasParams || hasArgList;
if (mustBeLast && mustBeLastIndex == -1)
{
mustBeLastIndex = nodes.Count - 1;
mustBeLastHadParams = hasParams;
}
continue;
}
else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.CommaToken, closeKind, allowThisKeyword) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.IdentifierToken, closeKind, allowThisKeyword) == PostSkipAction.Continue)
{
goto tryAgain;
}
if (mustBeLastIndex >= 0 && mustBeLastIndex < nodes.Count - 1)
{
nodes[mustBeLastIndex] = this.AddError(nodes[mustBeLastIndex], mustBeLastHadParams ? ErrorCode.ERR_ParamsLast : ErrorCode.ERR_VarargsLast);
}
}
_termState = saveTerm;
close = this.EatToken(closeKind);
}
finally
{
_pool.Free(modifiers);
_pool.Free(attributes);
}
}
private bool IsEndOfParameterList()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.CloseBracketToken;
}
private PostSkipAction SkipBadParameterListTokens(ref SyntaxToken open, SeparatedSyntaxListBuilder<ParameterSyntax> list, SyntaxKind expected, SyntaxKind closeKind, bool allowThisKeyword)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref open, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleParameter(allowThisKeyword),
p => p.CurrentToken.Kind == closeKind || p.IsTerminator(),
expected);
}
private bool IsPossibleParameter(bool allowThisKeyword)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.OpenBracketToken: // attribute
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
case SyntaxKind.ParamsKeyword:
case SyntaxKind.ArgListKeyword:
return true;
case SyntaxKind.ThisKeyword:
return allowThisKeyword;
case SyntaxKind.IdentifierToken:
return this.IsTrueIdentifier();
default:
return IsPredefinedType(this.CurrentToken.Kind);
}
}
private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter, SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
if (parameter == null)
{
return false;
}
// cannot reuse parameter if it had attributes.
//
// TODO(cyrusn): Why? We can reuse other constructs if they have attributes.
if (attributes.Count != 0 || parameter.AttributeLists.Count != 0)
{
return false;
}
// cannot reuse parameter if it had modifiers.
if ((modifiers != null && modifiers.Count != 0) || parameter.Modifiers.Count != 0)
{
return false;
}
return CanReuseParameter(parameter);
}
private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter)
{
// cannot reuse a node that possibly ends in an expression
if (parameter.Default != null)
{
return false;
}
// cannot reuse lambda parameters as normal parameters (parsed with
// different rules)
CSharp.CSharpSyntaxNode parent = parameter.Parent;
if (parent != null)
{
if (parent.Kind() == SyntaxKind.SimpleLambdaExpression)
{
return false;
}
CSharp.CSharpSyntaxNode grandparent = parent.Parent;
if (grandparent != null && grandparent.Kind() == SyntaxKind.ParenthesizedLambdaExpression)
{
Debug.Assert(parent.Kind() == SyntaxKind.ParameterList);
return false;
}
}
return true;
}
private ParameterSyntax ParseParameter(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
bool allowThisKeyword,
bool allowDefaults,
bool allowAttributes)
{
if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameter(this.CurrentNode as CSharp.Syntax.ParameterSyntax, attributes, modifiers))
{
return (ParameterSyntax)this.EatNode();
}
this.ParseAttributeDeclarations(attributes, allowAttributes);
this.ParseParameterModifiers(modifiers, allowThisKeyword);
var hasArgList = this.CurrentToken.Kind == SyntaxKind.ArgListKeyword;
TypeSyntax type = null;
if (!hasArgList)
{
type = this.ParseType(true);
}
else if (this.IsPossibleType())
{
type = this.ParseType(true);
type = WithAdditionalDiagnostics(type, this.GetExpectedTokenError(SyntaxKind.CloseParenToken, SyntaxKind.IdentifierToken, 0, type.Width));
}
SyntaxToken name = null;
if (!hasArgList)
{
name = this.ParseIdentifierToken();
// When the user type "int foo[]", give them a useful error
if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind == SyntaxKind.CloseBracketToken)
{
var open = this.EatToken();
var close = this.EatToken();
open = this.AddError(open, ErrorCode.ERR_BadArraySyntax);
name = AddTrailingSkippedSyntax(name, SyntaxList.List(open, close));
}
}
else if (this.IsPossibleName())
{
// Current token is an identifier token, we expected a CloseParenToken.
// Get the expected token error for the missing token with correct diagnostic
// span and then parse the identifier token.
SyntaxDiagnosticInfo diag = this.GetExpectedTokenError(SyntaxKind.CloseParenToken, SyntaxKind.IdentifierToken);
name = this.ParseIdentifierToken();
name = WithAdditionalDiagnostics(name, diag);
}
else
{
// name is not optional on ParameterSyntax
name = this.EatToken(SyntaxKind.ArgListKeyword);
}
EqualsValueClauseSyntax def = null;
if (this.CurrentToken.Kind == SyntaxKind.EqualsToken)
{
var equals = this.EatToken(SyntaxKind.EqualsToken);
var expr = this.ParseExpressionCore();
def = _syntaxFactory.EqualsValueClause(equals, expr);
if (!allowDefaults)
{
def = this.AddError(def, equals, ErrorCode.ERR_DefaultValueNotAllowed);
}
else
{
def = CheckFeatureAvailability(def, MessageID.IDS_FeatureOptionalParameter);
}
}
return _syntaxFactory.Parameter(attributes, modifiers.ToTokenList(), type, name, def);
}
private static bool IsParameterModifier(SyntaxKind kind, bool allowThisKeyword)
{
return GetParamFlags(kind, allowThisKeyword) != ParamFlags.None;
}
[Flags]
private enum ParamFlags
{
None = 0x00,
This = 0x01,
Ref = 0x02,
Out = 0x04,
Params = 0x08,
}
private static ParamFlags GetParamFlags(SyntaxKind kind, bool allowThisKeyword)
{
switch (kind)
{
case SyntaxKind.ThisKeyword:
// if (this.IsCSharp3Enabled)
return (allowThisKeyword ? ParamFlags.This : ParamFlags.None);
// goto default;
case SyntaxKind.RefKeyword:
return ParamFlags.Ref;
case SyntaxKind.OutKeyword:
return ParamFlags.Out;
case SyntaxKind.ParamsKeyword:
return ParamFlags.Params;
default:
return ParamFlags.None;
}
}
private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool allowThisKeyword)
{
var flags = ParamFlags.None;
while (IsParameterModifier(this.CurrentToken.Kind, allowThisKeyword))
{
var mod = this.EatToken();
if (mod.Kind == SyntaxKind.ThisKeyword ||
mod.Kind == SyntaxKind.RefKeyword ||
mod.Kind == SyntaxKind.OutKeyword ||
mod.Kind == SyntaxKind.ParamsKeyword)
{
if (mod.Kind == SyntaxKind.ThisKeyword)
{
mod = CheckFeatureAvailability(mod, MessageID.IDS_FeatureExtensionMethod);
if ((flags & ParamFlags.This) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_DupParamMod, SyntaxFacts.GetText(SyntaxKind.ThisKeyword));
}
else if ((flags & ParamFlags.Out) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadOutWithThis);
}
else if ((flags & ParamFlags.Ref) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadRefWithThis);
}
else if ((flags & ParamFlags.Params) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadParamModThis);
}
else
{
flags |= ParamFlags.This;
}
}
else if (mod.Kind == SyntaxKind.RefKeyword)
{
if ((flags & ParamFlags.Ref) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_DupParamMod, SyntaxFacts.GetText(SyntaxKind.RefKeyword));
}
else if ((flags & ParamFlags.This) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadRefWithThis);
}
else if ((flags & ParamFlags.Params) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_ParamsCantBeRefOut);
}
else if ((flags & ParamFlags.Out) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_MultiParamMod);
}
else
{
flags |= ParamFlags.Ref;
}
}
else if (mod.Kind == SyntaxKind.OutKeyword)
{
if ((flags & ParamFlags.Out) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_DupParamMod, SyntaxFacts.GetText(SyntaxKind.OutKeyword));
}
else if ((flags & ParamFlags.This) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadOutWithThis);
}
else if ((flags & ParamFlags.Params) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_ParamsCantBeRefOut);
}
else if ((flags & ParamFlags.Ref) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_MultiParamMod);
}
else
{
flags |= ParamFlags.Out;
}
}
else if (mod.Kind == SyntaxKind.ParamsKeyword)
{
if ((flags & ParamFlags.Params) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_DupParamMod, SyntaxFacts.GetText(SyntaxKind.ParamsKeyword));
}
else if ((flags & ParamFlags.This) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_BadParamModThis);
}
else if ((flags & (ParamFlags.Ref | ParamFlags.Out | ParamFlags.This)) != 0)
{
mod = this.AddError(mod, ErrorCode.ERR_MultiParamMod);
}
else
{
flags |= ParamFlags.Params;
}
}
}
modifiers.Add(mod);
}
}
private MemberDeclarationSyntax ParseFixedSizeBufferDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
SyntaxKind parentKind)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.FixedKeyword);
var fixedToken = this.EatToken();
fixedToken = CheckFeatureAvailability(fixedToken, MessageID.IDS_FeatureFixedBuffer);
modifiers.Add(fixedToken);
var type = this.ParseType(parentIsParameter: false);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfFieldDeclaration;
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
this.ParseVariableDeclarators(type, VariableFlags.Fixed, variables, parentKind);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.FieldDeclaration(
attributes, modifiers.ToTokenList(),
_syntaxFactory.VariableDeclaration(type, variables),
semicolon);
}
finally
{
_termState = saveTerm;
_pool.Free(variables);
}
}
private MemberDeclarationSyntax ParseEventDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
SyntaxKind parentKind)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.EventKeyword);
var eventToken = this.EatToken();
var type = this.ParseType(parentIsParameter: false);
if (IsFieldDeclaration(isEvent: true))
{
return this.ParseEventFieldDeclaration(attributes, modifiers, eventToken, type, parentKind);
}
else
{
return this.ParseEventDeclarationWithAccessors(attributes, modifiers, eventToken, type);
}
}
private MemberDeclarationSyntax ParseEventDeclarationWithAccessors(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
SyntaxToken eventToken,
TypeSyntax type)
{
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt;
SyntaxToken identifierOrThisOpt;
TypeParameterListSyntax typeParameterList;
this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterList, isEvent: true);
// If we got an explicitInterfaceOpt but not an identifier, then we're in the special
// case for ERR_ExplicitEventFieldImpl (see ParseMemberName for details).
if (explicitInterfaceOpt != null && identifierOrThisOpt == null)
{
Debug.Assert(typeParameterList == null, "Exit condition of ParseMemberName in this scenario");
// No need for a diagnostic, ParseMemberName has already added one.
var missingIdentifier = CreateMissingIdentifierToken();
var missingAccessorList =
_syntaxFactory.AccessorList(
SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken),
default(SyntaxList<AccessorDeclarationSyntax>),
SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken));
return _syntaxFactory.EventDeclaration(
attributes,
modifiers.ToTokenList(),
eventToken,
type,
explicitInterfaceOpt, //already has an appropriate error attached
missingIdentifier,
missingAccessorList);
}
SyntaxToken identifier;
if (identifierOrThisOpt == null)
{
identifier = CreateMissingIdentifierToken();
}
else if (identifierOrThisOpt.Kind != SyntaxKind.IdentifierToken)
{
Debug.Assert(identifierOrThisOpt.Kind == SyntaxKind.ThisKeyword);
identifier = ConvertToMissingWithTrailingTrivia(identifierOrThisOpt, SyntaxKind.IdentifierToken);
}
else
{
identifier = identifierOrThisOpt;
}
Debug.Assert(identifier != null);
Debug.Assert(identifier.Kind == SyntaxKind.IdentifierToken);
if (identifier.IsMissing && !type.IsMissing)
{
identifier = this.AddError(identifier, ErrorCode.ERR_IdentifierExpected);
}
if (typeParameterList != null) // check to see if the user tried to create a generic event.
{
identifier = AddTrailingSkippedSyntax(identifier, typeParameterList);
identifier = this.AddError(identifier, ErrorCode.ERR_UnexpectedGenericName);
}
var accessorList = this.ParseAccessorList(isEvent: true);
var decl = _syntaxFactory.EventDeclaration(
attributes,
modifiers.ToTokenList(),
eventToken,
type,
explicitInterfaceOpt,
identifier,
accessorList);
decl = EatUnexpectedTrailingSemicolon(decl);
return decl;
}
private TNode EatUnexpectedTrailingSemicolon<TNode>(TNode decl) where TNode : CSharpSyntaxNode
{
// allow for case of one unexpected semicolon...
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
var semi = this.EatToken();
semi = this.AddError(semi, ErrorCode.ERR_UnexpectedSemicolon);
decl = AddTrailingSkippedSyntax(decl, semi);
}
return decl;
}
private FieldDeclarationSyntax ParseNormalFieldDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
TypeSyntax type,
SyntaxKind parentKind)
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfFieldDeclaration;
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
this.ParseVariableDeclarators(type, flags: 0, variables: variables, parentKind: parentKind);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.FieldDeclaration(
attributes,
modifiers.ToTokenList(),
_syntaxFactory.VariableDeclaration(type, variables),
semicolon);
}
finally
{
_termState = saveTerm;
_pool.Free(variables);
}
}
private MemberDeclarationSyntax ParseEventFieldDeclaration(
SyntaxListBuilder<AttributeListSyntax> attributes,
SyntaxListBuilder modifiers,
SyntaxToken eventToken,
TypeSyntax type,
SyntaxKind parentKind)
{
// An attribute specified on an event declaration that omits event accessors can apply
// to the event being declared, to the associated field (if the event is not abstract),
// or to the associated add and remove methods. In the absence of an
// attribute-target-specifier, the attribute applies to the event. The presence of the
// event attribute-target-specifier indicates that the attribute applies to the event;
// the presence of the field attribute-target-specifier indicates that the attribute
// applies to the field; and the presence of the method attribute-target-specifier
// indicates that the attribute applies to the methods.
//
// NOTE(cyrusn): We allow more than the above here. Specifically, even if the event is
// abstract, we allow the attribute to specify that it belongs to a field. Later, in the
// semantic pass, we will disallow this.
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfFieldDeclaration;
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
this.ParseVariableDeclarators(type, flags: 0, variables: variables, parentKind: parentKind);
if (this.CurrentToken.Kind == SyntaxKind.DotToken)
{
eventToken = this.AddError(eventToken, ErrorCode.ERR_ExplicitEventFieldImpl); // Better error message for confusing event situation.
}
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.EventFieldDeclaration(
attributes,
modifiers.ToTokenList(),
eventToken,
_syntaxFactory.VariableDeclaration(type, variables),
semicolon);
}
finally
{
_termState = saveTerm;
_pool.Free(variables);
}
}
private bool IsEndOfFieldDeclaration()
{
return this.CurrentToken.Kind == SyntaxKind.SemicolonToken;
}
private void ParseVariableDeclarators(TypeSyntax type, VariableFlags flags, SeparatedSyntaxListBuilder<VariableDeclaratorSyntax> variables, SyntaxKind parentKind)
{
// Although we try parse variable declarations in contexts where they are not allowed (non-interactive top-level or a namespace)
// the reported errors should take into consideration whether or not one expects them in the current context.
bool variableDeclarationsExpected =
parentKind != SyntaxKind.NamespaceDeclaration &&
(parentKind != SyntaxKind.CompilationUnit || IsScript);
ParseVariableDeclarators(type, flags, variables, variableDeclarationsExpected);
}
private void ParseVariableDeclarators(TypeSyntax type, VariableFlags flags, SeparatedSyntaxListBuilder<VariableDeclaratorSyntax> variables, bool variableDeclarationsExpected)
{
variables.Add(this.ParseVariableDeclarator(type, flags, isFirst: true));
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
variables.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
variables.Add(this.ParseVariableDeclarator(type, flags, isFirst: false));
}
else if (!variableDeclarationsExpected || this.SkipBadVariableListTokens(variables, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
private PostSkipAction SkipBadVariableListTokens(SeparatedSyntaxListBuilder<VariableDeclaratorSyntax> list, SyntaxKind expected)
{
CSharpSyntaxNode tmp = null;
Debug.Assert(list.Count > 0);
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => this.CurrentToken.Kind != SyntaxKind.CommaToken,
p => this.CurrentToken.Kind == SyntaxKind.SemicolonToken || this.IsTerminator(),
expected);
}
[Flags]
private enum VariableFlags
{
Fixed = 0x01,
Const = 0x02,
Local = 0x04
}
private static SyntaxTokenList GetOriginalModifiers(CSharp.CSharpSyntaxNode decl)
{
if (decl != null)
{
switch (decl.Kind())
{
case SyntaxKind.FieldDeclaration:
return ((CSharp.Syntax.FieldDeclarationSyntax)decl).Modifiers;
case SyntaxKind.MethodDeclaration:
return ((CSharp.Syntax.MethodDeclarationSyntax)decl).Modifiers;
case SyntaxKind.ConstructorDeclaration:
return ((CSharp.Syntax.ConstructorDeclarationSyntax)decl).Modifiers;
case SyntaxKind.DestructorDeclaration:
return ((CSharp.Syntax.DestructorDeclarationSyntax)decl).Modifiers;
case SyntaxKind.PropertyDeclaration:
return ((CSharp.Syntax.PropertyDeclarationSyntax)decl).Modifiers;
case SyntaxKind.EventFieldDeclaration:
return ((CSharp.Syntax.EventFieldDeclarationSyntax)decl).Modifiers;
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
return ((CSharp.Syntax.AccessorDeclarationSyntax)decl).Modifiers;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
return ((CSharp.Syntax.TypeDeclarationSyntax)decl).Modifiers;
case SyntaxKind.DelegateDeclaration:
return ((CSharp.Syntax.DelegateDeclarationSyntax)decl).Modifiers;
}
}
return default(SyntaxTokenList);
}
private static bool WasFirstVariable(CSharp.Syntax.VariableDeclaratorSyntax variable)
{
var parent = GetOldParent(variable) as CSharp.Syntax.VariableDeclarationSyntax;
if (parent != null)
{
return parent.Variables[0] == variable;
}
return false;
}
private static VariableFlags GetOriginalVariableFlags(CSharp.Syntax.VariableDeclaratorSyntax old)
{
var parent = GetOldParent(old);
var mods = GetOriginalModifiers(parent);
VariableFlags flags = default(VariableFlags);
if (mods.Any(SyntaxKind.FixedKeyword))
{
flags |= VariableFlags.Fixed;
}
if (mods.Any(SyntaxKind.ConstKeyword))
{
flags |= VariableFlags.Const;
}
if (parent != null && (parent.Kind() == SyntaxKind.VariableDeclaration || parent.Kind() == SyntaxKind.LocalDeclarationStatement))
{
flags |= VariableFlags.Local;
}
return flags;
}
private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorSyntax old, VariableFlags flags, bool isFirst)
{
if (old == null)
{
return false;
}
SyntaxKind oldKind;
return (flags == GetOriginalVariableFlags(old))
&& (isFirst == WasFirstVariable(old))
&& old.Initializer == null // can't reuse node that possibly ends in an expression
&& (oldKind = GetOldParent(old).Kind()) != SyntaxKind.VariableDeclaration // or in a method body
&& oldKind != SyntaxKind.LocalDeclarationStatement;
}
private VariableDeclaratorSyntax ParseVariableDeclarator(TypeSyntax parentType, VariableFlags flags, bool isFirst, bool isExpressionContext = false)
{
if (this.IsIncrementalAndFactoryContextMatches && CanReuseVariableDeclarator(this.CurrentNode as CSharp.Syntax.VariableDeclaratorSyntax, flags, isFirst))
{
return (VariableDeclaratorSyntax)this.EatNode();
}
if (!isExpressionContext)
{
// Check for the common pattern of:
//
// C //<-- here
// Console.WriteLine();
//
// Standard greedy parsing will assume that this should be parsed as a variable
// declaration: "C Console". We want to avoid that as it can confused parts of the
// system further up. So, if we see certain things following the identifier, then we can
// assume it's not the actual name.
//
// So, if we're after a newline and we see a name followed by the list below, then we
// assume that we're accidentally consuming too far into the next statement.
//
// <dot>, <arrow>, any binary operator (except =), <question>. None of these characters
// are allowed in a normal variable declaration. This also provides a more useful error
// message to the user. Instead of telling them that a semicolon is expected after the
// following token, then instead get a useful message about an identifier being missing.
// The above list prevents:
//
// C //<-- here
// Console.WriteLine();
//
// C //<-- here
// Console->WriteLine();
//
// C
// A + B;
//
// C
// A ? B : D;
//
// C
// A()
var resetPoint = this.GetResetPoint();
try
{
var currentTokenKind = this.CurrentToken.Kind;
if (currentTokenKind == SyntaxKind.IdentifierToken && !parentType.IsMissing)
{
var isAfterNewLine = parentType.GetLastToken().TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia);
if (isAfterNewLine)
{
int offset, width;
this.GetDiagnosticSpanForMissingToken(out offset, out width);
this.EatToken();
currentTokenKind = this.CurrentToken.Kind;
var isNonEqualsBinaryToken =
currentTokenKind != SyntaxKind.EqualsToken &&
SyntaxFacts.IsBinaryExpressionOperatorToken(currentTokenKind);
if (currentTokenKind == SyntaxKind.DotToken ||
currentTokenKind == SyntaxKind.OpenParenToken ||
currentTokenKind == SyntaxKind.MinusGreaterThanToken ||
isNonEqualsBinaryToken)
{
var missingIdentifier = CreateMissingIdentifierToken();
missingIdentifier = this.AddError(missingIdentifier, offset, width, ErrorCode.ERR_IdentifierExpected);
return _syntaxFactory.VariableDeclarator(missingIdentifier, null, null);
}
}
}
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}
// NOTE: Diverges from Dev10.
//
// When we see parse an identifier and we see the partial contextual keyword, we check
// to see whether it is already attached to a partial class or partial method
// declaration. However, in the specific case of variable declarators, Dev10
// specifically treats it as a variable name, even if it could be interpreted as a
// keyword.
var name = this.ParseIdentifierToken();
BracketedArgumentListSyntax argumentList = null;
EqualsValueClauseSyntax initializer = null;
TerminatorState saveTerm = _termState;
bool isFixed = (flags & VariableFlags.Fixed) != 0;
bool isConst = (flags & VariableFlags.Const) != 0;
bool isLocal = (flags & VariableFlags.Local) != 0;
// Give better error message in the case where the user did something like:
//
// X x = 1, Y y = 2;
// using (X x = expr1, Y y = expr2) ...
//
// The superfluous type name is treated as variable (it is an identifier) and a missing ',' is injected after it.
if (!isFirst && this.IsTrueIdentifier())
{
name = this.AddError(name, ErrorCode.ERR_MultiTypeInDeclaration);
}
switch (this.CurrentToken.Kind)
{
case SyntaxKind.EqualsToken:
if (isFixed)
{
goto default;
}
var equals = this.EatToken();
var init = this.ParseVariableInitializer(isLocal && !isConst);
initializer = _syntaxFactory.EqualsValueClause(equals, init);
break;
case SyntaxKind.OpenParenToken:
// Special case for accidental use of C-style constructors
// Fake up something to hold the arguments.
_termState |= TerminatorState.IsPossibleEndOfVariableDeclaration;
argumentList = this.ParseBracketedArgumentList();
_termState = saveTerm;
argumentList = this.AddError(argumentList, ErrorCode.ERR_BadVarDecl);
break;
case SyntaxKind.OpenBracketToken:
bool sawNonOmittedSize;
_termState |= TerminatorState.IsPossibleEndOfVariableDeclaration;
var specifier = this.ParseArrayRankSpecifier(isArrayCreation: false, expectSizes: flags == VariableFlags.Fixed, sawNonOmittedSize: out sawNonOmittedSize);
_termState = saveTerm;
var open = specifier.OpenBracketToken;
var sizes = specifier.Sizes;
var close = specifier.CloseBracketToken;
if (isFixed && !sawNonOmittedSize)
{
close = this.AddError(close, ErrorCode.ERR_ValueExpected);
}
var args = _pool.AllocateSeparated<ArgumentSyntax>();
try
{
var withSeps = sizes.GetWithSeparators();
foreach (var item in withSeps)
{
var expression = item as ExpressionSyntax;
if (expression != null)
{
args.Add(_syntaxFactory.Argument(null, default(SyntaxToken), expression));
}
else
{
args.AddSeparator((SyntaxToken)item);
}
}
argumentList = _syntaxFactory.BracketedArgumentList(open, args, close);
if (!isFixed)
{
argumentList = this.AddError(argumentList, ErrorCode.ERR_CStyleArray);
// If we have "int x[] = new int[10];" then parse the initializer.
if (this.CurrentToken.Kind == SyntaxKind.EqualsToken)
{
goto case SyntaxKind.EqualsToken;
}
}
}
finally
{
_pool.Free(args);
}
break;
default:
if (isConst)
{
name = this.AddError(name, ErrorCode.ERR_ConstValueRequired); // Error here for missing constant initializers
}
else if (isFixed)
{
if (parentType.Kind == SyntaxKind.ArrayType)
{
// They accidentally put the array before the identifier
name = this.AddError(name, ErrorCode.ERR_FixedDimsRequired);
}
else
{
goto case SyntaxKind.OpenBracketToken;
}
}
break;
}
return _syntaxFactory.VariableDeclarator(name, argumentList, initializer);
}
private bool IsPossibleEndOfVariableDeclaration()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.CommaToken:
case SyntaxKind.SemicolonToken:
return true;
default:
return false;
}
}
private ExpressionSyntax ParseVariableInitializer(bool allowStackAlloc)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.StackAllocKeyword:
StackAllocArrayCreationExpressionSyntax stackAllocExpr = this.ParseStackAllocExpression();
if (!allowStackAlloc)
{
// CONSIDER: this is what dev10 reports (assuming unsafe constructs are allowed at all),
// but we could add a more specific error code.
stackAllocExpr = this.AddErrorToFirstToken(stackAllocExpr, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(SyntaxKind.StackAllocKeyword));
}
return stackAllocExpr;
case SyntaxKind.OpenBraceToken:
return this.ParseArrayInitializer();
default:
return this.ParseElementInitializer();
}
}
private bool IsPossibleVariableInitializer(bool allowStack)
{
return (allowStack && this.CurrentToken.Kind == SyntaxKind.StackAllocKeyword)
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.IsPossibleExpression();
}
private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers, SyntaxKind parentKind)
{
var constToken = this.EatToken(SyntaxKind.ConstKeyword);
modifiers.Add(constToken);
var type = this.ParseType(false);
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
this.ParseVariableDeclarators(type, VariableFlags.Const, variables, parentKind);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.FieldDeclaration(
attributes,
modifiers.ToTokenList(),
_syntaxFactory.VariableDeclaration(type, variables),
semicolon);
}
finally
{
_pool.Free(variables);
}
}
private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DelegateKeyword);
var delegateToken = this.EatToken(SyntaxKind.DelegateKeyword);
var type = this.ParseReturnType();
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfMethodSignature;
var name = this.ParseIdentifierToken();
var typeParameters = this.ParseTypeParameterList(allowVariance: true);
var parameterList = this.ParseParenthesizedParameterList(allowThisKeyword: false, allowDefaults: true, allowAttributes: true);
var constraints = default(SyntaxListBuilder<TypeParameterConstraintClauseSyntax>);
try
{
if (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
constraints = _pool.Allocate<TypeParameterConstraintClauseSyntax>();
this.ParseTypeParameterConstraintClauses(typeParameters != null, constraints);
}
_termState = saveTerm;
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.DelegateDeclaration(attributes, modifiers.ToTokenList(), delegateToken, type, name, typeParameters, parameterList, constraints, semicolon);
}
finally
{
if (!constraints.IsNull)
{
_pool.Free(constraints);
}
}
}
private EnumDeclarationSyntax ParseEnumDeclaration(SyntaxListBuilder<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.EnumKeyword);
var enumToken = this.EatToken(SyntaxKind.EnumKeyword);
var name = this.ParseIdentifierToken();
// check to see if the user tried to create a generic enum.
var typeParameters = this.ParseTypeParameterList(allowVariance: true);
if (typeParameters != null)
{
name = AddTrailingSkippedSyntax(name, typeParameters);
name = this.AddError(name, ErrorCode.ERR_UnexpectedGenericName);
}
BaseListSyntax baseList = null;
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
var colon = this.EatToken(SyntaxKind.ColonToken);
var type = this.ParseType(false);
var tmpList = _pool.AllocateSeparated<BaseTypeSyntax>();
tmpList.Add(_syntaxFactory.SimpleBaseType(type));
baseList = _syntaxFactory.BaseList(colon, tmpList);
_pool.Free(tmpList);
}
var members = default(SeparatedSyntaxList<EnumMemberDeclarationSyntax>);
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
if (!openBrace.IsMissing)
{
var builder = _pool.AllocateSeparated<EnumMemberDeclarationSyntax>();
try
{
this.ParseEnumMemberDeclarations(ref openBrace, builder);
members = builder.ToList();
}
finally
{
_pool.Free(builder);
}
}
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
SyntaxToken semicolon = null;
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
semicolon = this.EatToken();
}
return _syntaxFactory.EnumDeclaration(
attributes,
modifiers.ToTokenList(),
enumToken,
name,
baseList,
openBrace,
members,
closeBrace,
semicolon);
}
private void ParseEnumMemberDeclarations(
ref SyntaxToken openBrace,
SeparatedSyntaxListBuilder<EnumMemberDeclarationSyntax> members)
{
if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken)
{
tryAgain:
if (this.IsPossibleEnumMemberDeclaration() || this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
// first member
members.Add(this.ParseEnumMemberDeclaration());
// additional members
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken || this.IsPossibleEnumMemberDeclaration())
{
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
// semicolon instead of comma.. consume it with error and act as if it were a comma.
members.AddSeparator(this.EatTokenWithPrejudice(SyntaxKind.CommaToken));
}
else
{
members.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
}
// check for exit case after legal trailing comma
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (!this.IsPossibleEnumMemberDeclaration())
{
goto tryAgain;
}
members.Add(this.ParseEnumMemberDeclaration());
continue;
}
else if (this.SkipBadEnumMemberListTokens(ref openBrace, members, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadEnumMemberListTokens(ref openBrace, members, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
}
private PostSkipAction SkipBadEnumMemberListTokens(ref SyntaxToken openBrace, SeparatedSyntaxListBuilder<EnumMemberDeclarationSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref openBrace, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && p.CurrentToken.Kind != SyntaxKind.SemicolonToken && !p.IsPossibleEnumMemberDeclaration(),
p => p.CurrentToken.Kind == SyntaxKind.CloseBraceToken || p.IsTerminator(),
expected);
}
private EnumMemberDeclarationSyntax ParseEnumMemberDeclaration()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.EnumMemberDeclaration)
{
return (EnumMemberDeclarationSyntax)this.EatNode();
}
var memberAttrs = _pool.Allocate<AttributeListSyntax>();
try
{
this.ParseAttributeDeclarations(memberAttrs);
var memberName = this.ParseIdentifierToken();
EqualsValueClauseSyntax equalsValue = null;
if (this.CurrentToken.Kind == SyntaxKind.EqualsToken)
{
var equals = this.EatToken(SyntaxKind.EqualsToken);
ExpressionSyntax value;
if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
value = this.CreateMissingIdentifierName(); //an identifier is a valid expression
value = this.AddErrorToFirstToken(value, ErrorCode.ERR_ConstantExpected);
}
else
{
value = this.ParseExpressionCore();
}
equalsValue = _syntaxFactory.EqualsValueClause(equals, value);
}
return _syntaxFactory.EnumMemberDeclaration(memberAttrs, memberName, equalsValue);
}
finally
{
_pool.Free(memberAttrs);
}
}
private bool IsPossibleEnumMemberDeclaration()
{
return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken || this.IsTrueIdentifier();
}
private bool IsDotOrColonColon()
{
return this.CurrentToken.Kind == SyntaxKind.DotToken || this.CurrentToken.Kind == SyntaxKind.ColonColonToken;
}
// This is public and parses open types. You probably don't want to use it.
public NameSyntax ParseName()
{
return this.ParseQualifiedName();
}
private IdentifierNameSyntax CreateMissingIdentifierName()
{
return _syntaxFactory.IdentifierName(CreateMissingIdentifierToken());
}
private static SyntaxToken CreateMissingIdentifierToken()
{
return SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken);
}
[Flags]
private enum NameOptions
{
None = 0,
InExpression = 1 << 0, // Used to influence parser ambiguity around "<" and generics vs. expressions. Used in ParseSimpleName.
InTypeList = 1 << 1, // Allows attributes to appear within the generic type argument list. Used during ParseInstantiation.
}
/// <summary>
/// True if current identifier token is not really some contextual keyword
/// </summary>
/// <returns></returns>
private bool IsTrueIdentifier()
{
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
if (!IsCurrentTokenPartialKeywordOfPartialMethodOrType() &&
!IsCurrentTokenQueryKeywordInQuery())
{
return true;
}
}
return false;
}
private IdentifierNameSyntax ParseIdentifierName()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.IdentifierName)
{
if (!SyntaxFacts.IsContextualKeyword(((CSharp.Syntax.IdentifierNameSyntax)this.CurrentNode).Identifier.Kind()))
{
return (IdentifierNameSyntax)this.EatNode();
}
}
var tk = ParseIdentifierToken();
return SyntaxFactory.IdentifierName(tk);
}
private SyntaxToken ParseIdentifierToken()
{
var ctk = this.CurrentToken.Kind;
if (ctk == SyntaxKind.IdentifierToken)
{
// Error tolerance for IntelliSense. Consider the following case: [EditorBrowsable( partial class Foo {
// } Because we're parsing an attribute argument we'll end up consuming the "partial" identifier and
// we'll eventually end up in an pretty confused state. Because of that it becomes very difficult to
// show the correct parameter help in this case. So, when we see "partial" we check if it's being used
// as an identifier or as a contextual keyword. If it's the latter then we bail out. See
// Bug: vswhidbey/542125
if (IsCurrentTokenPartialKeywordOfPartialMethodOrType() || IsCurrentTokenQueryKeywordInQuery())
{
var result = CreateMissingIdentifierToken();
result = this.AddError(result, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text);
return result;
}
SyntaxToken identifierToken = this.EatToken();
if (this.IsInAsync && identifierToken.ContextualKind == SyntaxKind.AwaitKeyword)
{
identifierToken = this.AddError(identifierToken, ErrorCode.ERR_BadAwaitAsIdentifier);
}
return identifierToken;
}
else
{
var name = CreateMissingIdentifierToken();
name = this.AddError(name, ErrorCode.ERR_IdentifierExpected);
return name;
}
}
private bool IsCurrentTokenQueryKeywordInQuery()
{
return this.IsInQuery && this.IsCurrentTokenQueryContextualKeyword;
}
private bool IsCurrentTokenPartialKeywordOfPartialMethodOrType()
{
if (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)
{
if (this.IsPartialType() || this.IsPartialMember())
{
return true;
}
}
return false;
}
private TypeParameterListSyntax ParseTypeParameterList(bool allowVariance)
{
if (this.CurrentToken.Kind != SyntaxKind.LessThanToken)
{
return null;
}
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfTypeParameterList;
try
{
var parameters = _pool.AllocateSeparated<TypeParameterSyntax>();
var open = this.EatToken(SyntaxKind.LessThanToken);
open = CheckFeatureAvailability(open, MessageID.IDS_FeatureGenerics);
// first parameter
parameters.Add(this.ParseTypeParameter(allowVariance));
// remaining parameter & commas
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.GreaterThanToken || this.IsPossibleTypeParameterConstraintClauseStart())
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
parameters.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
parameters.Add(this.ParseTypeParameter(allowVariance));
}
else if (this.SkipBadTypeParameterListTokens(parameters, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
var close = this.EatToken(SyntaxKind.GreaterThanToken);
return _syntaxFactory.TypeParameterList(open, parameters, close);
}
finally
{
_termState = saveTerm;
}
}
private PostSkipAction SkipBadTypeParameterListTokens(SeparatedSyntaxListBuilder<TypeParameterSyntax> list, SyntaxKind expected)
{
CSharpSyntaxNode tmp = null;
Debug.Assert(list.Count > 0);
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => this.CurrentToken.Kind != SyntaxKind.CommaToken,
p => this.CurrentToken.Kind == SyntaxKind.GreaterThanToken || this.IsTerminator(),
expected);
}
private TypeParameterSyntax ParseTypeParameter(bool allowVariance)
{
if (this.IsPossibleTypeParameterConstraintClauseStart())
{
return _syntaxFactory.TypeParameter(
default(SyntaxList<AttributeListSyntax>),
default(SyntaxToken),
this.AddError(CreateMissingIdentifierToken(), ErrorCode.ERR_IdentifierExpected));
}
var attrs = _pool.Allocate<AttributeListSyntax>();
try
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind != SyntaxKind.CloseBracketToken)
{
var saveTerm = _termState;
_termState = TerminatorState.IsEndOfTypeArgumentList;
this.ParseAttributeDeclarations(attrs);
_termState = saveTerm;
}
SyntaxToken varianceToken = null;
if (this.CurrentToken.Kind == SyntaxKind.InKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword)
{
// Again, we always recognize the variance syntax, but give an error if
// it is not appropriate.
varianceToken = this.EatToken();
varianceToken = CheckFeatureAvailability(varianceToken, MessageID.IDS_FeatureTypeVariance);
if (!allowVariance)
{
varianceToken = this.AddError(varianceToken, ErrorCode.ERR_IllegalVarianceSyntax);
}
}
return _syntaxFactory.TypeParameter(attrs, varianceToken, this.ParseIdentifierToken());
}
finally
{
_pool.Free(attrs);
}
}
// Parses the parts of the names between Dots and ColonColons.
private SimpleNameSyntax ParseSimpleName(NameOptions options = NameOptions.None)
{
var id = this.ParseIdentifierName();
if (id.Identifier.IsMissing)
{
return id;
}
// You can pass ignore generics if you don't even want the parser to consider generics at all.
// The name parsing will then stop at the first "<". It doesn't make sense to pass both Generic and IgnoreGeneric.
SimpleNameSyntax name = id;
if (this.CurrentToken.Kind == SyntaxKind.LessThanToken)
{
var pt = this.GetResetPoint();
var kind = this.ScanTypeArgumentList((options & NameOptions.InExpression) != 0);
this.Reset(ref pt);
this.Release(ref pt);
if (kind == ScanTypeArgumentListKind.DefiniteTypeArgumentList || (kind == ScanTypeArgumentListKind.PossibleTypeArgumentList && (options & NameOptions.InTypeList) != 0))
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.LessThanToken);
SyntaxToken open;
var types = _pool.AllocateSeparated<TypeSyntax>();
SyntaxToken close;
this.ParseTypeArgumentList(out open, types, out close);
name = _syntaxFactory.GenericName(id.Identifier,
_syntaxFactory.TypeArgumentList(open, types, close));
_pool.Free(types);
}
}
return name;
}
private enum ScanTypeArgumentListKind
{
NotTypeArgumentList,
PossibleTypeArgumentList,
DefiniteTypeArgumentList
}
private ScanTypeArgumentListKind ScanTypeArgumentList(bool inExpression)
{
if (this.CurrentToken.Kind == SyntaxKind.LessThanToken)
{
if (inExpression)
{
// Scan for a type argument list. If we think it's a type argument list
// then assume it is unless we see specific tokens following it.
if (this.ScanPossibleTypeArgumentList())
{
var tokenID = this.CurrentToken.Kind;
if (tokenID != SyntaxKind.OpenParenToken &&
tokenID != SyntaxKind.CloseParenToken &&
tokenID != SyntaxKind.CloseBracketToken &&
tokenID != SyntaxKind.ColonToken &&
tokenID != SyntaxKind.SemicolonToken &&
tokenID != SyntaxKind.CommaToken &&
tokenID != SyntaxKind.DotToken &&
tokenID != SyntaxKind.QuestionToken &&
tokenID != SyntaxKind.EqualsEqualsToken &&
tokenID != SyntaxKind.ExclamationEqualsToken &&
// The preceding tokens are from 7.5.4.2 Grammar Ambiguities;
// the following tokens are not.
tokenID != SyntaxKind.AmpersandAmpersandToken &&
tokenID != SyntaxKind.BarBarToken &&
tokenID != SyntaxKind.CaretToken &&
tokenID != SyntaxKind.BarToken &&
tokenID != SyntaxKind.CloseBraceToken &&
tokenID != SyntaxKind.EndOfFileToken)
{
return ScanTypeArgumentListKind.PossibleTypeArgumentList;
}
else
{
return ScanTypeArgumentListKind.DefiniteTypeArgumentList;
}
}
}
else
{
return ScanTypeArgumentListKind.DefiniteTypeArgumentList;
}
}
return ScanTypeArgumentListKind.NotTypeArgumentList;
}
private bool ScanPossibleTypeArgumentList()
{
SyntaxToken lastTokenOfList = null;
return ScanPossibleTypeArgumentList(ref lastTokenOfList) != ScanTypeFlags.NotType;
}
private ScanTypeFlags ScanPossibleTypeArgumentList(ref SyntaxToken lastTokenOfList)
{
if (this.CurrentToken.Kind == SyntaxKind.LessThanToken)
{
ScanTypeFlags result = ScanTypeFlags.GenericTypeOrExpression;
do
{
lastTokenOfList = this.EatToken();
// We currently do not have the ability to scan attributes, so if this is an open square, we early out and assume it is an attribute
if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken)
{
return result;
}
if (this.CurrentToken.Kind == SyntaxKind.GreaterThanToken)
{
lastTokenOfList = EatToken();
return result;
}
switch (this.ScanType(out lastTokenOfList))
{
case ScanTypeFlags.NotType:
lastTokenOfList = null;
return ScanTypeFlags.NotType;
case ScanTypeFlags.MustBeType:
case ScanTypeFlags.GenericTypeOrMethod:
result = ScanTypeFlags.GenericTypeOrMethod;
break;
}
}
while (this.CurrentToken.Kind == SyntaxKind.CommaToken);
if (this.CurrentToken.Kind != SyntaxKind.GreaterThanToken)
{
lastTokenOfList = null;
return ScanTypeFlags.NotType;
}
lastTokenOfList = this.EatToken();
return result;
}
return ScanTypeFlags.NonGenericTypeOrExpression;
}
// ParseInstantiation: Parses the generic argument/parameter parts of the name.
private void ParseTypeArgumentList(out SyntaxToken open, SeparatedSyntaxListBuilder<TypeSyntax> types, out SyntaxToken close)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.LessThanToken);
open = this.EatToken(SyntaxKind.LessThanToken);
open = CheckFeatureAvailability(open, MessageID.IDS_FeatureGenerics);
if (this.IsOpenName())
{
// NOTE: trivia will be attached to comma, not omitted type argument
var omittedTypeArgumentInstance = _syntaxFactory.OmittedTypeArgument(SyntaxFactory.Token(SyntaxKind.OmittedTypeArgumentToken));
types.Add(omittedTypeArgumentInstance);
while (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
types.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
types.Add(omittedTypeArgumentInstance);
}
close = this.EatToken(SyntaxKind.GreaterThanToken);
return;
}
// first type
types.Add(this.ParseTypeArgument());
// remaining types & commas
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.GreaterThanToken || this.IsPossibleTypeParameterConstraintClauseStart())
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType())
{
types.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
types.Add(this.ParseTypeArgument());
}
else if (this.SkipBadTypeArgumentListTokens(types, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
close = this.EatToken(SyntaxKind.GreaterThanToken);
}
private PostSkipAction SkipBadTypeArgumentListTokens(SeparatedSyntaxListBuilder<TypeSyntax> list, SyntaxKind expected)
{
CSharpSyntaxNode tmp = null;
Debug.Assert(list.Count > 0);
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => this.CurrentToken.Kind != SyntaxKind.CommaToken && !this.IsPossibleType(),
p => this.CurrentToken.Kind == SyntaxKind.GreaterThanToken || this.IsTerminator(),
expected);
}
// Parses the individual generic parameter/arguments in a name.
private TypeSyntax ParseTypeArgument()
{
if (this.IsPossibleTypeParameterConstraintClauseStart())
{
return this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected);
}
var attrs = _pool.Allocate<AttributeListSyntax>();
try
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind != SyntaxKind.CloseBracketToken)
{
// Here, if we see a "[" that looks like it has something in it, we parse
// it as an attribute and then later put an error on the whole type if
// it turns out that attributes are not allowed.
// TODO: should there be another flag that controls this behavior? we have
// "allowAttrs" but should there also be a "recognizeAttrs" that we can
// set to false in an expression context?
var saveTerm = _termState;
_termState = TerminatorState.IsEndOfTypeArgumentList;
this.ParseAttributeDeclarations(attrs);
_termState = saveTerm;
}
SyntaxToken varianceToken = null;
if (this.CurrentToken.Kind == SyntaxKind.InKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword)
{
// Recognize the variance syntax, but give an error as it's
// only appropriate in a type parameter list.
varianceToken = this.EatToken();
varianceToken = CheckFeatureAvailability(varianceToken, MessageID.IDS_FeatureTypeVariance);
varianceToken = this.AddError(varianceToken, ErrorCode.ERR_IllegalVarianceSyntax);
}
var result = this.ParseType(parentIsParameter: false);
if (varianceToken != null)
{
result = AddLeadingSkippedSyntax(result, varianceToken);
}
if (attrs.Count > 0)
{
result = AddLeadingSkippedSyntax(result, attrs.ToListNode());
result = this.AddError(result, ErrorCode.ERR_TypeExpected);
}
return result;
}
finally
{
_pool.Free(attrs);
}
}
private bool IsEndOfTypeArgumentList()
{
return this.CurrentToken.Kind == SyntaxKind.GreaterThanToken;
}
private bool IsOpenName()
{
bool isOpen = true;
int n = 0;
while (this.PeekToken(n).Kind == SyntaxKind.CommaToken)
{
n++;
}
if (this.PeekToken(n).Kind != SyntaxKind.GreaterThanToken)
{
isOpen = false;
}
return isOpen;
}
private void ParseMemberName(
out ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt,
out SyntaxToken identifierOrThisOpt,
out TypeParameterListSyntax typeParameterListOpt,
bool isEvent)
{
identifierOrThisOpt = null;
explicitInterfaceOpt = null;
typeParameterListOpt = null;
if (!IsPossibleMemberName())
{
// No clue what this is. Just bail. Our caller will have to
// move forward and try again.
return;
}
NameSyntax explicitInterfaceName = null;
SyntaxToken separator = null;
ResetPoint beforeIdentifierPoint = default(ResetPoint);
bool beforeIdentifierPointSet = false;
try
{
while (true)
{
// Check if we got 'this'. If so, then we have an indexer.
// Note: we parse out type parameters here as well so that
// we can give a useful error about illegal generic indexers.
if (this.CurrentToken.Kind == SyntaxKind.ThisKeyword)
{
beforeIdentifierPoint = GetResetPoint();
beforeIdentifierPointSet = true;
identifierOrThisOpt = this.EatToken();
typeParameterListOpt = this.ParseTypeParameterList(allowVariance: false);
break;
}
// now, scan past the next name. if it's followed by a dot then
// it's part of the explicit name we're building up. Otherwise,
// it's the name of the member.
var point = GetResetPoint();
bool isMemberName;
try
{
ScanNamedTypePart();
isMemberName = !IsDotOrColonColon();
}
finally
{
this.Reset(ref point);
this.Release(ref point);
}
if (isMemberName)
{
// We're past any explicit interface portion and We've
// gotten to the member name.
beforeIdentifierPoint = GetResetPoint();
beforeIdentifierPointSet = true;
if (separator != null && separator.Kind == SyntaxKind.ColonColonToken)
{
separator = this.AddError(separator, ErrorCode.ERR_AliasQualAsExpression);
separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
}
identifierOrThisOpt = this.ParseIdentifierToken();
typeParameterListOpt = this.ParseTypeParameterList(allowVariance: false);
break;
}
else
{
// If we saw a . or :: then we must have something explicit.
// first parse the upcoming name portion.
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfNameInExplicitInterface;
if (explicitInterfaceName == null)
{
// If this is the first time, then just get the next simple
// name and store it as the explicit interface name.
explicitInterfaceName = this.ParseSimpleName(NameOptions.InTypeList);
// Now, get the next separator.
separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken
? this.EatToken() // fine after the first identifier
: this.EatToken(SyntaxKind.DotToken);
}
else
{
// Parse out the next part and combine it with the
// current explicit name to form the new explicit name.
var tmp = this.ParseQualifiedNameRight(NameOptions.InTypeList, explicitInterfaceName, separator);
Debug.Assert(!ReferenceEquals(tmp, explicitInterfaceName), "We should have consumed something and updated explicitInterfaceName");
explicitInterfaceName = tmp;
// Now, get the next separator.
separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken
? this.ConvertToMissingWithTrailingTrivia(this.EatToken(), SyntaxKind.DotToken)
: this.EatToken(SyntaxKind.DotToken);
}
_termState = saveTerm;
}
}
if (explicitInterfaceName != null)
{
if (separator.Kind != SyntaxKind.DotToken)
{
separator = WithAdditionalDiagnostics(separator, GetExpectedTokenError(SyntaxKind.DotToken, separator.Kind, separator.GetLeadingTriviaWidth(), separator.Width));
separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
}
if (isEvent && this.CurrentToken.Kind != SyntaxKind.OpenBraceToken)
{
// CS0071: If you're explicitly implementing an event field, you have to use the accessor form
//
// Good:
// event EventDelegate Parent.E
// {
// add { ... }
// remove { ... }
// }
//
// Bad:
// event EventDelegate Parent.E; //(or anything else where the next token isn't open brace
//
// To recover: rollback to before the name of the field was parsed (just the part after the last
// dot), insert a missing identifier for the field name, insert missing accessors, and then treat
// the event name that's actually there as the beginning of a new member. e.g.
//
// event EventDelegate Parent./*Missing nodes here*/
//
// E;
//
// Rationale: The identifier could be the name of a type at the beginning of an existing member
// declaration (above which someone has started to type an explicit event implementation).
explicitInterfaceOpt = _syntaxFactory.ExplicitInterfaceSpecifier(
explicitInterfaceName,
AddError(separator, ErrorCode.ERR_ExplicitEventFieldImpl));
Debug.Assert(beforeIdentifierPointSet);
Reset(ref beforeIdentifierPoint);
//clear fields that were populated after the reset point
identifierOrThisOpt = null;
typeParameterListOpt = null;
}
else
{
explicitInterfaceOpt = _syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator);
}
}
}
finally
{
if (beforeIdentifierPointSet)
{
Release(ref beforeIdentifierPoint);
}
}
}
private NameSyntax ParseAliasQualifiedName(NameOptions allowedParts = NameOptions.None)
{
NameSyntax name = this.ParseSimpleName(allowedParts);
if (this.CurrentToken.Kind == SyntaxKind.ColonColonToken)
{
var token = this.EatToken();
name = ParseQualifiedNameRight(allowedParts, name, token);
}
return name;
}
private NameSyntax ParseQualifiedName(NameOptions options = NameOptions.None)
{
NameSyntax name = this.ParseAliasQualifiedName(options);
while (this.IsDotOrColonColon())
{
if (this.PeekToken(1).Kind == SyntaxKind.ThisKeyword)
{
break;
}
var separator = this.EatToken();
name = ParseQualifiedNameRight(options, name, separator);
}
return name;
}
private NameSyntax ParseQualifiedNameRight(
NameOptions options,
NameSyntax left,
SyntaxToken separator)
{
var right = this.ParseSimpleName(options);
if (separator.Kind == SyntaxKind.DotToken)
{
return _syntaxFactory.QualifiedName(left, separator, right);
}
else if (separator.Kind == SyntaxKind.ColonColonToken)
{
if (left.Kind != SyntaxKind.IdentifierName)
{
separator = this.AddError(separator, ErrorCode.ERR_UnexpectedAliasedName, separator.ToString());
}
// If the left hand side is not an identifier name then the user has done
// something like Foo.Bar::Blah. We've already made an error node for the
// ::, so just pretend that they typed Foo.Bar.Blah and continue on.
var identifierLeft = left as IdentifierNameSyntax;
if (identifierLeft == null)
{
separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
return _syntaxFactory.QualifiedName(left, separator, right);
}
else
{
if (identifierLeft.Identifier.ContextualKind == SyntaxKind.GlobalKeyword)
{
identifierLeft = _syntaxFactory.IdentifierName(ConvertToKeyword(identifierLeft.Identifier));
}
identifierLeft = CheckFeatureAvailability(identifierLeft, MessageID.IDS_FeatureGlobalNamespace);
// If the name on the right had errors or warnings then we need to preserve
// them in the tree.
return WithAdditionalDiagnostics(_syntaxFactory.AliasQualifiedName(identifierLeft, separator, right), left.GetDiagnostics());
}
}
else
{
return left;
}
}
private SyntaxToken ConvertToMissingWithTrailingTrivia(SyntaxToken token, SyntaxKind expectedKind)
{
var newToken = SyntaxFactory.MissingToken(expectedKind);
newToken = AddTrailingSkippedSyntax(newToken, token);
return newToken;
}
private enum ScanTypeFlags
{
/// <summary>
/// Definitely not a type name.
/// </summary>
NotType,
/// <summary>
/// Definitely a type name: either a predefined type (int, string, etc.) or an array type name (ending with a bracket).
/// </summary>
MustBeType,
/// <summary>
/// Might be a generic (qualified) type name or a method name.
/// </summary>
GenericTypeOrMethod,
/// <summary>
/// Might be a generic (qualified) type name or an expression or a method name.
/// </summary>
GenericTypeOrExpression,
/// <summary>
/// Might be a non-generic (qualified) type name or an expression.
/// </summary>
NonGenericTypeOrExpression,
/// <summary>
/// A type name with alias prefix (Alias::Name)
/// </summary>
AliasQualifiedName,
/// <summary>
/// Nullable type (ending with ?).
/// </summary>
NullableType,
/// <summary>
/// Might be a pointer type or a multiplication.
/// </summary>
PointerOrMultiplication,
}
private bool IsPossibleType()
{
var tk = this.CurrentToken.Kind;
return IsPredefinedType(tk) || this.IsTrueIdentifier();
}
private bool IsPossibleName()
{
return this.IsTrueIdentifier();
}
private ScanTypeFlags ScanType()
{
SyntaxToken lastTokenOfType;
return ScanType(out lastTokenOfType);
}
private ScanTypeFlags ScanType(out SyntaxToken lastTokenOfType)
{
ScanTypeFlags result = this.ScanNonArrayType(out lastTokenOfType);
if (result == ScanTypeFlags.NotType)
{
return result;
}
// Finally, check for array types and nullables.
while (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken)
{
this.EatToken();
if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken)
{
while (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
this.EatToken();
}
if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken)
{
lastTokenOfType = null;
return ScanTypeFlags.NotType;
}
}
lastTokenOfType = this.EatToken();
result = ScanTypeFlags.MustBeType;
}
return result;
}
private void ScanNamedTypePart()
{
SyntaxToken lastTokenOfType;
ScanNamedTypePart(out lastTokenOfType);
}
private ScanTypeFlags ScanNamedTypePart(out SyntaxToken lastTokenOfType)
{
if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken || !this.IsTrueIdentifier())
{
lastTokenOfType = null;
return ScanTypeFlags.NotType;
}
lastTokenOfType = this.EatToken();
if (this.CurrentToken.Kind == SyntaxKind.LessThanToken)
{
return this.ScanPossibleTypeArgumentList(ref lastTokenOfType);
}
else
{
return ScanTypeFlags.NonGenericTypeOrExpression;
}
}
private ScanTypeFlags ScanNonArrayType()
{
SyntaxToken lastTokenOfType;
return ScanNonArrayType(out lastTokenOfType);
}
private ScanTypeFlags ScanNonArrayType(out SyntaxToken lastTokenOfType)
{
ScanTypeFlags result;
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
result = this.ScanNamedTypePart(out lastTokenOfType);
if (result == ScanTypeFlags.NotType)
{
return ScanTypeFlags.NotType;
}
bool isAlias = this.CurrentToken.Kind == SyntaxKind.ColonColonToken;
// Scan a name
for (bool firstLoop = true; IsDotOrColonColon(); firstLoop = false)
{
if (!firstLoop && isAlias)
{
isAlias = false;
}
lastTokenOfType = this.EatToken();
result = this.ScanNamedTypePart(out lastTokenOfType);
if (result == ScanTypeFlags.NotType)
{
return ScanTypeFlags.NotType;
}
}
if (isAlias)
{
result = ScanTypeFlags.AliasQualifiedName;
}
}
else if (IsPredefinedType(this.CurrentToken.Kind))
{
// Simple type...
lastTokenOfType = this.EatToken();
result = ScanTypeFlags.MustBeType;
}
else
{
// Can't be a type!
lastTokenOfType = null;
return ScanTypeFlags.NotType;
}
if (this.CurrentToken.Kind == SyntaxKind.QuestionToken)
{
lastTokenOfType = this.EatToken();
result = ScanTypeFlags.NullableType;
}
// Now check for pointer type(s)
while (this.CurrentToken.Kind == SyntaxKind.AsteriskToken)
{
lastTokenOfType = this.EatToken();
if (result == ScanTypeFlags.GenericTypeOrExpression || result == ScanTypeFlags.NonGenericTypeOrExpression)
{
result = ScanTypeFlags.PointerOrMultiplication;
}
else if (result == ScanTypeFlags.GenericTypeOrMethod)
{
result = ScanTypeFlags.MustBeType;
}
}
return result;
}
private static bool IsPredefinedType(SyntaxKind keyword)
{
return SyntaxFacts.IsPredefinedType(keyword);
}
public TypeSyntax ParseTypeName()
{
return ParseType(parentIsParameter: false);
}
private TypeSyntax ParseTypeOrVoid()
{
if (this.CurrentToken.Kind == SyntaxKind.VoidKeyword && this.PeekToken(1).Kind != SyntaxKind.AsteriskToken)
{
// Must be 'void' type, so create such a type node and return it.
return _syntaxFactory.PredefinedType(this.EatToken());
}
return this.ParseType(parentIsParameter: false);
}
private TypeSyntax ParseType(bool parentIsParameter)
{
return ParseTypeCore(parentIsParameter, isOrAs: false, expectSizes: false, isArrayCreation: false);
}
private bool IsTerm()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.ArgListKeyword:
case SyntaxKind.MakeRefKeyword:
case SyntaxKind.RefTypeKeyword:
case SyntaxKind.RefValueKeyword:
case SyntaxKind.BaseKeyword:
case SyntaxKind.CheckedKeyword:
case SyntaxKind.DefaultKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NewKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.SizeOfKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.UncheckedKeyword:
case SyntaxKind.NumericLiteralToken:
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteralToken:
case SyntaxKind.CharacterLiteralToken:
case SyntaxKind.OpenParenToken:
case SyntaxKind.EqualsGreaterThanToken:
case SyntaxKind.InterpolatedStringToken:
case SyntaxKind.InterpolatedStringStartToken:
return true;
case SyntaxKind.IdentifierToken:
return this.IsTrueIdentifier();
default:
return false;
}
}
private TypeSyntax ParseTypeCore(
bool parentIsParameter,
bool isOrAs,
bool expectSizes,
bool isArrayCreation)
{
var type = this.ParseUnderlyingType(parentIsParameter);
if (this.CurrentToken.Kind == SyntaxKind.QuestionToken)
{
var resetPoint = this.GetResetPoint();
try
{
var question = this.EatToken();
if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind)))
{
this.Reset(ref resetPoint);
Debug.Assert(type != null);
return type;
}
question = CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable);
type = _syntaxFactory.NullableType(type, question);
}
finally
{
this.Release(ref resetPoint);
}
}
// Check for pointer types (only if pType is NOT an array type)
type = this.ParsePointerTypeMods(type);
// Now check for arrays.
if (this.IsPossibleRankAndDimensionSpecifier())
{
var ranks = _pool.Allocate<ArrayRankSpecifierSyntax>();
try
{
while (this.IsPossibleRankAndDimensionSpecifier())
{
bool unused;
var rank = this.ParseArrayRankSpecifier(isArrayCreation, expectSizes, out unused);
ranks.Add(rank);
expectSizes = false;
}
type = _syntaxFactory.ArrayType(type, ranks);
}
finally
{
_pool.Free(ranks);
}
}
Debug.Assert(type != null);
return type;
}
private bool IsPossibleRankAndDimensionSpecifier()
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken)
{
// When specifying rank and dimension, only commas and close square
// brackets are valid after an open square bracket. However, we accept
// numbers as well as the user might (mistakenly) try to specify the
// array size here. This way, when the parser actually consumes these
// tokens it will be able to specify an appropriate error message.
/*
SyntaxKind k = this.PeekToken(1).Kind;
if (k == SyntaxKind.Comma ||
k == SyntaxKind.CloseBracket ||
k == SyntaxKind.NumericLiteral)
{
return true;
}
*/
return true;
}
return false;
}
private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, bool expectSizes, out bool sawNonOmittedSize)
{
sawNonOmittedSize = false;
bool sawOmittedSize = false;
var open = this.EatToken(SyntaxKind.OpenBracketToken);
var list = _pool.AllocateSeparated<ExpressionSyntax>();
try
{
var omittedArraySizeExpressionInstance = _syntaxFactory.OmittedArraySizeExpression(SyntaxFactory.Token(SyntaxKind.OmittedArraySizeExpressionToken));
while (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken)
{
if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// NOTE: trivia will be attached to comma, not omitted array size
sawOmittedSize = true;
list.Add(omittedArraySizeExpressionInstance);
list.AddSeparator(this.EatToken());
}
else if (this.IsPossibleExpression())
{
var size = this.ParseExpressionCore();
sawNonOmittedSize = true;
if (!expectSizes)
{
size = this.AddError(size, isArrayCreation ? ErrorCode.ERR_InvalidArray : ErrorCode.ERR_ArraySizeInDeclaration);
}
list.Add(size);
if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken)
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
}
}
else if (this.SkipBadArrayRankSpecifierTokens(ref open, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
// Don't end on a comma.
// If the omitted size would be the only element, then skip it unless sizes were expected.
if (((list.Count & 1) == 0))
{
sawOmittedSize = true;
list.Add(omittedArraySizeExpressionInstance);
}
// Never mix omitted and non-omitted array sizes. If there were non-omitted array sizes,
// then convert all of the omitted array sizes to missing identifiers.
if (sawOmittedSize && sawNonOmittedSize)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].Kind == SyntaxKind.OmittedArraySizeExpression)
{
int width = list[i].Width;
int offset = list[i].GetLeadingTriviaWidth();
list[i] = this.AddError(this.CreateMissingIdentifierName(), offset, width, ErrorCode.ERR_ValueExpected);
}
}
}
// Eat the close brace and we're done.
var close = this.EatToken(SyntaxKind.CloseBracketToken);
return _syntaxFactory.ArrayRankSpecifier(open, list, close);
}
finally
{
_pool.Free(list);
}
}
private PostSkipAction SkipBadArrayRankSpecifierTokens(ref SyntaxToken openBracket, SeparatedSyntaxListBuilder<ExpressionSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref openBracket, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleExpression(),
p => p.CurrentToken.Kind == SyntaxKind.CloseBracketToken || p.IsTerminator(),
expected);
}
private TypeSyntax ParseUnderlyingType(bool parentIsParameter)
{
if (IsPredefinedType(this.CurrentToken.Kind))
{
// This is a predefined type
var token = this.EatToken();
if (token.Kind == SyntaxKind.VoidKeyword && this.CurrentToken.Kind != SyntaxKind.AsteriskToken)
{
token = this.AddError(token, parentIsParameter ? ErrorCode.ERR_NoVoidParameter : ErrorCode.ERR_NoVoidHere);
}
return _syntaxFactory.PredefinedType(token);
}
else if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
return this.ParseQualifiedName();
}
else
{
var name = this.CreateMissingIdentifierName();
return this.AddError(name, ErrorCode.ERR_TypeExpected);
}
}
private TypeSyntax ParsePointerTypeMods(TypeSyntax type)
{
// Check for pointer types
while (this.CurrentToken.Kind == SyntaxKind.AsteriskToken)
{
type = _syntaxFactory.PointerType(type, this.EatToken());
}
return type;
}
public StatementSyntax ParseStatement()
{
return ParseWithStackGuard(
ParseStatementCore,
() => SyntaxFactory.EmptyStatement(SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)));
}
private StatementSyntax ParseStatementCore()
{
try
{
_recursionDepth++;
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNode is CSharp.Syntax.StatementSyntax)
{
return (StatementSyntax)this.EatNode();
}
// First, try to parse as a non-declaration statement. If the statement is a single
// expression then we only allow legal expression statements. (That is, "new C();",
// "C();", "x = y;" and so on.)
StatementSyntax result = ParseStatementNoDeclaration(allowAnyExpression: false);
if (result != null)
{
return result;
}
// We could not successfully parse the statement as a non-declaration. Try to parse
// it as either a declaration or as an "await X();" statement that is in a non-async
// method.
return ParsePossibleBadAwaitStatement();
}
finally
{
_recursionDepth--;
}
}
private StatementSyntax ParsePossibleBadAwaitStatement()
{
ResetPoint resetPointBeforeStatement = this.GetResetPoint();
StatementSyntax result = ParsePossibleBadAwaitStatement(ref resetPointBeforeStatement);
this.Release(ref resetPointBeforeStatement);
return result;
}
private StatementSyntax ParsePossibleBadAwaitStatement(ref ResetPoint resetPointBeforeStatement)
{
// Precondition: We have already attempted to parse the statement as a non-declaration and failed.
//
// That means that we are in one of the following cases:
//
// 1) This is a perfectly mundane and correct local declaration statement like "int x;"
// 2) This is a perfectly mundane but erroneous local declaration statement, like "int X();"
// 3) We are in the rare case of the code containing "await x;" and the intention is that
// "await" is the type of "x". This only works in a non-async method.
// 4) We have what would be a legal await statement, like "await X();", but we are not in
// an async method, so the parse failed. (Had we been in an async method then the parse
// attempt done by our caller would have succeeded.)
// 5) The statement begins with "await" but is not a legal local declaration and not a legal
// await expression regardless of whether the method is marked as "async".
bool beginsWithAwait = this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword;
StatementSyntax result = ParseLocalDeclarationStatement();
// Cases (1), (2) and (3):
if (!beginsWithAwait || !result.ContainsDiagnostics)
{
return result;
}
// The statement begins with "await" and could not be parsed as a legal declaration statement.
// We know from our precondition that it is not a legal "await X();" statement, though it is
// possible that it was only not legal because we were not in an async context.
Debug.Assert(!IsInAsync);
// Let's see if we're in case (4). Pretend that we're in an async method and see if parsing
// a non-declaration statement would have succeeded.
this.Reset(ref resetPointBeforeStatement);
IsInAsync = true;
result = ParseStatementNoDeclaration(allowAnyExpression: false);
IsInAsync = false;
if (!result.ContainsDiagnostics)
{
// We are in case (4). We do not report that we have an "await" expression in a non-async
// method at parse time; rather we do that in BindAwait(), during the initial round of
// semantic analysis.
return result;
}
// We are in case (5); we can't figure out what is going on here. Our best guess is that it is
// a malformed local declaration, so back up and re-parse it.
this.Reset(ref resetPointBeforeStatement);
result = ParseLocalDeclarationStatement();
Debug.Assert(result.ContainsDiagnostics);
return result;
}
/// <summary>
/// Parses any statement but a declaration statement. Returns null if the lookahead looks like a declaration.
/// </summary>
/// <remarks>
/// Variable declarations in global code are parsed as field declarations so we need to fallback if we encounter a declaration statement.
/// </remarks>
private StatementSyntax ParseStatementNoDeclaration(bool allowAnyExpression)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.FixedKeyword:
return this.ParseFixedStatement();
case SyntaxKind.BreakKeyword:
return this.ParseBreakStatement();
case SyntaxKind.ContinueKeyword:
return this.ParseContinueStatement();
case SyntaxKind.TryKeyword:
case SyntaxKind.CatchKeyword:
case SyntaxKind.FinallyKeyword:
return this.ParseTryStatement();
case SyntaxKind.CheckedKeyword:
case SyntaxKind.UncheckedKeyword:
return this.ParseCheckedStatement();
case SyntaxKind.ConstKeyword:
return null;
case SyntaxKind.DoKeyword:
return this.ParseDoStatement();
case SyntaxKind.ForKeyword:
case SyntaxKind.ForEachKeyword:
return this.ParseForOrForEachStatement();
case SyntaxKind.GotoKeyword:
return this.ParseGotoStatement();
case SyntaxKind.IfKeyword:
return this.ParseIfStatement();
case SyntaxKind.LockKeyword:
return this.ParseLockStatement();
case SyntaxKind.ReturnKeyword:
return this.ParseReturnStatement();
case SyntaxKind.SwitchKeyword:
return this.ParseSwitchStatement();
case SyntaxKind.ThrowKeyword:
return this.ParseThrowStatement();
case SyntaxKind.UnsafeKeyword:
return this.ParseUnsafeStatement();
case SyntaxKind.UsingKeyword:
return this.ParseUsingStatement();
case SyntaxKind.WhileKeyword:
return this.ParseWhileStatement();
case SyntaxKind.OpenBraceToken:
return this.ParseBlock();
case SyntaxKind.SemicolonToken:
return _syntaxFactory.EmptyStatement(this.EatToken());
case SyntaxKind.IdentifierToken:
if (this.IsPossibleLabeledStatement())
{
return this.ParseLabeledStatement();
}
else if (this.IsPossibleYieldStatement())
{
return this.ParseYieldStatement();
}
else if (this.IsPossibleAwaitExpressionStatement())
{
return this.ParseExpressionStatement();
}
else if (this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: allowAnyExpression))
{
return this.ParseExpressionStatement(this.ParseQueryExpression(0));
}
else
{
goto default;
}
default:
if (this.IsPossibleLocalDeclarationStatement(allowAnyExpression))
{
return null;
}
else
{
return this.ParseExpressionStatement();
}
}
}
private bool IsPossibleLabeledStatement()
{
return this.PeekToken(1).Kind == SyntaxKind.ColonToken && this.IsTrueIdentifier();
}
private bool IsPossibleYieldStatement()
{
return this.CurrentToken.ContextualKind == SyntaxKind.YieldKeyword && (this.PeekToken(1).Kind == SyntaxKind.ReturnKeyword || this.PeekToken(1).Kind == SyntaxKind.BreakKeyword);
}
private bool IsPossibleLocalDeclarationStatement(bool allowAnyExpression)
{
// This method decides whether to parse a statement as a
// declaration or as an expression statement. In the old
// compiler it would simple call IsLocalDeclaration.
var tk = this.CurrentToken.Kind;
if ((SyntaxFacts.IsPredefinedType(tk) && this.PeekToken(1).Kind != SyntaxKind.DotToken) || IsDeclarationModifier(tk))
{
return true;
}
bool? typedIdentifier = IsPossibleTypedIdentifierStart(this.CurrentToken, this.PeekToken(1), allowThisKeyword: false);
if (typedIdentifier != null)
{
return typedIdentifier.Value;
}
var resetPoint = this.GetResetPoint();
try
{
ScanTypeFlags st = this.ScanType();
// We could always return true for st == AliasQualName in addition to MustBeType on the first line, however, we want it to return false in the case where
// CurrentToken.Kind != SyntaxKind.Identifier so that error cases, like: A::N(), are not parsed as variable declarations and instead are parsed as A.N() where we can give
// a better error message saying "did you meant to use a '.'?"
if (st == ScanTypeFlags.MustBeType && this.CurrentToken.Kind != SyntaxKind.DotToken)
{
return true;
}
if (st == ScanTypeFlags.NotType || this.CurrentToken.Kind != SyntaxKind.IdentifierToken)
{
return false;
}
// T? and T* might start an expression, we need to parse further to disambiguate:
if (allowAnyExpression)
{
if (st == ScanTypeFlags.PointerOrMultiplication)
{
return false;
}
else if (st == ScanTypeFlags.NullableType)
{
return IsPossibleDeclarationStatementFollowingNullableType();
}
}
return true;
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}
// Looks ahead for a declaration of a field, property or method declaration following a nullable type T?.
private bool IsPossibleDeclarationStatementFollowingNullableType()
{
if (IsFieldDeclaration(isEvent: false))
{
return IsPossibleFieldDeclarationFollowingNullableType();
}
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt;
SyntaxToken identifierOrThisOpt;
TypeParameterListSyntax typeParameterListOpt;
this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterListOpt, isEvent: false);
if (explicitInterfaceOpt == null && identifierOrThisOpt == null && typeParameterListOpt == null)
{
return false;
}
// looks like a property:
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
return true;
}
// don't accept indexers:
if (identifierOrThisOpt.Kind == SyntaxKind.ThisKeyword)
{
return false;
}
return IsPossibleMethodDeclarationFollowingNullableType();
}
// At least one variable declaration terminated by a semicolon or a comma.
// idf;
// idf,
// idf = <expr>;
// idf = <expr>,
private bool IsPossibleFieldDeclarationFollowingNullableType()
{
if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken)
{
return false;
}
this.EatToken();
if (this.CurrentToken.Kind == SyntaxKind.EqualsToken)
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfFieldDeclaration;
this.EatToken();
this.ParseVariableInitializer(allowStackAlloc: false);
_termState = saveTerm;
}
return this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken;
}
private bool IsPossibleMethodDeclarationFollowingNullableType()
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfMethodSignature;
var paramList = this.ParseParenthesizedParameterList(allowThisKeyword: true, allowDefaults: true, allowAttributes: true);
_termState = saveTerm;
var separatedParameters = paramList.Parameters.GetWithSeparators();
// parsed full signature:
if (!paramList.CloseParenToken.IsMissing)
{
// (...) {
// (...) where
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword)
{
return true;
}
// disambiguates conditional expressions
// (...) :
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
return false;
}
}
// no parameters, just an open paren followed by a token that doesn't belong to a parameter definition:
if (separatedParameters.Count == 0)
{
return false;
}
var parameter = (ParameterSyntax)separatedParameters[0];
// has an attribute:
// ([Attr]
if (parameter.AttributeLists.Count > 0)
{
return true;
}
// has params modifier:
// (params
for (int i = 0; i < parameter.Modifiers.Count; i++)
{
if (parameter.Modifiers[i].Kind == SyntaxKind.ParamsKeyword)
{
return true;
}
}
if (parameter.Type == null)
{
// has arglist:
// (__arglist
if (parameter.Identifier.Kind == SyntaxKind.ArgListKeyword)
{
return true;
}
}
else if (parameter.Type.Kind == SyntaxKind.NullableType)
{
// nullable type with modifiers
// (ref T?
// (out T?
if (parameter.Modifiers.Count > 0)
{
return true;
}
// nullable type, identifier, and separator or closing parent
// (T ? idf,
// (T ? idf)
if (!parameter.Identifier.IsMissing &&
(separatedParameters.Count >= 2 && !separatedParameters[1].IsMissing ||
separatedParameters.Count == 1 && !paramList.CloseParenToken.IsMissing))
{
return true;
}
}
else if (parameter.Type.Kind == SyntaxKind.IdentifierName &&
((IdentifierNameSyntax)parameter.Type).Identifier.ContextualKind == SyntaxKind.FromKeyword)
{
// assume that "from" is meant to be a query start ("from" bound to a type is rare):
// (from
return false;
}
else
{
// has a name and a non-nullable type:
// (T idf
// (ref T idf
// (out T idf
if (!parameter.Identifier.IsMissing)
{
return true;
}
}
return false;
}
private bool IsPossibleNewExpression()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.NewKeyword);
// skip new
SyntaxToken nextToken = PeekToken(1);
// new { }
// new [ ]
switch (nextToken.Kind)
{
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken:
return true;
}
//
// Declaration with new modifier vs. new expression
// Parse it as an expression if the type is not followed by an identifier or this keyword.
//
// Member declarations:
// new T Idf ...
// new T this ...
// new partial Idf ("partial" as a type name)
// new partial this ("partial" as a type name)
// new partial T Idf
// new partial T this
// new <modifier>
// new <class|interface|struct|enum>
// new partial <class|interface|struct|enum>
//
// New expressions:
// new T []
// new T { }
// new <non-type>
//
if (SyntaxFacts.GetBaseTypeDeclarationKind(nextToken.Kind) != SyntaxKind.None)
{
return false;
}
SyntaxModifier modifier = GetModifier(nextToken);
if (modifier == SyntaxModifier.Partial)
{
if (SyntaxFacts.IsPredefinedType(PeekToken(2).Kind))
{
return false;
}
// class, struct, enum, interface keywords, but also other modifiers that are not allowed after
// partial keyword but start class declaration, so we can assume the user just swapped them.
if (IsPossibleStartOfTypeDeclaration(PeekToken(2).Kind))
{
return false;
}
}
else if (modifier != SyntaxModifier.None)
{
return false;
}
bool? typedIdentifier = IsPossibleTypedIdentifierStart(nextToken, PeekToken(2), allowThisKeyword: true);
if (typedIdentifier != null)
{
// new Idf Idf
// new Idf .
// new partial T
// new partial .
return !typedIdentifier.Value;
}
var resetPoint = this.GetResetPoint();
try
{
// skips new keyword
EatToken();
ScanTypeFlags st = this.ScanType();
return !IsPossibleMemberName() || st == ScanTypeFlags.NotType;
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}
/// <returns>
/// true if the current token can be the first token of a typed identifier (a type name followed by an identifier),
/// false if it definitely can't be,
/// null if we need to scan further to find out.
/// </returns>
private static bool? IsPossibleTypedIdentifierStart(SyntaxToken current, SyntaxToken next, bool allowThisKeyword)
{
if (current.Kind == SyntaxKind.IdentifierToken)
{
switch (next.Kind)
{
// tokens that can be in type names...
case SyntaxKind.DotToken:
case SyntaxKind.AsteriskToken:
case SyntaxKind.QuestionToken:
case SyntaxKind.OpenBracketToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.ColonColonToken:
return null;
case SyntaxKind.IdentifierToken:
return true;
case SyntaxKind.ThisKeyword:
return allowThisKeyword;
default:
return false;
}
}
return null;
}
// If "isMethodBody" is true, then this is the immediate body of a method/accessor.
// In this case, we create a many-child list if the body is not a small single statement.
// This then allows a "with many weak children" red node when the red node is created.
// If "isAccessorBody" is true, then we produce a special diagnostic if the open brace is
// missing. Also, "isMethodBody" must be true.
private BlockSyntax ParseBlock(bool isMethodBody = false, bool isAccessorBody = false)
{
// This makes logical sense, but isn't actually required.
Debug.Assert(!isAccessorBody || isMethodBody, "An accessor body is a method body.");
// Check again for incremental re-use, since ParseBlock is called from a bunch of places
// other than ParseStatementCore()
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.Block)
{
return (BlockSyntax)this.EatNode();
}
// There's a special error code for a missing token after an accessor keyword
var openBrace = isAccessorBody && this.CurrentToken.Kind != SyntaxKind.OpenBraceToken
? this.AddError(SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken), ErrorCode.ERR_SemiOrLBraceExpected)
: this.EatToken(SyntaxKind.OpenBraceToken);
var statements = _pool.Allocate<StatementSyntax>();
try
{
CSharpSyntaxNode tmp = openBrace;
this.ParseStatements(ref tmp, statements, stopOnSwitchSections: false);
openBrace = (SyntaxToken)tmp;
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
SyntaxList<StatementSyntax> statementList;
if (isMethodBody && IsLargeEnoughNonEmptyStatementList(statements))
{
// Force creation a many-children list, even if only 1, 2, or 3 elements in the statement list.
statementList = new SyntaxList<StatementSyntax>(SyntaxList.List(((SyntaxListBuilder)statements).ToArray()));
}
else
{
statementList = statements;
}
return _syntaxFactory.Block(openBrace, statementList, closeBrace);
}
finally
{
_pool.Free(statements);
}
}
// Is this statement list non-empty, and large enough to make using weak children beneficial?
private static bool IsLargeEnoughNonEmptyStatementList(SyntaxListBuilder<StatementSyntax> statements)
{
if (statements.Count == 0)
{
return false;
}
else if (statements.Count == 1)
{
// If we have a single statement, it might be small, like "return null", or large,
// like a loop or if or switch with many statements inside. Use the width as a proxy for
// how big it is. If it's small, its better to forgo a many children list anyway, since the
// weak reference would consume as much memory as is saved.
return statements[0].Width > 60;
}
else
{
// For 2 or more statements, go ahead and create a many-children lists.
return true;
}
}
private void ParseStatements(ref CSharpSyntaxNode previousNode, SyntaxListBuilder<StatementSyntax> statements, bool stopOnSwitchSections)
{
var saveTerm = _termState;
_termState |= TerminatorState.IsPossibleStatementStartOrStop; // partial statements can abort if a new statement starts
if (stopOnSwitchSections)
{
_termState |= TerminatorState.IsSwitchSectionStart;
}
while (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken
&& this.CurrentToken.Kind != SyntaxKind.EndOfFileToken
&& !(stopOnSwitchSections && this.IsPossibleSwitchSection()))
{
if (this.IsPossibleStatement())
{
var statement = this.ParseStatementCore();
statements.Add(statement);
}
else
{
CSharpSyntaxNode trailingTrivia;
var action = this.SkipBadStatementListTokens(statements, SyntaxKind.CloseBraceToken, out trailingTrivia);
if (trailingTrivia != null)
{
previousNode = AddTrailingSkippedSyntax(previousNode, trailingTrivia);
}
if (action == PostSkipAction.Abort)
{
break;
}
}
}
_termState = saveTerm;
}
private bool IsPossibleStatementStartOrStop()
{
return this.CurrentToken.Kind == SyntaxKind.SemicolonToken
|| this.IsPossibleStatement();
}
private PostSkipAction SkipBadStatementListTokens(SyntaxListBuilder<StatementSyntax> statements, SyntaxKind expected, out CSharpSyntaxNode trailingTrivia)
{
return this.SkipBadListTokensWithExpectedKindHelper(
statements,
p => !p.IsPossibleStatement(),
p => p.CurrentToken.Kind == SyntaxKind.CloseBraceToken || p.IsTerminator(),
expected,
out trailingTrivia
);
}
private bool IsPossibleStatement()
{
var tk = this.CurrentToken.Kind;
switch (tk)
{
case SyntaxKind.FixedKeyword:
case SyntaxKind.BreakKeyword:
case SyntaxKind.ContinueKeyword:
case SyntaxKind.TryKeyword:
case SyntaxKind.CheckedKeyword:
case SyntaxKind.UncheckedKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DoKeyword:
case SyntaxKind.ForKeyword:
case SyntaxKind.ForEachKeyword:
case SyntaxKind.GotoKeyword:
case SyntaxKind.IfKeyword:
case SyntaxKind.LockKeyword:
case SyntaxKind.ReturnKeyword:
case SyntaxKind.SwitchKeyword:
case SyntaxKind.ThrowKeyword:
case SyntaxKind.UnsafeKeyword:
case SyntaxKind.UsingKeyword:
case SyntaxKind.WhileKeyword:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.SemicolonToken:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.VolatileKeyword:
return true;
case SyntaxKind.IdentifierToken:
return IsTrueIdentifier();
case SyntaxKind.CatchKeyword:
case SyntaxKind.FinallyKeyword:
return !_isInTry;
default:
return IsPredefinedType(tk)
|| IsPossibleExpression();
}
}
private FixedStatementSyntax ParseFixedStatement()
{
var @fixed = this.EatToken(SyntaxKind.FixedKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
TypeSyntax type;
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfFixedStatement;
this.ParseDeclaration(false, out type, variables);
_termState = saveTerm;
var decl = _syntaxFactory.VariableDeclaration(type, variables);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
StatementSyntax statement = this.ParseEmbeddedStatement(false);
return _syntaxFactory.FixedStatement(@fixed, openParen, decl, closeParen, statement);
}
finally
{
_pool.Free(variables);
}
}
private bool IsEndOfFixedStatement()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.CurrentToken.Kind == SyntaxKind.SemicolonToken;
}
private StatementSyntax ParseEmbeddedStatement(bool complexCheck)
{
StatementSyntax statement;
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken && (!complexCheck || this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken))
{
statement = this.ParseStatementCore();
statement = this.AddError(statement, ErrorCode.WRN_PossibleMistakenNullStatement);
}
else
{
statement = this.ParseStatementCore();
}
// 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.
if (statement != null && (statement.Kind == SyntaxKind.LabeledStatement || statement.Kind == SyntaxKind.LocalDeclarationStatement))
{
statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt);
}
return statement;
}
private BreakStatementSyntax ParseBreakStatement()
{
var breakKeyword = this.EatToken(SyntaxKind.BreakKeyword);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.BreakStatement(breakKeyword, semicolon);
}
private ContinueStatementSyntax ParseContinueStatement()
{
var continueKeyword = this.EatToken(SyntaxKind.ContinueKeyword);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.ContinueStatement(continueKeyword, semicolon);
}
private TryStatementSyntax ParseTryStatement()
{
var isInTry = _isInTry;
_isInTry = true;
var @try = this.EatToken(SyntaxKind.TryKeyword);
BlockSyntax block;
if (@try.IsMissing)
{
block = _syntaxFactory.Block(this.EatToken(SyntaxKind.OpenBraceToken), default(SyntaxList<StatementSyntax>), this.EatToken(SyntaxKind.CloseBraceToken));
}
else
{
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfTryBlock;
block = this.ParseBlock();
_termState = saveTerm;
}
var catches = default(SyntaxListBuilder<CatchClauseSyntax>);
FinallyClauseSyntax @finally = null;
try
{
bool hasEnd = false;
bool hasCatchAll = false;
if (this.CurrentToken.Kind == SyntaxKind.CatchKeyword)
{
hasEnd = true;
catches = _pool.Allocate<CatchClauseSyntax>();
while (this.CurrentToken.Kind == SyntaxKind.CatchKeyword)
{
var clause = this.ParseCatchClause(hasCatchAll);
hasCatchAll |= clause.Declaration == null && clause.Filter == null;
catches.Add(clause);
}
}
if (this.CurrentToken.Kind == SyntaxKind.FinallyKeyword)
{
hasEnd = true;
var fin = this.EatToken();
var finBlock = this.ParseBlock();
@finally = _syntaxFactory.FinallyClause(fin, finBlock);
}
if (!hasEnd)
{
block = this.AddErrorToLastToken(block, ErrorCode.ERR_ExpectedEndTry);
// synthesize missing tokens for "finally { }":
@finally = _syntaxFactory.FinallyClause(
SyntaxToken.CreateMissing(SyntaxKind.FinallyKeyword, null, null),
_syntaxFactory.Block(
SyntaxToken.CreateMissing(SyntaxKind.OpenBraceToken, null, null),
default(SyntaxList<StatementSyntax>),
SyntaxToken.CreateMissing(SyntaxKind.CloseBraceToken, null, null)));
}
_isInTry = isInTry;
return _syntaxFactory.TryStatement(@try, block, catches, @finally);
}
finally
{
if (!catches.IsNull)
{
_pool.Free(catches);
}
}
}
private bool IsEndOfTryBlock()
{
return this.CurrentToken.Kind == SyntaxKind.CloseBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CatchKeyword
|| this.CurrentToken.Kind == SyntaxKind.FinallyKeyword;
}
private CatchClauseSyntax ParseCatchClause(bool hasCatchAll)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.CatchKeyword);
var @catch = this.EatToken();
// Check for the error of catch clause following empty catch here.
if (hasCatchAll)
{
@catch = this.AddError(@catch, ErrorCode.ERR_TooManyCatches);
}
CatchDeclarationSyntax decl = null;
var saveTerm = _termState;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
var openParen = this.EatToken();
_termState |= TerminatorState.IsEndOfCatchClause;
var type = this.ParseClassType();
SyntaxToken name = null;
if (this.IsTrueIdentifier())
{
name = this.ParseIdentifierToken();
}
_termState = saveTerm;
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
decl = _syntaxFactory.CatchDeclaration(openParen, type, name, closeParen);
}
CatchFilterClauseSyntax filter = null;
var keywordKind = this.CurrentToken.ContextualKind;
if (keywordKind == SyntaxKind.WhenKeyword || keywordKind == SyntaxKind.IfKeyword)
{
var whenKeyword = this.EatContextualToken(SyntaxKind.WhenKeyword);
if (keywordKind == SyntaxKind.IfKeyword)
{
// The initial design of C# exception filters called for the use of the
// "if" keyword in this position. We've since changed to "when", but
// the error recovery experience for early adopters (and for old source
// stored in the symbol server) will be better if we consume "if" as
// though it were "when".
whenKeyword = AddTrailingSkippedSyntax(whenKeyword, EatToken());
}
whenKeyword = CheckFeatureAvailability(whenKeyword, MessageID.IDS_FeatureExceptionFilter);
_termState |= TerminatorState.IsEndOfilterClause;
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var filterExpression = this.ParseExpressionCore();
_termState = saveTerm;
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
filter = _syntaxFactory.CatchFilterClause(whenKeyword, openParen, filterExpression, closeParen);
}
_termState |= TerminatorState.IsEndOfCatchBlock;
var block = this.ParseBlock();
_termState = saveTerm;
return _syntaxFactory.CatchClause(@catch, decl, filter, block);
}
private bool IsEndOfCatchClause()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CloseBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CatchKeyword
|| this.CurrentToken.Kind == SyntaxKind.FinallyKeyword;
}
private bool IsEndOfFilterClause()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CloseBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CatchKeyword
|| this.CurrentToken.Kind == SyntaxKind.FinallyKeyword;
}
private bool IsEndOfCatchBlock()
{
return this.CurrentToken.Kind == SyntaxKind.CloseBraceToken
|| this.CurrentToken.Kind == SyntaxKind.CatchKeyword
|| this.CurrentToken.Kind == SyntaxKind.FinallyKeyword;
}
private TypeSyntax ParseClassType()
{
var type = this.ParseType(false);
switch (type.Kind)
{
case SyntaxKind.PredefinedType:
var kt = ((PredefinedTypeSyntax)type).Keyword.Kind;
if (kt != SyntaxKind.ObjectKeyword && kt != SyntaxKind.StringKeyword)
{
goto default;
}
break;
default:
if (!SyntaxFacts.IsName(type.Kind))
{
type = this.AddError(type, ErrorCode.ERR_ClassTypeExpected);
}
break;
}
return type;
}
private StatementSyntax ParseCheckedStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.CheckedKeyword || this.CurrentToken.Kind == SyntaxKind.UncheckedKeyword);
if (this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)
{
return this.ParseExpressionStatement();
}
var spec = this.EatToken();
var block = this.ParseBlock();
return _syntaxFactory.CheckedStatement(SyntaxFacts.GetCheckStatement(spec.Kind), spec, block);
}
private DoStatementSyntax ParseDoStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DoKeyword);
var @do = this.EatToken(SyntaxKind.DoKeyword);
var statement = this.ParseEmbeddedStatement(false);
var @while = this.EatToken(SyntaxKind.WhileKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfDoWhileExpression;
var expression = this.ParseExpressionCore();
_termState = saveTerm;
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.DoStatement(@do, statement, @while, openParen, expression, closeParen, semicolon);
}
private bool IsEndOfDoWhileExpression()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.SemicolonToken;
}
private StatementSyntax ParseForOrForEachStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword || this.CurrentToken.Kind == SyntaxKind.ForEachKeyword);
// Check if the user wrote the following accidentally:
//
// for (SomeType t in
//
// instead of
//
// foreach (SomeType t in
//
// In that case, parse it as a foreach, but given the appropriate message that a
// 'foreach' keyword was expected.
var resetPoint = this.GetResetPoint();
try
{
if (this.CurrentToken.Kind == SyntaxKind.ForKeyword)
{
this.EatToken();
if (this.EatToken().Kind == SyntaxKind.OpenParenToken &&
this.ScanType() != ScanTypeFlags.NotType &&
this.EatToken().Kind == SyntaxKind.IdentifierToken &&
this.EatToken().Kind == SyntaxKind.InKeyword)
{
// Looks like a foreach statement. Parse it that way instead
this.Reset(ref resetPoint);
return this.ParseForEachStatement();
}
else
{
// Normal for statement.
this.Reset(ref resetPoint);
return this.ParseForStatement();
}
}
else
{
return this.ParseForEachStatement();
}
}
finally
{
this.Release(ref resetPoint);
}
}
private ForStatementSyntax ParseForStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword);
var @for = this.EatToken(SyntaxKind.ForKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfForStatementArgument;
var resetPoint = this.GetResetPoint();
var initializers = _pool.AllocateSeparated<ExpressionSyntax>();
var incrementors = _pool.AllocateSeparated<ExpressionSyntax>();
try
{
// Here can be either a declaration or an expression statement list. Scan
// for a declaration first.
ScanTypeFlags st;
if (this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false))
{
st = ScanTypeFlags.NotType;
}
else
{
st = this.ScanType();
}
VariableDeclarationSyntax decl = null;
if (st != ScanTypeFlags.NotType && this.IsTrueIdentifier())
{
this.Reset(ref resetPoint);
TypeSyntax type;
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
this.ParseDeclaration(false, out type, variables);
decl = _syntaxFactory.VariableDeclaration(type, variables);
_pool.Free(variables);
}
else
{
// Not a type followed by an identifier, so it must be an expression list.
this.Reset(ref resetPoint);
if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
this.ParseForStatementExpressionList(ref openParen, initializers);
}
}
var semi = this.EatToken(SyntaxKind.SemicolonToken);
ExpressionSyntax condition = null;
if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
condition = this.ParseExpressionCore();
}
var semi2 = this.EatToken(SyntaxKind.SemicolonToken);
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
this.ParseForStatementExpressionList(ref semi2, incrementors);
}
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = ParseEmbeddedStatement(true);
return _syntaxFactory.ForStatement(@for, openParen, decl, initializers, semi, condition, semi2, incrementors, closeParen, statement);
}
finally
{
_termState = saveTerm;
this.Release(ref resetPoint);
_pool.Free(incrementors);
_pool.Free(initializers);
}
}
private bool IsEndOfForStatementArgument()
{
return this.CurrentToken.Kind == SyntaxKind.SemicolonToken
|| this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.OpenBraceToken;
}
private void ParseForStatementExpressionList(ref SyntaxToken startToken, SeparatedSyntaxListBuilder<ExpressionSyntax> list)
{
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken && this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
tryAgain:
if (this.IsPossibleExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first argument
list.Add(this.ParseExpressionCore());
// additional arguments
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleExpression())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
list.Add(this.ParseExpressionCore());
continue;
}
else if (this.SkipBadForStatementExpressionListTokens(ref startToken, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadForStatementExpressionListTokens(ref startToken, list, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
}
private PostSkipAction SkipBadForStatementExpressionListTokens(ref SyntaxToken startToken, SeparatedSyntaxListBuilder<ExpressionSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref startToken, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleExpression(),
p => p.CurrentToken.Kind == SyntaxKind.CloseParenToken || p.CurrentToken.Kind == SyntaxKind.SemicolonToken || p.IsTerminator(),
expected);
}
private ForEachStatementSyntax ParseForEachStatement()
{
// Can be a 'for' keyword if the user typed: 'for (SomeType t in'
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForEachKeyword || this.CurrentToken.Kind == SyntaxKind.ForKeyword);
// Syntax for foreach is:
// foreach ( <type> <identifier> in <expr> ) <embedded-statement>
SyntaxToken @foreach;
// If we're at a 'for', then consume it and attach
// it as skipped text to the missing 'foreach' token.
if (this.CurrentToken.Kind == SyntaxKind.ForKeyword)
{
var skippedForToken = this.EatToken();
skippedForToken = this.AddError(skippedForToken, ErrorCode.ERR_SyntaxError, SyntaxFacts.GetText(SyntaxKind.ForEachKeyword), SyntaxFacts.GetText(SyntaxKind.ForKeyword));
@foreach = ConvertToMissingWithTrailingTrivia(skippedForToken, SyntaxKind.ForEachKeyword);
}
else
{
@foreach = this.EatToken(SyntaxKind.ForEachKeyword);
}
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var type = this.ParseType(false);
SyntaxToken name;
if (this.CurrentToken.Kind == SyntaxKind.InKeyword)
{
name = this.ParseIdentifierToken();
name = this.AddError(name, ErrorCode.ERR_BadForeachDecl);
}
else
{
name = this.ParseIdentifierToken();
}
var @in = this.EatToken(SyntaxKind.InKeyword, ErrorCode.ERR_InExpected);
var expression = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(true);
return _syntaxFactory.ForEachStatement(@foreach, openParen, type, name, @in, expression, closeParen, statement);
}
private GotoStatementSyntax ParseGotoStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.GotoKeyword);
var @goto = this.EatToken(SyntaxKind.GotoKeyword);
SyntaxToken caseOrDefault = null;
ExpressionSyntax arg = null;
SyntaxKind kind;
if (this.CurrentToken.Kind == SyntaxKind.CaseKeyword || this.CurrentToken.Kind == SyntaxKind.DefaultKeyword)
{
caseOrDefault = this.EatToken();
if (caseOrDefault.Kind == SyntaxKind.CaseKeyword)
{
kind = SyntaxKind.GotoCaseStatement;
arg = this.ParseExpressionCore();
}
else
{
kind = SyntaxKind.GotoDefaultStatement;
}
}
else
{
kind = SyntaxKind.GotoStatement;
arg = this.ParseIdentifierName();
}
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.GotoStatement(kind, @goto, caseOrDefault, arg, semicolon);
}
private IfStatementSyntax ParseIfStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IfKeyword);
var @if = this.EatToken(SyntaxKind.IfKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var condition = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(false);
ElseClauseSyntax @else = null;
if (this.CurrentToken.Kind == SyntaxKind.ElseKeyword)
{
var elseToken = this.EatToken(SyntaxKind.ElseKeyword);
var elseStatement = this.ParseEmbeddedStatement(false);
@else = _syntaxFactory.ElseClause(elseToken, elseStatement);
}
return _syntaxFactory.IfStatement(@if, openParen, condition, closeParen, statement, @else);
}
private LockStatementSyntax ParseLockStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.LockKeyword);
var @lock = this.EatToken(SyntaxKind.LockKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expression = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(false);
return _syntaxFactory.LockStatement(@lock, openParen, expression, closeParen, statement);
}
private ReturnStatementSyntax ParseReturnStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ReturnKeyword);
var @return = this.EatToken(SyntaxKind.ReturnKeyword);
ExpressionSyntax arg = null;
if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
arg = this.ParseExpressionCore();
}
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.ReturnStatement(@return, arg, semicolon);
}
private YieldStatementSyntax ParseYieldStatement()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.YieldKeyword);
var yieldToken = ConvertToKeyword(this.EatToken());
SyntaxToken returnOrBreak = null;
ExpressionSyntax arg = null;
SyntaxKind kind;
yieldToken = CheckFeatureAvailability(yieldToken, MessageID.IDS_FeatureIterators);
if (this.CurrentToken.Kind == SyntaxKind.BreakKeyword)
{
kind = SyntaxKind.YieldBreakStatement;
returnOrBreak = this.EatToken();
}
else
{
kind = SyntaxKind.YieldReturnStatement;
returnOrBreak = this.EatToken(SyntaxKind.ReturnKeyword);
if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
returnOrBreak = this.AddError(returnOrBreak, ErrorCode.ERR_EmptyYield);
}
else
{
arg = this.ParseExpressionCore();
}
}
var semi = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.YieldStatement(kind, yieldToken, returnOrBreak, arg, semi);
}
private SwitchStatementSyntax ParseSwitchStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.SwitchKeyword);
var @switch = this.EatToken(SyntaxKind.SwitchKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expression = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
openBrace = this.AddError(openBrace, ErrorCode.WRN_EmptySwitch);
}
var sections = _pool.Allocate<SwitchSectionSyntax>();
try
{
while (this.IsPossibleSwitchSection())
{
var swcase = this.ParseSwitchSection();
sections.Add(swcase);
}
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
return _syntaxFactory.SwitchStatement(@switch, openParen, expression, closeParen, openBrace, sections, closeBrace);
}
finally
{
_pool.Free(sections);
}
}
private bool IsPossibleSwitchSection()
{
return (this.CurrentToken.Kind == SyntaxKind.CaseKeyword) ||
(this.CurrentToken.Kind == SyntaxKind.DefaultKeyword && this.PeekToken(1).Kind != SyntaxKind.OpenParenToken);
}
private SwitchSectionSyntax ParseSwitchSection()
{
Debug.Assert(this.IsPossibleSwitchSection());
// First, parse case label(s)
var labels = _pool.Allocate<SwitchLabelSyntax>();
var statements = _pool.Allocate<StatementSyntax>();
try
{
do
{
SyntaxToken specifier;
SwitchLabelSyntax label;
SyntaxToken colon;
if (this.CurrentToken.Kind == SyntaxKind.CaseKeyword)
{
ExpressionSyntax expression;
specifier = this.EatToken();
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
expression = this.CreateMissingIdentifierName();
expression = this.AddError(expression, ErrorCode.ERR_ConstantExpected);
}
else
{
expression = this.ParseExpressionCore();
}
colon = this.EatToken(SyntaxKind.ColonToken);
label = _syntaxFactory.CaseSwitchLabel(specifier, expression, colon);
}
else
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DefaultKeyword);
specifier = this.EatToken(SyntaxKind.DefaultKeyword);
colon = this.EatToken(SyntaxKind.ColonToken);
label = _syntaxFactory.DefaultSwitchLabel(specifier, colon);
}
labels.Add(label);
}
while (IsPossibleSwitchSection());
// Next, parse statement list stopping for new sections
CSharpSyntaxNode tmp = labels[labels.Count - 1];
this.ParseStatements(ref tmp, statements, true);
labels[labels.Count - 1] = (SwitchLabelSyntax)tmp;
return _syntaxFactory.SwitchSection(labels, statements);
}
finally
{
_pool.Free(statements);
_pool.Free(labels);
}
}
private ThrowStatementSyntax ParseThrowStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ThrowKeyword);
var @throw = this.EatToken(SyntaxKind.ThrowKeyword);
ExpressionSyntax arg = null;
if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
arg = this.ParseExpressionCore();
}
var semi = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.ThrowStatement(@throw, arg, semi);
}
private UnsafeStatementSyntax ParseUnsafeStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.UnsafeKeyword);
var @unsafe = this.EatToken(SyntaxKind.UnsafeKeyword);
var block = this.ParseBlock();
return _syntaxFactory.UnsafeStatement(@unsafe, block);
}
private UsingStatementSyntax ParseUsingStatement()
{
var @using = this.EatToken(SyntaxKind.UsingKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
VariableDeclarationSyntax declaration = null;
ExpressionSyntax expression = null;
var resetPoint = this.GetResetPoint();
ParseUsingExpression(ref declaration, ref expression, ref resetPoint);
this.Release(ref resetPoint);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(false);
return _syntaxFactory.UsingStatement(@using, openParen, declaration, expression, closeParen, statement);
}
private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref ExpressionSyntax expression, ref ResetPoint resetPoint)
{
if (this.IsAwaitExpression())
{
expression = this.ParseExpressionCore();
return;
}
TypeSyntax type;
// Now, this can be either an expression or a decl list
ScanTypeFlags st;
if (this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false))
{
st = ScanTypeFlags.NotType;
}
else
{
st = this.ScanType();
}
if (st == ScanTypeFlags.NullableType)
{
// We need to handle:
// * using (f ? x = a : x = b)
// * using (f ? x = a)
// * using (f ? x, y)
if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken)
{
this.Reset(ref resetPoint);
expression = this.ParseExpressionCore();
}
else
{
SeparatedSyntaxListBuilder<VariableDeclaratorSyntax> variables;
switch (this.PeekToken(1).Kind)
{
default:
this.Reset(ref resetPoint);
expression = this.ParseExpressionCore();
break;
case SyntaxKind.CommaToken:
case SyntaxKind.CloseParenToken:
this.Reset(ref resetPoint);
variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
this.ParseDeclaration(false, out type, variables);
declaration = _syntaxFactory.VariableDeclaration(type, variables.ToList());
_pool.Free(variables);
break;
case SyntaxKind.EqualsToken:
// Parse it as a decl. If the next token is a : and only one variable was parsed,
// convert the whole thing to ?: expression.
this.Reset(ref resetPoint);
variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
this.ParseDeclaration(false, out type, variables);
// We may have non-nullable types in error scenarios.
if (this.CurrentToken.Kind == SyntaxKind.ColonToken &&
type.Kind == SyntaxKind.NullableType &&
SyntaxFacts.IsName(((NullableTypeSyntax)type).ElementType.Kind) &&
variables.Count == 1)
{
// We have "name? id = expr :" so need to convert to a ?: expression.
this.Reset(ref resetPoint);
expression = this.ParseExpressionCore();
}
else
{
declaration = _syntaxFactory.VariableDeclaration(type, variables.ToList());
}
_pool.Free(variables);
break;
}
}
}
else if (IsUsingStatementVariableDeclaration(st))
{
this.Reset(ref resetPoint);
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
this.ParseDeclaration(false, out type, variables);
declaration = _syntaxFactory.VariableDeclaration(type, variables);
_pool.Free(variables);
}
else
{
// Must be an expression statement
this.Reset(ref resetPoint);
expression = this.ParseExpressionCore();
}
}
private bool IsUsingStatementVariableDeclaration(ScanTypeFlags st)
{
Debug.Assert(st != ScanTypeFlags.NullableType);
bool condition1 = st == ScanTypeFlags.MustBeType && this.CurrentToken.Kind != SyntaxKind.DotToken;
bool condition2 = st != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.IdentifierToken;
bool condition3 = st == ScanTypeFlags.NonGenericTypeOrExpression || this.PeekToken(1).Kind == SyntaxKind.EqualsToken;
return condition1 || (condition2 && condition3);
}
private WhileStatementSyntax ParseWhileStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.WhileKeyword);
var @while = this.EatToken(SyntaxKind.WhileKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var condition = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(true);
return _syntaxFactory.WhileStatement(@while, openParen, condition, closeParen, statement);
}
private LabeledStatementSyntax ParseLabeledStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IdentifierToken);
// We have an identifier followed by a colon. But if the identifier is a contextual keyword in a query context,
// ParseIdentifier will result in a missing name and Eat(Colon) will fail. We won't make forward progress.
Debug.Assert(this.IsTrueIdentifier());
var label = this.ParseIdentifierToken();
var colon = this.EatToken(SyntaxKind.ColonToken);
Debug.Assert(!colon.IsMissing);
var statement = this.ParseStatementCore();
return _syntaxFactory.LabeledStatement(label, colon, statement);
}
private LocalDeclarationStatementSyntax ParseLocalDeclarationStatement()
{
TypeSyntax type;
var mods = _pool.Allocate();
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>();
try
{
this.ParseDeclarationModifiers(mods);
this.ParseDeclaration(mods.Any(SyntaxKind.ConstKeyword), out type, variables);
var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
return _syntaxFactory.LocalDeclarationStatement(
mods.ToTokenList(),
_syntaxFactory.VariableDeclaration(type, variables),
semicolon);
}
finally
{
_pool.Free(variables);
_pool.Free(mods);
}
}
private void ParseDeclaration(bool isConst, out TypeSyntax type, SeparatedSyntaxListBuilder<VariableDeclaratorSyntax> variables)
{
type = this.ParseType(false);
VariableFlags flags = VariableFlags.Local;
if (isConst)
{
flags |= VariableFlags.Const;
}
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfDeclarationClause;
this.ParseVariableDeclarators(type, flags, variables, variableDeclarationsExpected: true);
_termState = saveTerm;
}
private bool IsEndOfDeclarationClause()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.SemicolonToken:
case SyntaxKind.CloseParenToken:
case SyntaxKind.ColonToken:
return true;
default:
return false;
}
}
private void ParseDeclarationModifiers(SyntaxListBuilder list)
{
SyntaxKind k;
while (IsDeclarationModifier(k = this.CurrentToken.Kind))
{
var mod = this.EatToken();
if (k == SyntaxKind.StaticKeyword || k == SyntaxKind.ReadOnlyKeyword || k == SyntaxKind.VolatileKeyword)
{
mod = this.AddError(mod, ErrorCode.ERR_BadMemberFlag, mod.Text);
}
else if (list.Any(mod.Kind))
{
// check for duplicates, can only be const
mod = this.AddError(mod, ErrorCode.ERR_TypeExpected, mod.Text);
}
list.Add(mod);
}
}
private static bool IsDeclarationModifier(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.ConstKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.VolatileKeyword:
return true;
default:
return false;
}
}
private ExpressionStatementSyntax ParseExpressionStatement()
{
return ParseExpressionStatement(this.ParseExpressionCore());
}
private ExpressionStatementSyntax ParseExpressionStatement(ExpressionSyntax expression)
{
SyntaxToken semicolon;
if (IsScript && this.CurrentToken.Kind == SyntaxKind.EndOfFileToken)
{
semicolon = SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken);
}
else
{
// Do not report an error if the expression is not a statement expression.
// The error is reported in semantic analysis.
semicolon = this.EatToken(SyntaxKind.SemicolonToken);
}
return _syntaxFactory.ExpressionStatement(expression, semicolon);
}
public ExpressionSyntax ParseExpression()
{
return ParseWithStackGuard(
this.ParseExpressionCore,
this.CreateMissingIdentifierName);
}
private ExpressionSyntax ParseExpressionCore()
{
return this.ParseSubExpression(0);
}
private bool IsPossibleExpression()
{
var tk = this.CurrentToken.Kind;
switch (tk)
{
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.DefaultKeyword:
case SyntaxKind.SizeOfKeyword:
case SyntaxKind.MakeRefKeyword:
case SyntaxKind.RefTypeKeyword:
case SyntaxKind.CheckedKeyword:
case SyntaxKind.UncheckedKeyword:
case SyntaxKind.RefValueKeyword:
case SyntaxKind.ArgListKeyword:
case SyntaxKind.BaseKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.OpenParenToken:
case SyntaxKind.NumericLiteralToken:
case SyntaxKind.StringLiteralToken:
case SyntaxKind.InterpolatedStringStartToken:
case SyntaxKind.InterpolatedStringToken:
case SyntaxKind.CharacterLiteralToken:
case SyntaxKind.NewKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.ColonColonToken: // bad aliased name
return true;
case SyntaxKind.IdentifierToken:
// Specifically allow the from contextual keyword, because it can always be the start of an
// expression (whether it is used as an identifier or a keyword).
return this.IsTrueIdentifier() || (this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword);
default:
return IsExpectedPrefixUnaryOperator(tk)
|| (IsPredefinedType(tk) && tk != SyntaxKind.VoidKeyword)
|| SyntaxFacts.IsAnyUnaryExpression(tk)
|| SyntaxFacts.IsBinaryExpression(tk)
|| SyntaxFacts.IsAssignmentExpressionOperatorToken(tk);
}
}
private static bool IsInvalidSubExpression(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.BreakKeyword:
case SyntaxKind.CaseKeyword:
case SyntaxKind.CatchKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.ContinueKeyword:
case SyntaxKind.DoKeyword:
case SyntaxKind.FinallyKeyword:
case SyntaxKind.ForKeyword:
case SyntaxKind.ForEachKeyword:
case SyntaxKind.GotoKeyword:
case SyntaxKind.IfKeyword:
case SyntaxKind.LockKeyword:
case SyntaxKind.ReturnKeyword:
case SyntaxKind.SwitchKeyword:
case SyntaxKind.ThrowKeyword:
case SyntaxKind.TryKeyword:
case SyntaxKind.UsingKeyword:
case SyntaxKind.WhileKeyword:
return true;
default:
return false;
}
}
internal static bool IsRightAssociative(SyntaxKind op)
{
switch (op)
{
case SyntaxKind.SimpleAssignmentExpression:
case SyntaxKind.AddAssignmentExpression:
case SyntaxKind.SubtractAssignmentExpression:
case SyntaxKind.MultiplyAssignmentExpression:
case SyntaxKind.DivideAssignmentExpression:
case SyntaxKind.ModuloAssignmentExpression:
case SyntaxKind.AndAssignmentExpression:
case SyntaxKind.ExclusiveOrAssignmentExpression:
case SyntaxKind.OrAssignmentExpression:
case SyntaxKind.LeftShiftAssignmentExpression:
case SyntaxKind.RightShiftAssignmentExpression:
case SyntaxKind.CoalesceExpression:
return true;
default:
return false;
}
}
private static uint GetPrecedence(SyntaxKind op)
{
switch (op)
{
case SyntaxKind.SimpleAssignmentExpression:
case SyntaxKind.AddAssignmentExpression:
case SyntaxKind.SubtractAssignmentExpression:
case SyntaxKind.MultiplyAssignmentExpression:
case SyntaxKind.DivideAssignmentExpression:
case SyntaxKind.ModuloAssignmentExpression:
case SyntaxKind.AndAssignmentExpression:
case SyntaxKind.ExclusiveOrAssignmentExpression:
case SyntaxKind.OrAssignmentExpression:
case SyntaxKind.LeftShiftAssignmentExpression:
case SyntaxKind.RightShiftAssignmentExpression:
return 1;
case SyntaxKind.CoalesceExpression:
return 2;
case SyntaxKind.LogicalOrExpression:
return 3;
case SyntaxKind.LogicalAndExpression:
return 4;
case SyntaxKind.BitwiseOrExpression:
return 5;
case SyntaxKind.ExclusiveOrExpression:
return 6;
case SyntaxKind.BitwiseAndExpression:
return 7;
case SyntaxKind.EqualsExpression:
case SyntaxKind.NotEqualsExpression:
return 8;
case SyntaxKind.LessThanExpression:
case SyntaxKind.LessThanOrEqualExpression:
case SyntaxKind.GreaterThanExpression:
case SyntaxKind.GreaterThanOrEqualExpression:
case SyntaxKind.IsExpression:
case SyntaxKind.AsExpression:
return 9;
case SyntaxKind.LeftShiftExpression:
case SyntaxKind.RightShiftExpression:
return 10;
case SyntaxKind.AddExpression:
case SyntaxKind.SubtractExpression:
return 11;
case SyntaxKind.MultiplyExpression:
case SyntaxKind.DivideExpression:
case SyntaxKind.ModuloExpression:
return 12;
case SyntaxKind.UnaryPlusExpression:
case SyntaxKind.UnaryMinusExpression:
case SyntaxKind.BitwiseNotExpression:
case SyntaxKind.LogicalNotExpression:
case SyntaxKind.PreIncrementExpression:
case SyntaxKind.PreDecrementExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.SizeOfExpression:
case SyntaxKind.CheckedExpression:
case SyntaxKind.UncheckedExpression:
case SyntaxKind.MakeRefExpression:
case SyntaxKind.RefValueExpression:
case SyntaxKind.RefTypeExpression:
case SyntaxKind.AwaitExpression:
return 13;
case SyntaxKind.CastExpression:
return 14;
case SyntaxKind.PointerIndirectionExpression:
return 15;
case SyntaxKind.AddressOfExpression:
return 16;
default:
return 0;
}
}
private static bool IsExpectedPrefixUnaryOperator(SyntaxKind kind)
{
return SyntaxFacts.IsPrefixUnaryExpression(kind) && kind != SyntaxKind.RefKeyword && kind != SyntaxKind.OutKeyword;
}
private static bool IsExpectedBinaryOperator(SyntaxKind kind)
{
return SyntaxFacts.IsBinaryExpression(kind);
}
private static bool IsExpectedAssignmentOperator(SyntaxKind kind)
{
return SyntaxFacts.IsAssignmentExpressionOperatorToken(kind);
}
private bool IsPossibleAwaitExpressionStatement()
{
return (this.IsScript || this.IsInAsync) && this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword;
}
private bool IsAwaitExpression()
{
if (this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword)
{
if (this.IsInAsync)
{
// If we see an await in an async function, parse it as an unop.
return true;
}
// If we see an await followed by a token that cannot follow an identifier, parse await as a unop.
// BindAwait() catches the cases where await successfully parses as a unop but is not in an async
// function, and reports an appropriate ERR_BadAwaitWithoutAsync* error.
switch (this.PeekToken(1).Kind)
{
case SyntaxKind.IdentifierToken:
// Keywords
case SyntaxKind.NewKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.BaseKeyword:
case SyntaxKind.DelegateKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.CheckedKeyword:
case SyntaxKind.UncheckedKeyword:
case SyntaxKind.DefaultKeyword:
// Literals
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.StringLiteralToken:
case SyntaxKind.InterpolatedStringStartToken:
case SyntaxKind.InterpolatedStringToken:
case SyntaxKind.NumericLiteralToken:
case SyntaxKind.NullKeyword:
case SyntaxKind.CharacterLiteralToken:
return true;
}
}
return false;
}
private ExpressionSyntax ParseSubExpression(uint precedence)
{
_recursionDepth++;
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
var result = ParseSubExpressionCore(precedence);
_recursionDepth--;
return result;
}
private ExpressionSyntax ParseSubExpressionCore(uint precedence)
{
ExpressionSyntax leftOperand = null;
uint newPrecedence = 0;
SyntaxKind opKind = SyntaxKind.None;
// all of these are tokens that start statements and are invalid
// to start a expression with. if we see one, then we must have
// something like:
//
// return
// if (...
// parse out a missing name node for the expression, and keep on going
var tk = this.CurrentToken.Kind;
if (IsInvalidSubExpression(tk))
{
return this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk));
}
// No left operand, so we need to parse one -- possibly preceded by a
// unary operator.
if (IsExpectedPrefixUnaryOperator(tk))
{
opKind = SyntaxFacts.GetPrefixUnaryExpression(tk);
newPrecedence = GetPrecedence(opKind);
var opToken = this.EatToken();
var operand = this.ParseSubExpression(newPrecedence);
leftOperand = _syntaxFactory.PrefixUnaryExpression(opKind, opToken, operand);
}
else if (IsAwaitExpression())
{
opKind = SyntaxKind.AwaitExpression;
newPrecedence = GetPrecedence(opKind);
var awaitToken = this.EatContextualToken(SyntaxKind.AwaitKeyword);
awaitToken = CheckFeatureAvailability(awaitToken, MessageID.IDS_FeatureAsync);
var operand = this.ParseSubExpression(newPrecedence);
leftOperand = _syntaxFactory.AwaitExpression(awaitToken, operand);
}
else if (this.IsQueryExpression(mayBeVariableDeclaration: false, mayBeMemberDeclaration: false))
{
leftOperand = this.ParseQueryExpression(precedence);
}
else if (this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword && IsInQuery)
{
// If this "from" token wasn't the start of a query then it's not really an expression.
// Consume it so that we don't try to parse it again as the next argument in an
// argument list.
SyntaxToken skipped = this.EatToken(); // consume but skip "from"
skipped = this.AddError(skipped, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text);
leftOperand = AddTrailingSkippedSyntax(this.CreateMissingIdentifierName(), skipped);
}
else
{
// Not a unary operator - get a primary expression.
leftOperand = this.ParseTerm(precedence);
}
while (true)
{
// We either have a binary or assignment operator here, or we're finished.
tk = this.CurrentToken.Kind;
bool isAssignmentOperator = false;
if (IsExpectedBinaryOperator(tk))
{
opKind = SyntaxFacts.GetBinaryExpression(tk);
}
else if (IsExpectedAssignmentOperator(tk))
{
opKind = SyntaxFacts.GetAssignmentExpression(tk);
isAssignmentOperator = true;
}
else
{
break;
}
newPrecedence = GetPrecedence(opKind);
Debug.Assert(newPrecedence > 0); // All binary operators must have precedence > 0!
// check for >> or >>=
bool doubleOp = false;
if (tk == SyntaxKind.GreaterThanToken
&& (this.PeekToken(1).Kind == SyntaxKind.GreaterThanToken || this.PeekToken(1).Kind == SyntaxKind.GreaterThanEqualsToken))
{
// check to see if they really are adjacent
if (this.CurrentToken.GetTrailingTriviaWidth() == 0 && this.PeekToken(1).GetLeadingTriviaWidth() == 0)
{
if (this.PeekToken(1).Kind == SyntaxKind.GreaterThanToken)
{
opKind = SyntaxFacts.GetBinaryExpression(SyntaxKind.GreaterThanGreaterThanToken);
}
else
{
opKind = SyntaxFacts.GetAssignmentExpression(SyntaxKind.GreaterThanGreaterThanEqualsToken);
isAssignmentOperator = true;
}
newPrecedence = GetPrecedence(opKind);
doubleOp = true;
}
}
// Check the precedence to see if we should "take" this operator
if (newPrecedence < precedence)
{
break;
}
// Same precedence, but not right-associative -- deal with this "later"
if ((newPrecedence == precedence) && !IsRightAssociative(opKind))
{
break;
}
// Precedence is okay, so we'll "take" this operator.
var opToken = this.EatToken();
if (doubleOp)
{
// combine tokens into a single token
var opToken2 = this.EatToken();
var kind = opToken2.Kind == SyntaxKind.GreaterThanToken ? SyntaxKind.GreaterThanGreaterThanToken : SyntaxKind.GreaterThanGreaterThanEqualsToken;
opToken = SyntaxFactory.Token(opToken.GetLeadingTrivia(), kind, opToken2.GetTrailingTrivia());
}
if (opKind == SyntaxKind.IsExpression || opKind == SyntaxKind.AsExpression)
{
leftOperand = _syntaxFactory.BinaryExpression(opKind, leftOperand, opToken,
this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false));
}
else
{
var rightOperand = this.ParseSubExpression(newPrecedence);
if (isAssignmentOperator)
{
leftOperand = _syntaxFactory.AssignmentExpression(opKind, leftOperand, opToken, rightOperand);
}
else
{
leftOperand = _syntaxFactory.BinaryExpression(opKind, leftOperand, opToken, rightOperand);
}
}
}
// From the language spec:
//
// conditional-expression:
// null-coalescing-expression
// null-coalescing-expression ? expression : expression
//
// Only take the ternary if we're at a precedence less than the null coalescing
// expression.
var nullCoalescingPrecedence = GetPrecedence(SyntaxKind.CoalesceExpression);
if (tk == SyntaxKind.QuestionToken && precedence < nullCoalescingPrecedence)
{
var questionToken = this.EatToken();
var colonLeft = this.ParseExpressionCore();
var colon = this.EatToken(SyntaxKind.ColonToken);
var colonRight = this.ParseExpressionCore();
leftOperand = _syntaxFactory.ConditionalExpression(leftOperand, questionToken, colonLeft, colon, colonRight);
}
return leftOperand;
}
private ExpressionSyntax ParseTerm(uint precedence)
{
ExpressionSyntax expr = null;
var tk = this.CurrentToken.Kind;
switch (tk)
{
case SyntaxKind.TypeOfKeyword:
expr = this.ParseTypeOfExpression();
break;
case SyntaxKind.DefaultKeyword:
expr = this.ParseDefaultExpression();
break;
case SyntaxKind.SizeOfKeyword:
expr = this.ParseSizeOfExpression();
break;
case SyntaxKind.MakeRefKeyword:
expr = this.ParseMakeRefExpression();
break;
case SyntaxKind.RefTypeKeyword:
expr = this.ParseRefTypeExpression();
break;
case SyntaxKind.CheckedKeyword:
case SyntaxKind.UncheckedKeyword:
expr = this.ParseCheckedOrUncheckedExpression();
break;
case SyntaxKind.RefValueKeyword:
expr = this.ParseRefValueExpression();
break;
case SyntaxKind.ColonColonToken:
// misplaced ::
// TODO: this should not be a compound name.. (disallow dots)
expr = this.ParseQualifiedName(NameOptions.InExpression);
break;
case SyntaxKind.IdentifierToken:
if (this.IsTrueIdentifier())
{
if (this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword && this.PeekToken(1).Kind == SyntaxKind.DelegateKeyword)
{
expr = this.ParseAnonymousMethodExpression();
}
else if (this.IsPossibleLambdaExpression(precedence))
{
expr = this.ParseLambdaExpression();
}
else
{
expr = this.ParseAliasQualifiedName(NameOptions.InExpression);
}
}
else
{
expr = this.CreateMissingIdentifierName();
expr = this.AddError(expr, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text);
}
break;
case SyntaxKind.ThisKeyword:
expr = _syntaxFactory.ThisExpression(this.EatToken());
break;
case SyntaxKind.BaseKeyword:
expr = _syntaxFactory.BaseExpression(this.EatToken());
break;
case SyntaxKind.ArgListKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.NumericLiteralToken:
case SyntaxKind.StringLiteralToken:
case SyntaxKind.CharacterLiteralToken:
expr = _syntaxFactory.LiteralExpression(SyntaxFacts.GetLiteralExpression(tk), this.EatToken());
break;
case SyntaxKind.InterpolatedStringStartToken:
throw new NotImplementedException(); // this should not occur because these tokens are produced and parsed immediately
case SyntaxKind.InterpolatedStringToken:
expr = this.ParseInterpolatedStringToken();
break;
case SyntaxKind.OpenParenToken:
expr = this.ParseCastOrParenExpressionOrLambda(precedence);
break;
case SyntaxKind.NewKeyword:
expr = this.ParseNewExpression();
break;
case SyntaxKind.DelegateKeyword:
expr = this.ParseAnonymousMethodExpression();
break;
default:
// check for intrinsic type followed by '.'
if (IsPredefinedType(tk))
{
expr = _syntaxFactory.PredefinedType(this.EatToken());
if (this.CurrentToken.Kind != SyntaxKind.DotToken || tk == SyntaxKind.VoidKeyword)
{
expr = this.AddError(expr, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk));
}
}
else
{
expr = this.CreateMissingIdentifierName();
if (tk == SyntaxKind.EndOfFileToken)
{
expr = this.AddError(expr, ErrorCode.ERR_ExpressionExpected);
}
else
{
expr = this.AddError(expr, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk));
}
}
break;
}
return this.ParsePostFixExpression(expr);
}
private bool IsPossibleLambdaExpression(uint precedence)
{
if (precedence <= LambdaPrecedence && this.PeekToken(1).Kind == SyntaxKind.EqualsGreaterThanToken)
{
return true;
}
if (ScanAsyncLambda(precedence))
{
return true;
}
return false;
}
private ExpressionSyntax ParsePostFixExpression(ExpressionSyntax expr)
{
Debug.Assert(expr != null);
while (true)
{
SyntaxKind tk = this.CurrentToken.Kind;
switch (tk)
{
case SyntaxKind.OpenParenToken:
expr = _syntaxFactory.InvocationExpression(expr, this.ParseParenthesizedArgumentList());
break;
case SyntaxKind.OpenBracketToken:
expr = _syntaxFactory.ElementAccessExpression(expr, this.ParseBracketedArgumentList());
break;
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
expr = _syntaxFactory.PostfixUnaryExpression(SyntaxFacts.GetPostfixUnaryExpression(tk), expr, this.EatToken());
break;
case SyntaxKind.ColonColonToken:
if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken)
{
// replace :: with missing dot and annotate with skipped text "::" and error
var ccToken = this.EatToken();
ccToken = this.AddError(ccToken, ErrorCode.ERR_UnexpectedAliasedName);
var dotToken = this.ConvertToMissingWithTrailingTrivia(ccToken, SyntaxKind.DotToken);
expr = _syntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, dotToken, this.ParseSimpleName(NameOptions.InExpression));
}
else
{
// just some random trailing :: ?
expr = AddTrailingSkippedSyntax(expr, this.EatTokenWithPrejudice(SyntaxKind.DotToken));
}
break;
case SyntaxKind.MinusGreaterThanToken:
expr = _syntaxFactory.MemberAccessExpression(SyntaxKind.PointerMemberAccessExpression, expr, this.EatToken(), this.ParseSimpleName(NameOptions.InExpression));
break;
case SyntaxKind.DotToken:
expr = _syntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, this.EatToken(), this.ParseSimpleName(NameOptions.InExpression));
break;
case SyntaxKind.QuestionToken:
if (CanStartConsequenceExpression(this.PeekToken(1).Kind))
{
var qToken = this.EatToken();
var consequence = ParseConsequenceSyntax();
expr = _syntaxFactory.ConditionalAccessExpression(expr, qToken, consequence);
expr = CheckFeatureAvailability(expr, MessageID.IDS_FeatureNullPropagatingOperator);
break;
}
goto default;
default:
return expr;
}
}
}
private bool CanStartConsequenceExpression(SyntaxKind kind)
{
return kind == SyntaxKind.DotToken ||
kind == SyntaxKind.OpenBracketToken;
}
internal ExpressionSyntax ParseConsequenceSyntax()
{
SyntaxKind tk = this.CurrentToken.Kind;
ExpressionSyntax expr = null;
switch (tk)
{
case SyntaxKind.DotToken:
expr = _syntaxFactory.MemberBindingExpression(this.EatToken(), this.ParseSimpleName(NameOptions.InExpression));
break;
case SyntaxKind.OpenBracketToken:
expr = _syntaxFactory.ElementBindingExpression(this.ParseBracketedArgumentList());
break;
}
Debug.Assert(expr != null);
while (true)
{
tk = this.CurrentToken.Kind;
switch (tk)
{
case SyntaxKind.OpenParenToken:
expr = _syntaxFactory.InvocationExpression(expr, this.ParseParenthesizedArgumentList());
break;
case SyntaxKind.OpenBracketToken:
expr = _syntaxFactory.ElementAccessExpression(expr, this.ParseBracketedArgumentList());
break;
case SyntaxKind.DotToken:
expr = _syntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, this.EatToken(), this.ParseSimpleName(NameOptions.InExpression));
break;
case SyntaxKind.QuestionToken:
if (CanStartConsequenceExpression(this.PeekToken(1).Kind))
{
var qToken = this.EatToken();
var consequence = ParseConsequenceSyntax();
expr = _syntaxFactory.ConditionalAccessExpression(expr, qToken, consequence);
}
return expr;
default:
return expr;
}
}
}
internal ArgumentListSyntax ParseParenthesizedArgumentList()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.ArgumentList)
{
return (ArgumentListSyntax)this.EatNode();
}
SyntaxToken openToken, closeToken;
SeparatedSyntaxList<ArgumentSyntax> arguments;
ParseArgumentList(out openToken, out arguments, out closeToken, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken);
return _syntaxFactory.ArgumentList(openToken, arguments, closeToken);
}
internal BracketedArgumentListSyntax ParseBracketedArgumentList()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.BracketedArgumentList)
{
return (BracketedArgumentListSyntax)this.EatNode();
}
SyntaxToken openToken, closeToken;
SeparatedSyntaxList<ArgumentSyntax> arguments;
ParseArgumentList(out openToken, out arguments, out closeToken, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
return _syntaxFactory.BracketedArgumentList(openToken, arguments, closeToken);
}
private void ParseArgumentList(
out SyntaxToken openToken,
out SeparatedSyntaxList<ArgumentSyntax> arguments,
out SyntaxToken closeToken,
SyntaxKind openKind,
SyntaxKind closeKind)
{
bool isIndexer = openKind == SyntaxKind.OpenBracketToken;
var open = this.EatToken(openKind);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfArgumentList;
SeparatedSyntaxListBuilder<ArgumentSyntax> list = default(SeparatedSyntaxListBuilder<ArgumentSyntax>);
try
{
if (this.CurrentToken.Kind != closeKind && this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
{
tryAgain:
if (list.IsNull)
{
list = _pool.AllocateSeparated<ArgumentSyntax>();
}
if (this.IsPossibleArgumentExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first argument
list.Add(this.ParseArgumentExpression(isIndexer));
// additional arguments
while (true)
{
if (this.CurrentToken.Kind == closeKind || this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleArgumentExpression())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
list.Add(this.ParseArgumentExpression(isIndexer));
continue;
}
else if (this.SkipBadArgumentListTokens(ref open, list, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadArgumentListTokens(ref open, list, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
else if (isIndexer && this.CurrentToken.Kind == closeKind)
{
// An indexer always expects at least one value. And so we need to give an error
// for the case where we see only "[]". ParseArgumentExpression gives it.
if (list.IsNull)
{
list = _pool.AllocateSeparated<ArgumentSyntax>();
}
list.Add(this.ParseArgumentExpression(isIndexer));
}
_termState = saveTerm;
openToken = open;
closeToken = this.EatToken(closeKind);
arguments = list.ToList();
}
finally
{
if (!list.IsNull)
{
_pool.Free(list);
}
}
}
private PostSkipAction SkipBadArgumentListTokens(ref SyntaxToken open, SeparatedSyntaxListBuilder<ArgumentSyntax> list, SyntaxKind expected, SyntaxKind closeKind)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref open, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleArgumentExpression(),
p => p.CurrentToken.Kind == closeKind || p.CurrentToken.Kind == SyntaxKind.SemicolonToken || p.IsTerminator(),
expected);
}
private bool IsEndOfArgumentList()
{
return this.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| this.CurrentToken.Kind == SyntaxKind.CloseBracketToken;
}
private bool IsPossibleArgumentExpression()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
return true;
default:
return this.IsPossibleExpression();
}
}
private ArgumentSyntax ParseArgumentExpression(bool isIndexer)
{
NameColonSyntax nameColon = null;
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.ColonToken)
{
var name = this.ParseIdentifierName();
var colon = this.EatToken(SyntaxKind.ColonToken);
nameColon = _syntaxFactory.NameColon(name, colon);
nameColon = CheckFeatureAvailability(nameColon, MessageID.IDS_FeatureNamedArgument);
}
SyntaxToken refOrOutKeyword = null;
if (this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword)
{
refOrOutKeyword = this.EatToken();
}
ExpressionSyntax expression;
if (isIndexer && (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.CloseBracketToken))
{
expression = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_ValueExpected);
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
expression = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_MissingArgument);
}
else
{
expression = this.ParseSubExpression(0);
}
return _syntaxFactory.Argument(nameColon, refOrOutKeyword, expression);
}
private TypeOfExpressionSyntax ParseTypeOfExpression()
{
var keyword = this.EatToken();
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var type = this.ParseTypeOrVoid();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.TypeOfExpression(keyword, openParen, type, closeParen);
}
private DefaultExpressionSyntax ParseDefaultExpression()
{
var keyword = this.EatToken();
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var type = this.ParseType(false);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
keyword = CheckFeatureAvailability(keyword, MessageID.IDS_FeatureDefault);
return _syntaxFactory.DefaultExpression(keyword, openParen, type, closeParen);
}
private SizeOfExpressionSyntax ParseSizeOfExpression()
{
var keyword = this.EatToken();
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var type = this.ParseType(false);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.SizeOfExpression(keyword, openParen, type, closeParen);
}
private MakeRefExpressionSyntax ParseMakeRefExpression()
{
var keyword = this.EatToken();
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expr = this.ParseSubExpression(0);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.MakeRefExpression(keyword, openParen, expr, closeParen);
}
private RefTypeExpressionSyntax ParseRefTypeExpression()
{
var keyword = this.EatToken();
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expr = this.ParseSubExpression(0);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.RefTypeExpression(keyword, openParen, expr, closeParen);
}
private CheckedExpressionSyntax ParseCheckedOrUncheckedExpression()
{
var checkedOrUnchecked = this.EatToken();
Debug.Assert(checkedOrUnchecked.Kind == SyntaxKind.CheckedKeyword || checkedOrUnchecked.Kind == SyntaxKind.UncheckedKeyword);
var kind = (checkedOrUnchecked.Kind == SyntaxKind.CheckedKeyword) ? SyntaxKind.CheckedExpression : SyntaxKind.UncheckedExpression;
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expr = this.ParseSubExpression(0);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.CheckedExpression(kind, checkedOrUnchecked, openParen, expr, closeParen);
}
private RefValueExpressionSyntax ParseRefValueExpression()
{
var @refvalue = this.EatToken(SyntaxKind.RefValueKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expr = this.ParseSubExpression(0);
var comma = this.EatToken(SyntaxKind.CommaToken);
var type = this.ParseType(false);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.RefValueExpression(@refvalue, openParen, expr, comma, type, closeParen);
}
private bool ScanParenthesizedImplicitlyTypedLambda(uint precedence)
{
if (!(precedence <= LambdaPrecedence))
{
return false;
}
// case 1: ( x ,
if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken
&& (!this.IsInQuery || !IsTokenQueryContextualKeyword(this.PeekToken(1)))
&& this.PeekToken(2).Kind == SyntaxKind.CommaToken)
{
return true;
}
// case 2: ( x ) =>
if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken
&& (!this.IsInQuery || !IsTokenQueryContextualKeyword(this.PeekToken(1)))
&& this.PeekToken(2).Kind == SyntaxKind.CloseParenToken
&& this.PeekToken(3).Kind == SyntaxKind.EqualsGreaterThanToken)
{
return true;
}
// case 3: ( ) =>
if (this.PeekToken(1).Kind == SyntaxKind.CloseParenToken
&& this.PeekToken(2).Kind == SyntaxKind.EqualsGreaterThanToken)
{
return true;
}
// case 4: ( params
// This case is interesting in that it is not legal; this error could be caught at parse time but we would rather
// recover from the error and let the semantic analyzer deal with it.
if (this.PeekToken(1).Kind == SyntaxKind.ParamsKeyword)
{
return true;
}
return false;
}
private bool ScanExplicitlyTypedLambda(uint precedence)
{
if (!(precedence <= LambdaPrecedence))
{
return false;
}
var resetPoint = this.GetResetPoint();
try
{
// do we have the following:
// case 1: ( T x ,
// case 2: ( T x ) =>
// case 3: ( out T x,
// case 4: ( ref T x,
// case 5: ( out T x ) =>
// case 6: ( ref T x ) =>
//
// if so then parse it as a lambda
// Advance past the open paren.
this.EatToken();
// Eat 'out' or 'ref' for cases [3, 6]
if (this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword)
{
this.EatToken();
}
// NOTE: if we see "out" or ref" and part of cases 3,4,5,6 followed by EOF, we'll parse as a lambda.
if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken)
{
return true;
}
// NOTE: advances CurrentToken
if (this.ScanType() == ScanTypeFlags.NotType)
{
return false;
}
if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken)
{
return true;
}
if (!this.IsTrueIdentifier())
{
return false;
}
switch (this.PeekToken(1).Kind)
{
case SyntaxKind.EndOfFileToken:
case SyntaxKind.CommaToken:
return true;
case SyntaxKind.CloseParenToken:
switch (this.PeekToken(2).Kind)
{
case SyntaxKind.EndOfFileToken:
case SyntaxKind.EqualsGreaterThanToken:
return true;
default:
return false;
}
default:
return false;
}
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}
private ExpressionSyntax ParseCastOrParenExpressionOrLambda(uint precedence)
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.OpenParenToken);
var resetPoint = this.GetResetPoint();
try
{
if (ScanParenthesizedImplicitlyTypedLambda(precedence))
{
return this.ParseLambdaExpression();
}
// We have a decision to make -- is this a cast, or is it a parenthesized
// expression? Because look-ahead is cheap with our token stream, we check
// to see if this "looks like" a cast (without constructing any parse trees)
// to help us make the decision.
if (this.ScanCast())
{
if (!IsCurrentTokenQueryKeywordInQuery())
{
// Looks like a cast, so parse it as one.
this.Reset(ref resetPoint);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var type = this.ParseType(false);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var expr = this.ParseSubExpression(GetPrecedence(SyntaxKind.CastExpression));
return _syntaxFactory.CastExpression(openParen, type, closeParen, expr);
}
}
this.Reset(ref resetPoint);
if (this.ScanExplicitlyTypedLambda(precedence))
{
return this.ParseLambdaExpression();
}
// Doesn't look like a cast, so parse this as a parenthesized expression.
{
this.Reset(ref resetPoint);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var expression = this.ParseSubExpression(0);
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.ParenthesizedExpression(openParen, expression, closeParen);
}
}
finally
{
this.Release(ref resetPoint);
}
}
private bool ScanCast()
{
if (this.CurrentToken.Kind != SyntaxKind.OpenParenToken)
{
return false;
}
this.EatToken();
var type = this.ScanType();
if (type == ScanTypeFlags.NotType)
{
return false;
}
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
return false;
}
// If we have any of the following, we know it must be a cast:
// 1) (Foo*)bar;
// 2) (Foo?)bar;
// 3) "(int)bar" or "(int[])bar"
// 4) (G::Foo)bar
if (type == ScanTypeFlags.PointerOrMultiplication ||
type == ScanTypeFlags.NullableType ||
type == ScanTypeFlags.MustBeType ||
type == ScanTypeFlags.AliasQualifiedName)
{
return true;
}
this.EatToken();
// check for ambiguous type or expression followed by disambiguating token. i.e.
//
// "(A)b" is a cast. But "(A)+b" is not a cast.
return (type == ScanTypeFlags.GenericTypeOrMethod || type == ScanTypeFlags.GenericTypeOrExpression || type == ScanTypeFlags.NonGenericTypeOrExpression) && CanFollowCast(this.CurrentToken.Kind);
}
private bool ScanAsyncLambda(uint precedence)
{
// Adapted from CParser::ScanAsyncLambda
// Precedence must not exceed that of lambdas
if (precedence > LambdaPrecedence)
{
return false;
}
// Async lambda must start with 'async'
if (this.CurrentToken.ContextualKind != SyntaxKind.AsyncKeyword)
{
return false;
}
// 'async <identifier> => ...' looks like an async simple lambda
if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken && this.PeekToken(2).Kind == SyntaxKind.EqualsGreaterThanToken)
{
return true;
}
// Non-simple async lambda must be of the form 'async (...'
if (this.PeekToken(1).Kind != SyntaxKind.OpenParenToken)
{
return false;
}
{
var resetPoint = this.GetResetPoint();
// Skip 'async'
EatToken(SyntaxKind.IdentifierToken);
// Check whether looks like implicitly or explicitly typed lambda
bool isAsync = ScanParenthesizedImplicitlyTypedLambda(precedence) || ScanExplicitlyTypedLambda(precedence);
// Restore current token index
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
return isAsync;
}
}
private static bool CanFollowCast(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.AsKeyword:
case SyntaxKind.IsKeyword:
case SyntaxKind.SemicolonToken:
case SyntaxKind.CloseParenToken:
case SyntaxKind.CloseBracketToken:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CommaToken:
case SyntaxKind.EqualsToken:
case SyntaxKind.PlusEqualsToken:
case SyntaxKind.MinusEqualsToken:
case SyntaxKind.AsteriskEqualsToken:
case SyntaxKind.SlashEqualsToken:
case SyntaxKind.PercentEqualsToken:
case SyntaxKind.AmpersandEqualsToken:
case SyntaxKind.CaretEqualsToken:
case SyntaxKind.BarEqualsToken:
case SyntaxKind.LessThanLessThanEqualsToken:
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
case SyntaxKind.QuestionToken:
case SyntaxKind.ColonToken:
case SyntaxKind.BarBarToken:
case SyntaxKind.AmpersandAmpersandToken:
case SyntaxKind.BarToken:
case SyntaxKind.CaretToken:
case SyntaxKind.AmpersandToken:
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.GreaterThanEqualsToken:
case SyntaxKind.LessThanLessThanToken:
case SyntaxKind.GreaterThanGreaterThanToken:
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
case SyntaxKind.OpenBracketToken:
case SyntaxKind.DotToken:
case SyntaxKind.MinusGreaterThanToken:
case SyntaxKind.QuestionQuestionToken:
case SyntaxKind.EndOfFileToken:
return false;
default:
return true;
}
}
private ExpressionSyntax ParseNewExpression()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.NewKeyword);
if (this.IsAnonymousType())
{
return this.ParseAnonymousTypeExpression();
}
else if (this.IsImplicitlyTypedArray())
{
return this.ParseImplicitlyTypedArrayCreation();
}
else
{
// assume object creation as default case
return this.ParseArrayOrObjectCreationExpression();
}
}
private bool IsAnonymousType()
{
return this.CurrentToken.Kind == SyntaxKind.NewKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken;
}
private AnonymousObjectCreationExpressionSyntax ParseAnonymousTypeExpression()
{
Debug.Assert(IsAnonymousType());
var @new = this.EatToken(SyntaxKind.NewKeyword);
@new = CheckFeatureAvailability(@new, MessageID.IDS_FeatureAnonymousTypes);
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.OpenBraceToken);
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
var expressions = _pool.AllocateSeparated<AnonymousObjectMemberDeclaratorSyntax>();
this.ParseAnonymousTypeMemberInitializers(ref openBrace, ref expressions);
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
var result = _syntaxFactory.AnonymousObjectCreationExpression(@new, openBrace, expressions, closeBrace);
_pool.Free(expressions);
return result;
}
private void ParseAnonymousTypeMemberInitializers(ref SyntaxToken openBrace, ref SeparatedSyntaxListBuilder<AnonymousObjectMemberDeclaratorSyntax> list)
{
if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken)
{
tryAgain:
if (this.IsPossibleExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first argument
list.Add(this.ParseAnonymousTypeMemberInitializer());
// additional arguments
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleExpression())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
// check for exit case after legal trailing comma
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (!this.IsPossibleExpression())
{
goto tryAgain;
}
list.Add(this.ParseAnonymousTypeMemberInitializer());
continue;
}
else if (this.SkipBadInitializerListTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadInitializerListTokens(ref openBrace, list, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
}
private AnonymousObjectMemberDeclaratorSyntax ParseAnonymousTypeMemberInitializer()
{
bool isNamedAssignment = this.IsNamedAssignment();
NameEqualsSyntax nameEquals = null;
if (isNamedAssignment)
{
nameEquals = ParseNameEquals();
}
var expression = this.ParseExpressionCore();
if (!isNamedAssignment && !IsAnonymousTypeMemberExpression(expression))
{
expression = this.AddError(expression, ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator);
}
return _syntaxFactory.AnonymousObjectMemberDeclarator(nameEquals, expression);
}
private bool IsAnonymousTypeMemberExpression(ExpressionSyntax expr)
{
while (true)
{
switch (expr.Kind)
{
case SyntaxKind.QualifiedName:
expr = ((QualifiedNameSyntax)expr).Right;
continue;
case SyntaxKind.ConditionalAccessExpression:
expr = ((ConditionalAccessExpressionSyntax)expr).WhenNotNull;
if (expr.Kind == SyntaxKind.MemberBindingExpression)
{
return true;
}
continue;
case SyntaxKind.IdentifierName:
case SyntaxKind.SimpleMemberAccessExpression:
return true;
default:
return false;
}
}
}
private bool IsInitializerMember()
{
return this.IsComplexElementInitializer() ||
this.IsNamedAssignment() ||
this.IsDictionaryInitializer() ||
this.IsPossibleExpression();
}
private bool IsComplexElementInitializer()
{
return this.CurrentToken.Kind == SyntaxKind.OpenBraceToken;
}
private bool IsNamedAssignment()
{
return IsTrueIdentifier() && this.PeekToken(1).Kind == SyntaxKind.EqualsToken;
}
private bool IsDictionaryInitializer()
{
return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken;
}
private ExpressionSyntax ParseArrayOrObjectCreationExpression()
{
SyntaxToken @new = this.EatToken(SyntaxKind.NewKeyword);
bool isPossibleArrayCreation = this.IsPossibleArrayCreationExpression();
var type = this.ParseTypeCore(parentIsParameter: false, isOrAs: false, expectSizes: isPossibleArrayCreation, isArrayCreation: isPossibleArrayCreation);
if (type.Kind == SyntaxKind.ArrayType)
{
// Check for an initializer.
InitializerExpressionSyntax initializer = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
initializer = this.ParseArrayInitializer();
}
else if (type.Kind == SyntaxKind.ArrayType)
{
var rankSpec = ((ArrayTypeSyntax)type).RankSpecifiers[0];
if (GetNumberOfNonOmittedArraySizes(rankSpec) == 0)
{
type = this.AddError(type, rankSpec, ErrorCode.ERR_MissingArraySize);
}
}
return _syntaxFactory.ArrayCreationExpression(@new, (ArrayTypeSyntax)type, initializer);
}
else
{
ArgumentListSyntax argumentList = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
argumentList = this.ParseParenthesizedArgumentList();
}
InitializerExpressionSyntax initializer = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
initializer = this.ParseObjectOrCollectionInitializer();
}
// we need one or the other
if (argumentList == null && initializer == null)
{
argumentList = _syntaxFactory.ArgumentList(
this.AddError(SyntaxFactory.MissingToken(SyntaxKind.OpenParenToken), ErrorCode.ERR_BadNewExpr),
default(SeparatedSyntaxList<ArgumentSyntax>),
SyntaxFactory.MissingToken(SyntaxKind.CloseParenToken));
}
return _syntaxFactory.ObjectCreationExpression(@new, type, argumentList, initializer);
}
}
private static int GetNumberOfNonOmittedArraySizes(ArrayRankSpecifierSyntax rankSpec)
{
int count = rankSpec.Sizes.Count;
int result = 0;
for (int i = 0; i < count; i++)
{
if (rankSpec.Sizes[i].Kind != SyntaxKind.OmittedArraySizeExpression)
{
result++;
}
}
return result;
}
private bool IsPossibleArrayCreationExpression()
{
// previous token should be NewKeyword
var resetPoint = this.GetResetPoint();
try
{
ScanTypeFlags isType = this.ScanNonArrayType();
return isType != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.OpenBracketToken;
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}
private InitializerExpressionSyntax ParseObjectOrCollectionInitializer()
{
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
var initializers = _pool.AllocateSeparated<ExpressionSyntax>();
try
{
bool isObjectInitializer;
this.ParseObjectOrCollectionInitializerMembers(ref openBrace, initializers, out isObjectInitializer);
Debug.Assert(initializers.Count > 0 || isObjectInitializer);
openBrace = CheckFeatureAvailability(openBrace, isObjectInitializer ? MessageID.IDS_FeatureObjectInitializer : MessageID.IDS_FeatureCollectionInitializer);
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
return _syntaxFactory.InitializerExpression(
isObjectInitializer ?
SyntaxKind.ObjectInitializerExpression :
SyntaxKind.CollectionInitializerExpression,
openBrace,
initializers,
closeBrace);
}
finally
{
_pool.Free(initializers);
}
}
private void ParseObjectOrCollectionInitializerMembers(ref SyntaxToken startToken, SeparatedSyntaxListBuilder<ExpressionSyntax> list, out bool isObjectInitializer)
{
// Empty initializer list must be parsed as an object initializer.
isObjectInitializer = true;
if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken)
{
tryAgain:
if (this.IsInitializerMember() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// We have at least one initializer expression.
// If at least one initializer expression is a named assignment, this is an object initializer.
// Otherwise, this is a collection initializer.
isObjectInitializer = false;
// first argument
list.Add(this.ParseObjectOrCollectionInitializerMember(ref isObjectInitializer));
// additional arguments
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsInitializerMember())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
// check for exit case after legal trailing comma
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
list.Add(this.ParseObjectOrCollectionInitializerMember(ref isObjectInitializer));
continue;
}
else if (this.SkipBadInitializerListTokens(ref startToken, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadInitializerListTokens(ref startToken, list, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
// We may have invalid initializer elements. These will be reported during binding.
}
private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObjectInitializer)
{
if (this.IsComplexElementInitializer())
{
return this.ParseComplexElementInitializer();
}
else if (IsDictionaryInitializer())
{
isObjectInitializer = true;
var initializer = this.ParseDictionaryInitializer();
initializer = CheckFeatureAvailability(initializer, MessageID.IDS_FeatureDictionaryInitializer);
return initializer;
}
else if (this.IsNamedAssignment())
{
isObjectInitializer = true;
return this.ParseObjectInitializerNamedAssignment();
}
else
{
return this.ParseExpressionCore();
}
}
private PostSkipAction SkipBadInitializerListTokens<T>(ref SyntaxToken startToken, SeparatedSyntaxListBuilder<T> list, SyntaxKind expected)
where T : CSharpSyntaxNode
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref startToken, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleExpression(),
p => p.CurrentToken.Kind == SyntaxKind.CloseBraceToken || p.IsTerminator(),
expected);
}
private ExpressionSyntax ParseObjectInitializerNamedAssignment()
{
var identifier = this.ParseIdentifierName();
var equal = this.EatToken(SyntaxKind.EqualsToken);
ExpressionSyntax expression;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
expression = this.ParseObjectOrCollectionInitializer();
}
else
{
expression = this.ParseExpressionCore();
}
return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, identifier, equal, expression);
}
private ExpressionSyntax ParseDictionaryInitializer()
{
var arguments = this.ParseBracketedArgumentList();
var equal = this.EatToken(SyntaxKind.EqualsToken);
ExpressionSyntax expression;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
expression = this.ParseObjectOrCollectionInitializer();
}
else
{
expression = this.ParseExpressionCore();
}
var elementAccess = _syntaxFactory.ImplicitElementAccess(arguments);
return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, elementAccess, equal, expression);
}
private InitializerExpressionSyntax ParseComplexElementInitializer()
{
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
var initializers = _pool.AllocateSeparated<ExpressionSyntax>();
try
{
DiagnosticInfo closeBraceError;
this.ParseExpressionsForComplexElementInitializer(ref openBrace, initializers, out closeBraceError);
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
if (closeBraceError != null)
{
closeBrace = WithAdditionalDiagnostics(closeBrace, closeBraceError);
}
return _syntaxFactory.InitializerExpression(SyntaxKind.ComplexElementInitializerExpression, openBrace, initializers, closeBrace);
}
finally
{
_pool.Free(initializers);
}
}
private void ParseExpressionsForComplexElementInitializer(ref SyntaxToken openBrace, SeparatedSyntaxListBuilder<ExpressionSyntax> list, out DiagnosticInfo closeBraceError)
{
closeBraceError = null;
if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken)
{
tryAgain:
if (this.IsPossibleExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first argument
list.Add(this.ParseExpressionCore());
// additional arguments
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleExpression())
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
closeBraceError = MakeError(this.CurrentToken, ErrorCode.ERR_ExpressionExpected);
break;
}
list.Add(this.ParseExpressionCore());
continue;
}
else if (this.SkipBadInitializerListTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadInitializerListTokens(ref openBrace, list, SyntaxKind.IdentifierToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
}
private ExpressionSyntax ParseElementInitializer()
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
return this.ParseComplexElementInitializer();
}
else
{
return this.ParseExpressionCore();
}
}
private bool IsImplicitlyTypedArray()
{
return this.CurrentToken.Kind == SyntaxKind.NewKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBracketToken;
}
private ImplicitArrayCreationExpressionSyntax ParseImplicitlyTypedArrayCreation()
{
var @new = this.EatToken(SyntaxKind.NewKeyword);
@new = CheckFeatureAvailability(@new, MessageID.IDS_FeatureImplicitArray);
var openBracket = this.EatToken(SyntaxKind.OpenBracketToken);
var commas = _pool.Allocate();
try
{
while (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
commas.Add(this.EatToken());
}
var closeBracket = this.EatToken(SyntaxKind.CloseBracketToken);
var initializer = this.ParseArrayInitializer();
return _syntaxFactory.ImplicitArrayCreationExpression(@new, openBracket, commas.ToTokenList(), closeBracket, initializer);
}
finally
{
_pool.Free(commas);
}
}
private InitializerExpressionSyntax ParseArrayInitializer()
{
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
// NOTE: This loop allows " { <initexpr>, } " but not " { , } "
var list = _pool.AllocateSeparated<ExpressionSyntax>();
try
{
if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken)
{
tryAgain:
if (this.IsPossibleVariableInitializer(false) || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
list.Add(this.ParseVariableInitializer(false));
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (this.IsPossibleVariableInitializer(false) || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
// check for exit case after legal trailing comma
if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken)
{
break;
}
else if (!this.IsPossibleVariableInitializer(false))
{
goto tryAgain;
}
list.Add(this.ParseVariableInitializer(false));
continue;
}
else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
return _syntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, openBrace, list, closeBrace);
}
finally
{
_pool.Free(list);
}
}
private PostSkipAction SkipBadArrayInitializerTokens(ref SyntaxToken openBrace, SeparatedSyntaxListBuilder<ExpressionSyntax> list, SyntaxKind expected)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref openBrace, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleVariableInitializer(false),
p => this.CurrentToken.Kind == SyntaxKind.CloseBraceToken || this.IsTerminator(),
expected);
}
private StackAllocArrayCreationExpressionSyntax ParseStackAllocExpression()
{
var stackAlloc = this.EatToken(SyntaxKind.StackAllocKeyword);
var elementType = this.ParseTypeCore(parentIsParameter: false, isOrAs: false, expectSizes: true, isArrayCreation: false);
if (elementType.Kind != SyntaxKind.ArrayType)
{
elementType = this.AddError(elementType, ErrorCode.ERR_BadStackAllocExpr);
}
return _syntaxFactory.StackAllocArrayCreationExpression(stackAlloc, elementType);
}
private AnonymousMethodExpressionSyntax ParseAnonymousMethodExpression()
{
bool parentScopeIsInAsync = IsInAsync;
IsInAsync = false;
SyntaxToken asyncToken = null;
if (this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword)
{
asyncToken = this.EatContextualToken(SyntaxKind.AsyncKeyword);
asyncToken = CheckFeatureAvailability(asyncToken, MessageID.IDS_FeatureAsync);
IsInAsync = true;
}
var @delegate = this.EatToken(SyntaxKind.DelegateKeyword);
@delegate = CheckFeatureAvailability(@delegate, MessageID.IDS_FeatureAnonDelegates);
ParameterListSyntax parameterList = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
parameterList = this.ParseParenthesizedParameterList(allowThisKeyword: false, allowDefaults: false, allowAttributes: false);
}
// In mismatched braces cases (missing a }) it is possible for delegate declarations to be
// parsed as delegate statement expressions. When this situation occurs all subsequent
// delegate declarations will also be parsed as delegate statement expressions. In a file with
// a sufficient number of delegates, common in generated code, it will put considerable
// stack pressure on the parser.
//
// To help avoid this problem we don't recursively descend into a delegate expression unless
// { } are actually present. This keeps the stack pressure lower in bad code scenarios.
if (this.CurrentToken.Kind != SyntaxKind.OpenBraceToken)
{
// There's a special error code for a missing token after an accessor keyword
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
return _syntaxFactory.AnonymousMethodExpression(
asyncToken,
@delegate,
parameterList,
_syntaxFactory.Block(
openBrace,
default(SyntaxList<StatementSyntax>),
SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken)));
}
var body = this.ParseBlock();
IsInAsync = parentScopeIsInAsync;
return _syntaxFactory.AnonymousMethodExpression(asyncToken, @delegate, parameterList, body);
}
private const int LambdaPrecedence = 1;
private ExpressionSyntax ParseLambdaExpression()
{
bool parentScopeIsInAsync = IsInAsync;
SyntaxToken asyncToken = null;
if (this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword && PeekToken(1).Kind != SyntaxKind.EqualsGreaterThanToken)
{
asyncToken = this.EatContextualToken(SyntaxKind.AsyncKeyword);
asyncToken = CheckFeatureAvailability(asyncToken, MessageID.IDS_FeatureAsync);
IsInAsync = true;
}
ExpressionSyntax result;
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
var paramList = this.ParseLambdaParameterList();
var arrow = this.EatToken(SyntaxKind.EqualsGreaterThanToken);
arrow = CheckFeatureAvailability(arrow, MessageID.IDS_FeatureLambda);
CSharpSyntaxNode body;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
body = this.ParseBlock();
}
else
{
body = this.ParseExpressionCore();
}
result = _syntaxFactory.ParenthesizedLambdaExpression(asyncToken, paramList, arrow, body);
}
else
{
var name = this.ParseIdentifierToken();
var arrow = this.EatToken(SyntaxKind.EqualsGreaterThanToken);
arrow = CheckFeatureAvailability(arrow, MessageID.IDS_FeatureLambda);
CSharpSyntaxNode body = null;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
body = this.ParseBlock();
}
else
{
body = this.ParseExpressionCore();
}
result = _syntaxFactory.SimpleLambdaExpression(
asyncToken,
_syntaxFactory.Parameter(default(SyntaxList<AttributeListSyntax>), default(SyntaxList<SyntaxToken>), type: null, identifier: name, @default: null),
arrow,
body);
}
IsInAsync = parentScopeIsInAsync;
return result;
}
private ParameterListSyntax ParseLambdaParameterList()
{
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var saveTerm = _termState;
_termState |= TerminatorState.IsEndOfParameterList;
var nodes = _pool.AllocateSeparated<ParameterSyntax>();
try
{
bool hasTypes = false;
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
tryAgain:
if (this.IsPossibleLambdaParameter() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
// first parameter
var parameter = this.ParseLambdaParameter(isFirst: true, hasTypes: ref hasTypes);
nodes.Add(parameter);
// additional parameters
while (true)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter())
{
nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
parameter = this.ParseLambdaParameter(false, ref hasTypes);
nodes.Add(parameter);
continue;
}
else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.CommaToken, SyntaxKind.CloseParenToken) == PostSkipAction.Abort)
{
break;
}
}
}
else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.IdentifierToken, SyntaxKind.CloseParenToken) == PostSkipAction.Continue)
{
goto tryAgain;
}
}
_termState = saveTerm;
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
return _syntaxFactory.ParameterList(openParen, nodes, closeParen);
}
finally
{
_pool.Free(nodes);
}
}
private bool IsPossibleLambdaParameter()
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.ParamsKeyword:
// params is not actually legal in a lambda, but we allow it for error
// recovery purposes and then give an error during semantic analysis.
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
return true;
case SyntaxKind.IdentifierToken:
return this.IsTrueIdentifier();
default:
return IsPredefinedType(this.CurrentToken.Kind);
}
}
private PostSkipAction SkipBadLambdaParameterListTokens(ref SyntaxToken openParen, SeparatedSyntaxListBuilder<ParameterSyntax> list, SyntaxKind expected, SyntaxKind closeKind)
{
return this.SkipBadSeparatedListTokensWithExpectedKind(ref openParen, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleLambdaParameter(),
p => p.CurrentToken.Kind == closeKind || p.IsTerminator(),
expected);
}
private ParameterSyntax ParseLambdaParameter(bool isFirst, ref bool hasTypes)
{
TypeSyntax paramType = null;
SyntaxToken paramName = null;
SyntaxToken refOrOutOrParams = null;
// Params are actually illegal in a lambda, but we'll allow it for error recovery purposes and
// give the "params unexpected" error at semantic analysis time.
bool isRefOrOutOrParams = this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword || this.CurrentToken.Kind == SyntaxKind.ParamsKeyword;
var pk = this.PeekToken(1).Kind;
if (isRefOrOutOrParams
|| (pk != SyntaxKind.CommaToken && pk != SyntaxKind.CloseParenToken && (hasTypes || isFirst))
|| IsPredefinedType(this.CurrentToken.Kind))
{
if (isRefOrOutOrParams)
{
refOrOutOrParams = this.EatToken();
}
paramType = this.ParseType(true);
}
paramName = this.ParseIdentifierToken();
if (isFirst)
{
hasTypes = paramType != null;
}
else if (paramType != null && !hasTypes && !paramName.IsMissing)
{
paramType = this.AddError(paramType, ErrorCode.ERR_InconsistentLambdaParameterUsage);
}
else if (paramType == null && hasTypes && !paramName.IsMissing)
{
paramName = this.AddError(paramName, ErrorCode.ERR_InconsistentLambdaParameterUsage);
}
return _syntaxFactory.Parameter(default(SyntaxList<AttributeListSyntax>), refOrOutOrParams, paramType, paramName, null);
}
private bool IsCurrentTokenQueryContextualKeyword
{
get
{
return IsTokenQueryContextualKeyword(this.CurrentToken);
}
}
private static bool IsTokenQueryContextualKeyword(SyntaxToken token)
{
if (IsTokenStartOfNewQueryClause(token))
{
return true;
}
switch (token.ContextualKind)
{
case SyntaxKind.OnKeyword:
case SyntaxKind.EqualsKeyword:
case SyntaxKind.AscendingKeyword:
case SyntaxKind.DescendingKeyword:
case SyntaxKind.ByKeyword:
return true;
}
return false;
}
private static bool IsTokenStartOfNewQueryClause(SyntaxToken token)
{
switch (token.ContextualKind)
{
case SyntaxKind.FromKeyword:
case SyntaxKind.JoinKeyword:
case SyntaxKind.IntoKeyword:
case SyntaxKind.WhereKeyword:
case SyntaxKind.OrderByKeyword:
case SyntaxKind.GroupKeyword:
case SyntaxKind.SelectKeyword:
case SyntaxKind.LetKeyword:
return true;
default:
return false;
}
}
private bool IsQueryExpression(bool mayBeVariableDeclaration, bool mayBeMemberDeclaration)
{
if (this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword)
{
return this.IsQueryExpressionAfterFrom(mayBeVariableDeclaration, mayBeMemberDeclaration);
}
return false;
}
// from_clause ::= from <type>? <identifier> in expression
private bool IsQueryExpressionAfterFrom(bool mayBeVariableDeclaration, bool mayBeMemberDeclaration)
{
// from x ...
var pk1 = this.PeekToken(1).Kind;
if (IsPredefinedType(pk1))
{
return true;
}
if (pk1 == SyntaxKind.IdentifierToken)
{
var pk2 = this.PeekToken(2).Kind;
if (pk2 == SyntaxKind.InKeyword)
{
return true;
}
if (mayBeVariableDeclaration)
{
if (pk2 == SyntaxKind.SemicolonToken || // from x;
pk2 == SyntaxKind.CommaToken || // from x, y;
pk2 == SyntaxKind.EqualsToken) // from x = null;
{
return false;
}
}
if (mayBeMemberDeclaration)
{
// from idf { ... property decl
// from idf(... method decl
if (pk2 == SyntaxKind.OpenParenToken ||
pk2 == SyntaxKind.OpenBraceToken)
{
return false;
}
// otherwise we need to scan a type
}
else
{
return true;
}
}
// from T x ...
var resetPoint = this.GetResetPoint();
try
{
this.EatToken();
ScanTypeFlags isType = this.ScanType();
if (isType != ScanTypeFlags.NotType && (this.CurrentToken.Kind == SyntaxKind.IdentifierToken || this.CurrentToken.Kind == SyntaxKind.InKeyword))
{
return true;
}
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
return false;
}
private QueryExpressionSyntax ParseQueryExpression(uint precedence)
{
this.EnterQuery();
var fc = this.ParseFromClause();
fc = CheckFeatureAvailability(fc, MessageID.IDS_FeatureQueryExpression);
if (precedence > 1 && IsStrict)
{
fc = this.AddError(fc, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(SyntaxKind.FromKeyword));
}
var body = this.ParseQueryBody();
this.LeaveQuery();
return _syntaxFactory.QueryExpression(fc, body);
}
private QueryBodySyntax ParseQueryBody()
{
var clauses = _pool.Allocate<QueryClauseSyntax>();
try
{
SelectOrGroupClauseSyntax selectOrGroupBy = null;
QueryContinuationSyntax continuation = null;
// from, join, let, where and orderby
while (true)
{
switch (this.CurrentToken.ContextualKind)
{
case SyntaxKind.FromKeyword:
var fc = this.ParseFromClause();
clauses.Add(fc);
continue;
case SyntaxKind.JoinKeyword:
clauses.Add(this.ParseJoinClause());
continue;
case SyntaxKind.LetKeyword:
clauses.Add(this.ParseLetClause());
continue;
case SyntaxKind.WhereKeyword:
clauses.Add(this.ParseWhereClause());
continue;
case SyntaxKind.OrderByKeyword:
clauses.Add(this.ParseOrderByClause());
continue;
}
break;
}
// select or group clause
switch (this.CurrentToken.ContextualKind)
{
case SyntaxKind.SelectKeyword:
selectOrGroupBy = this.ParseSelectClause();
break;
case SyntaxKind.GroupKeyword:
selectOrGroupBy = this.ParseGroupClause();
break;
default:
selectOrGroupBy = this.AddError(_syntaxFactory.SelectClause(SyntaxFactory.MissingToken(SyntaxKind.SelectKeyword), this.CreateMissingIdentifierName()), ErrorCode.ERR_ExpectedSelectOrGroup);
break;
}
// optional query continuation clause
if (this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword)
{
continuation = this.ParseQueryContinuation();
}
return _syntaxFactory.QueryBody(clauses, selectOrGroupBy, continuation);
}
finally
{
_pool.Free(clauses);
}
}
private FromClauseSyntax ParseFromClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword);
var @from = this.EatContextualToken(SyntaxKind.FromKeyword);
@from = CheckFeatureAvailability(@from, MessageID.IDS_FeatureQueryExpression);
TypeSyntax type = null;
if (this.PeekToken(1).Kind != SyntaxKind.InKeyword)
{
type = this.ParseType(false);
}
SyntaxToken name;
if (this.PeekToken(1).ContextualKind == SyntaxKind.InKeyword &&
(this.CurrentToken.Kind != SyntaxKind.IdentifierToken || SyntaxFacts.IsQueryContextualKeyword(this.CurrentToken.ContextualKind)))
{
//if this token is a something other than an identifier (someone accidentally used a contextual
//keyword or a literal, for example), but we can see that the "in" is in the right place, then
//just replace whatever is here with a missing identifier
name = this.EatToken();
name = WithAdditionalDiagnostics(name, this.GetExpectedTokenError(SyntaxKind.IdentifierToken, name.ContextualKind, name.GetLeadingTriviaWidth(), name.Width));
name = this.ConvertToMissingWithTrailingTrivia(name, SyntaxKind.IdentifierToken);
}
else
{
name = this.ParseIdentifierToken();
}
var @in = this.EatToken(SyntaxKind.InKeyword);
var expression = this.ParseExpressionCore();
return _syntaxFactory.FromClause(@from, type, name, @in, expression);
}
private JoinClauseSyntax ParseJoinClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.JoinKeyword);
var @join = this.EatContextualToken(SyntaxKind.JoinKeyword);
TypeSyntax type = null;
if (this.PeekToken(1).Kind != SyntaxKind.InKeyword)
{
type = this.ParseType(false);
}
var name = this.ParseIdentifierToken();
var @in = this.EatToken(SyntaxKind.InKeyword);
var inExpression = this.ParseExpressionCore();
var @on = this.EatContextualToken(SyntaxKind.OnKeyword, ErrorCode.ERR_ExpectedContextualKeywordOn);
var leftExpression = this.ParseExpressionCore();
var @equals = this.EatContextualToken(SyntaxKind.EqualsKeyword, ErrorCode.ERR_ExpectedContextualKeywordEquals);
var rightExpression = this.ParseExpressionCore();
JoinIntoClauseSyntax joinInto = null;
if (this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword)
{
var @into = ConvertToKeyword(this.EatToken());
var intoId = this.ParseIdentifierToken();
joinInto = _syntaxFactory.JoinIntoClause(@into, intoId);
}
return _syntaxFactory.JoinClause(@join, type, name, @in, inExpression, @on, leftExpression, @equals, rightExpression, joinInto);
}
private LetClauseSyntax ParseLetClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.LetKeyword);
var @let = this.EatContextualToken(SyntaxKind.LetKeyword);
var name = this.ParseIdentifierToken();
var equal = this.EatToken(SyntaxKind.EqualsToken);
var expression = this.ParseExpressionCore();
return _syntaxFactory.LetClause(@let, name, equal, expression);
}
private WhereClauseSyntax ParseWhereClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword);
var @where = this.EatContextualToken(SyntaxKind.WhereKeyword);
var condition = this.ParseExpressionCore();
return _syntaxFactory.WhereClause(@where, condition);
}
private OrderByClauseSyntax ParseOrderByClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.OrderByKeyword);
var @orderby = this.EatContextualToken(SyntaxKind.OrderByKeyword);
var list = _pool.AllocateSeparated<OrderingSyntax>();
try
{
// first argument
list.Add(this.ParseOrdering());
// additional arguments
while (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken)
{
break;
}
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken)
{
list.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
list.Add(this.ParseOrdering());
continue;
}
else if (this.SkipBadOrderingListTokens(list, SyntaxKind.CommaToken) == PostSkipAction.Abort)
{
break;
}
}
return _syntaxFactory.OrderByClause(@orderby, list);
}
finally
{
_pool.Free(list);
}
}
private PostSkipAction SkipBadOrderingListTokens(SeparatedSyntaxListBuilder<OrderingSyntax> list, SyntaxKind expected)
{
CSharpSyntaxNode tmp = null;
Debug.Assert(list.Count > 0);
return this.SkipBadSeparatedListTokensWithExpectedKind(ref tmp, list,
p => p.CurrentToken.Kind != SyntaxKind.CommaToken,
p => p.CurrentToken.Kind == SyntaxKind.CloseParenToken
|| p.CurrentToken.Kind == SyntaxKind.SemicolonToken
|| p.IsCurrentTokenQueryContextualKeyword
|| p.IsTerminator(),
expected);
}
private OrderingSyntax ParseOrdering()
{
var expression = this.ParseExpressionCore();
SyntaxToken direction = null;
SyntaxKind kind = SyntaxKind.AscendingOrdering;
if (this.CurrentToken.ContextualKind == SyntaxKind.AscendingKeyword ||
this.CurrentToken.ContextualKind == SyntaxKind.DescendingKeyword)
{
direction = ConvertToKeyword(this.EatToken());
if (direction.Kind == SyntaxKind.DescendingKeyword)
{
kind = SyntaxKind.DescendingOrdering;
}
}
return _syntaxFactory.Ordering(kind, expression, direction);
}
private SelectClauseSyntax ParseSelectClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.SelectKeyword);
var @select = this.EatContextualToken(SyntaxKind.SelectKeyword);
var expression = this.ParseExpressionCore();
return _syntaxFactory.SelectClause(@select, expression);
}
private GroupClauseSyntax ParseGroupClause()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.GroupKeyword);
var @group = this.EatContextualToken(SyntaxKind.GroupKeyword);
var groupExpression = this.ParseExpressionCore();
var @by = this.EatContextualToken(SyntaxKind.ByKeyword, ErrorCode.ERR_ExpectedContextualKeywordBy);
var byExpression = this.ParseExpressionCore();
return _syntaxFactory.GroupClause(@group, groupExpression, @by, byExpression);
}
private QueryContinuationSyntax ParseQueryContinuation()
{
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword);
var @into = this.EatContextualToken(SyntaxKind.IntoKeyword);
var name = this.ParseIdentifierToken();
var body = this.ParseQueryBody();
return _syntaxFactory.QueryContinuation(@into, name, body);
}
private bool IsStrict => this.Options.Features.ContainsKey("strict");
[Obsolete("Use IsIncrementalAndFactoryContextMatches")]
private new bool IsIncremental
{
get { throw new Exception("Use IsIncrementalAndFactoryContextMatches"); }
}
private bool IsIncrementalAndFactoryContextMatches
{
get
{
if (!base.IsIncremental)
{
return false;
}
CSharp.CSharpSyntaxNode current = this.CurrentNode;
return current != null && MatchesFactoryContext(current.Green, _syntaxFactoryContext);
}
}
internal static bool MatchesFactoryContext(GreenNode green, SyntaxFactoryContext context)
{
return context.IsInAsync == green.ParsedInAsync &&
context.IsInQuery == green.ParsedInQuery;
}
private bool IsInAsync
{
get
{
return _syntaxFactoryContext.IsInAsync;
}
set
{
_syntaxFactoryContext.IsInAsync = value;
}
}
private bool IsInQuery
{
get { return _syntaxFactoryContext.IsInQuery; }
}
private void EnterQuery()
{
_syntaxFactoryContext.QueryDepth++;
}
private void LeaveQuery()
{
Debug.Assert(_syntaxFactoryContext.QueryDepth > 0);
_syntaxFactoryContext.QueryDepth--;
}
private new ResetPoint GetResetPoint()
{
return new ResetPoint(base.GetResetPoint(), _termState, _isInTry, _syntaxFactoryContext.IsInAsync, _syntaxFactoryContext.QueryDepth);
}
private void Reset(ref ResetPoint state)
{
_termState = state.TerminatorState;
_isInTry = state.IsInTry;
_syntaxFactoryContext.IsInAsync = state.IsInAsync;
_syntaxFactoryContext.QueryDepth = state.QueryDepth;
base.Reset(ref state.BaseResetPoint);
}
private void Release(ref ResetPoint state)
{
base.Release(ref state.BaseResetPoint);
}
private new struct ResetPoint
{
internal SyntaxParser.ResetPoint BaseResetPoint;
internal readonly TerminatorState TerminatorState;
internal readonly bool IsInTry;
internal readonly bool IsInAsync;
internal readonly int QueryDepth;
internal ResetPoint(
SyntaxParser.ResetPoint resetPoint,
TerminatorState terminatorState,
bool isInTry,
bool isInAsync,
int queryDepth)
{
this.BaseResetPoint = resetPoint;
this.TerminatorState = terminatorState;
this.IsInTry = isInTry;
this.IsInAsync = isInAsync;
this.QueryDepth = queryDepth;
}
}
internal TNode ConsumeUnexpectedTokens<TNode>(TNode node) where TNode : CSharpSyntaxNode
{
if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken) return node;
SyntaxListBuilder<SyntaxToken> b = _pool.Allocate<SyntaxToken>();
while (this.CurrentToken.Kind != SyntaxKind.EndOfFileToken)
{
b.Add(this.EatToken());
}
var trailingTrash = b.ToList();
_pool.Free(b);
node = this.AddError(node, ErrorCode.ERR_UnexpectedToken, trailingTrash[0].ToString());
node = this.AddTrailingSkippedSyntax(node, trailingTrash.Node);
return node;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment