private VariableDeclaratorSyntax ParseVariableDeclarator(TypeSyntax parentType, bool isExpressionContext = false)
{
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 accidently 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; // etc.
//
// C
// A ? B : D;
var resetPoint = GetResetPoint();
try
{
var currentTokenKind = Current.Kind;
if (currentTokenKind == SyntaxKind.IdentifierToken && !parentType.IsMissing)
{
var isAfterNewLine = parentType.GetLastChildToken().TrailingTrivia.Any(t => t.Kind == SyntaxKind.EndOfLineTrivia);
if (isAfterNewLine)
{
NextToken();
currentTokenKind = Current.Kind;
var isNonEqualsBinaryToken =
currentTokenKind != SyntaxKind.EqualsToken &&
SyntaxFacts.IsBinaryExpression(currentTokenKind);
if (currentTokenKind == SyntaxKind.DotToken ||
isNonEqualsBinaryToken)
{
var missingIdentifier = InsertMissingToken(SyntaxKind.IdentifierToken);
return new VariableDeclaratorSyntax(missingIdentifier,
new List<ArrayRankSpecifierSyntax>(),
new List<VariableDeclaratorQualifierSyntax>(),
null, null);
}
}
}
}
finally
{
Reset(ref resetPoint);
}
}
var name = Match(SyntaxKind.IdentifierToken);
var arrayRankSpecifiers = new List<ArrayRankSpecifierSyntax>();
if (Current.Kind == SyntaxKind.OpenBracketToken)
ParseArrayRankSpecifiers(arrayRankSpecifiers, false);
var qualifiers = new List<VariableDeclaratorQualifierSyntax>();
while (Current.Kind == SyntaxKind.ColonToken)
{
if (IsPossibleVariableDeclaratorQualifier(Lookahead))
{
qualifiers.Add(ParseVariableDeclaratorQualifier());
}
else
{
var action = SkipBadTokens(
p => !p.IsPossibleVariableDeclaratorQualifier(Current),
p => p.Current.Kind == SyntaxKind.EqualsToken || p.Current.Kind == SyntaxKind.OpenBraceToken || p.IsTerminator(),
SyntaxKind.RegisterKeyword);
if (action == PostSkipAction.Abort)
break;
}
}
AnnotationsSyntax annotations = null;
if (Current.Kind == SyntaxKind.LessThanToken)
annotations = ParseAnnotations();
InitializerSyntax initializer = null;
if (Current.Kind == SyntaxKind.EqualsToken)
{
if (Lookahead.Kind == SyntaxKind.SamplerStateLegacyKeyword)
{
initializer = ParseSamplerStateInitializer();
}
else
{
var equals = NextToken();
var init = ParseVariableInitializer();
initializer = new EqualsValueClauseSyntax(equals, init);
}
}
else if (Current.Kind == SyntaxKind.OpenBraceToken)
{
if (Lookahead.Kind == SyntaxKind.OpenBraceToken)
initializer = ParseStateArrayInitializer();
else
initializer = ParseStateInitializer();
}
return new VariableDeclaratorSyntax(name, arrayRankSpecifiers, qualifiers, annotations, initializer);
}