diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index e82f14d54ccacaaf3b8cb8967a99fcfe50da7e85..69d3b42e33b5b45e8fedf66218ebcb5697febf4c 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -220,7 +220,7 @@ internal CompilationUnitSyntax ParseCompilationUnitCore() } private NamespaceDeclarationSyntax ParseNamespaceDeclaration( - SyntaxListBuilder attributeLists, + SyntaxList attributeLists, SyntaxListBuilder modifiers) { _recursionDepth++; @@ -231,7 +231,7 @@ internal CompilationUnitSyntax ParseCompilationUnitCore() } private NamespaceDeclarationSyntax ParseNamespaceDeclarationCore( - SyntaxListBuilder attributeLists, + SyntaxList attributeLists, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.NamespaceKeyword); @@ -741,17 +741,27 @@ private bool IsPossibleAttributeDeclaration() return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken; } - private void ParseAttributeDeclarations(SyntaxListBuilder list) + private SyntaxList ParseAttributeDeclarations() { - var saveTerm = _termState; - _termState |= TerminatorState.IsAttributeDeclarationTerminator; + var attributes = _pool.Allocate(); + try + { + var saveTerm = _termState; + _termState |= TerminatorState.IsAttributeDeclarationTerminator; - while (this.IsPossibleAttributeDeclaration()) + while (this.IsPossibleAttributeDeclaration()) + { + attributes.Add(this.ParseAttributeDeclaration()); + } + + _termState = saveTerm; + + return attributes.ToList(); + } + finally { - list.Add(this.ParseAttributeDeclaration()); + _pool.Free(attributes); } - - _termState = saveTerm; } private bool IsAttributeDeclarationTerminator() @@ -1303,7 +1313,7 @@ private bool IsPossibleMemberName() } } - private MemberDeclarationSyntax ParseTypeDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private MemberDeclarationSyntax ParseTypeDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { // "top-level" expressions and statements should never occur inside an asynchronous context Debug.Assert(!IsInAsync); @@ -1350,7 +1360,7 @@ private void CheckForVersionSpecificModifiers(SyntaxListBuilder modifiers, Synta } } - private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ClassKeyword || this.CurrentToken.Kind == SyntaxKind.StructKeyword || @@ -1932,15 +1942,13 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind } } - var attributes = _pool.Allocate(); var modifiers = _pool.Allocate(); var saveTermState = _termState; try { - this.ParseAttributeDeclarations(attributes); - + var attributes = this.ParseAttributeDeclarations(); if (attributes.Count > 0) { acceptStatement = false; @@ -2238,7 +2246,6 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind finally { _pool.Free(modifiers); - _pool.Free(attributes); _termState = saveTermState; } } @@ -2335,7 +2342,7 @@ public static bool IsComplete(CSharpSyntaxNode node) } private ConstructorDeclarationSyntax ParseConstructorDeclaration( - SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + SyntaxList attributes, SyntaxListBuilder modifiers) { var name = this.ParseIdentifierToken(); var saveTerm = _termState; @@ -2391,13 +2398,13 @@ private ConstructorInitializerSyntax ParseConstructorInitializer() { var openToken = this.EatToken(SyntaxKind.OpenParenToken, reportError); var closeToken = this.EatToken(SyntaxKind.CloseParenToken, reportError); - argumentList = _syntaxFactory.ArgumentList(openToken, default(SeparatedSyntaxList), closeToken); + argumentList = _syntaxFactory.ArgumentList(openToken, default, closeToken); } return _syntaxFactory.ConstructorInitializer(kind, colon, token, argumentList); } - private DestructorDeclarationSyntax ParseDestructorDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private DestructorDeclarationSyntax ParseDestructorDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.TildeToken); var tilde = this.EatToken(SyntaxKind.TildeToken); @@ -2526,7 +2533,7 @@ private bool IsEndOfNameInExplicitInterface() } private MethodDeclarationSyntax ParseMethodDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, TypeSyntax type, ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt, @@ -2623,7 +2630,7 @@ private bool IsEndOfReturnType() } } - private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { SyntaxToken style; if (this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword) @@ -2659,7 +2666,7 @@ private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(S } private OperatorDeclarationSyntax ParseOperatorDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, TypeSyntax type) { @@ -2790,7 +2797,7 @@ private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(S } private IndexerDeclarationSyntax ParseIndexerDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, TypeSyntax type, ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt, @@ -2852,7 +2859,7 @@ private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(S } private PropertyDeclarationSyntax ParsePropertyDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, TypeSyntax type, ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt, @@ -3218,11 +3225,10 @@ private AccessorDeclarationSyntax ParseAccessorDeclaration(bool isEvent) return (AccessorDeclarationSyntax)this.EatNode(); } - var accAttrs = _pool.Allocate(); var accMods = _pool.Allocate(); try { - this.ParseAttributeDeclarations(accAttrs); + var accAttrs = this.ParseAttributeDeclarations(); this.ParseModifiers(accMods, forAccessors: true); // check availability of readonly members feature for accessors @@ -3319,7 +3325,6 @@ private AccessorDeclarationSyntax ParseAccessorDeclaration(bool isEvent) finally { _pool.Free(accMods); - _pool.Free(accAttrs); } } @@ -3474,63 +3479,49 @@ private static bool CanReuseBracketedParameterList(CSharp.Syntax.BracketedParame var saveTerm = _termState; _termState |= TerminatorState.IsEndOfParameterList; - var attributes = _pool.Allocate(); - var modifiers = _pool.Allocate(); - try + if (this.CurrentToken.Kind != closeKind) { - if (this.CurrentToken.Kind != closeKind) - { tryAgain: - if (this.IsPossibleParameter() || this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - // first parameter - attributes.Clear(); - modifiers.Clear(); - var parameter = this.ParseParameter(attributes, modifiers); - nodes.Add(parameter); + if (this.IsPossibleParameter() || this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + // first parameter + var parameter = this.ParseParameter(); + nodes.Add(parameter); - // additional parameters - while (true) + // additional parameters + while (true) + { + if (this.CurrentToken.Kind == closeKind) + { + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleParameter()) { - if (this.CurrentToken.Kind == closeKind) + nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + parameter = this.ParseParameter(); + if (parameter.IsMissing && this.IsPossibleParameter()) { - break; + // ensure we always consume tokens + parameter = AddTrailingSkippedSyntax(parameter, this.EatToken()); } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleParameter()) - { - nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - attributes.Clear(); - modifiers.Clear(); - parameter = this.ParseParameter(attributes, modifiers); - if (parameter.IsMissing && this.IsPossibleParameter()) - { - // ensure we always consume tokens - parameter = AddTrailingSkippedSyntax(parameter, this.EatToken()); - } - nodes.Add(parameter); - continue; - } - else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) - { - break; - } + nodes.Add(parameter); + continue; + } + else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) + { + break; } - } - else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) - { - goto tryAgain; } } - - _termState = saveTerm; - close = this.EatToken(closeKind); - } - finally - { - _pool.Free(modifiers); - _pool.Free(attributes); + else if (this.SkipBadParameterListTokens(ref open, nodes, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) + { + goto tryAgain; + } } + + _termState = saveTerm; + close = this.EatToken(closeKind); } private bool IsEndOfParameterList() @@ -3565,32 +3556,13 @@ private bool IsPossibleParameter() } } - private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter, SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter) { 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) { @@ -3618,52 +3590,59 @@ private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter) return true; } - private ParameterSyntax ParseParameter( - SyntaxListBuilder attributes, - SyntaxListBuilder modifiers) + private ParameterSyntax ParseParameter() { - if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameter(this.CurrentNode as CSharp.Syntax.ParameterSyntax, attributes, modifiers)) + if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameter(this.CurrentNode as CSharp.Syntax.ParameterSyntax)) { return (ParameterSyntax)this.EatNode(); } - this.ParseAttributeDeclarations(attributes); - this.ParseParameterModifiers(modifiers); + var attributes = this.ParseAttributeDeclarations(); - TypeSyntax type; - SyntaxToken name; - if (this.CurrentToken.Kind != SyntaxKind.ArgListKeyword) + var modifiers = _pool.Allocate(); + try { - type = this.ParseType(mode: ParseTypeMode.Parameter); - name = this.ParseIdentifierToken(); + this.ParseParameterModifiers(modifiers); + + TypeSyntax type; + SyntaxToken name; + if (this.CurrentToken.Kind != SyntaxKind.ArgListKeyword) + { + type = this.ParseType(mode: ParseTypeMode.Parameter); + name = this.ParseIdentifierToken(); - // When the user type "int goo[]", give them a useful error - if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind == SyntaxKind.CloseBracketToken) + // When the user type "int goo[]", 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 { - var open = this.EatToken(); - var close = this.EatToken(); - open = this.AddError(open, ErrorCode.ERR_BadArraySyntax); - name = AddTrailingSkippedSyntax(name, SyntaxList.List(open, close)); + // We store an __arglist parameter as a parameter with null type and whose + // .Identifier has the kind ArgListKeyword. + type = null; + name = this.EatToken(SyntaxKind.ArgListKeyword); } - } - else - { - // We store an __arglist parameter as a parameter with null type and whose - // .Identifier has the kind ArgListKeyword. - type = null; - name = this.EatToken(SyntaxKind.ArgListKeyword); - } - EqualsValueClauseSyntax def = null; - if (this.CurrentToken.Kind == SyntaxKind.EqualsToken) + EqualsValueClauseSyntax def = null; + if (this.CurrentToken.Kind == SyntaxKind.EqualsToken) + { + var equals = this.EatToken(SyntaxKind.EqualsToken); + var value = this.ParseExpressionCore(); + def = _syntaxFactory.EqualsValueClause(equals, value: value); + def = CheckFeatureAvailability(def, MessageID.IDS_FeatureOptionalParameter); + } + + return _syntaxFactory.Parameter(attributes, modifiers.ToList(), type, name, def); + } + finally { - var equals = this.EatToken(SyntaxKind.EqualsToken); - var value = this.ParseExpressionCore(); - def = _syntaxFactory.EqualsValueClause(equals, value: value); - def = CheckFeatureAvailability(def, MessageID.IDS_FeatureOptionalParameter); + _pool.Free(modifiers); } - - return _syntaxFactory.Parameter(attributes, modifiers.ToList(), type, name, def); } private static bool IsParameterModifier(SyntaxKind kind) @@ -3721,7 +3700,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers) } private FieldDeclarationSyntax ParseFixedSizeBufferDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxKind parentKind) { @@ -3755,7 +3734,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers) } private MemberDeclarationSyntax ParseEventDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxKind parentKind) { @@ -3775,7 +3754,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers) } private EventDeclarationSyntax ParseEventDeclarationWithAccessors( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxToken eventToken, TypeSyntax type) @@ -3886,7 +3865,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers) } private FieldDeclarationSyntax ParseNormalFieldDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, TypeSyntax type, SyntaxKind parentKind) @@ -3913,7 +3892,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers) } private EventFieldDeclarationSyntax ParseEventFieldDeclaration( - SyntaxListBuilder attributes, + SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxToken eventToken, TypeSyntax type, @@ -4454,7 +4433,7 @@ private bool IsPossibleVariableInitializer() return this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.IsPossibleExpression(); } - private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers, SyntaxKind parentKind) + private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxKind parentKind) { var constToken = this.EatToken(SyntaxKind.ConstKeyword); modifiers.Add(constToken); @@ -4478,7 +4457,7 @@ private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DelegateKeyword); @@ -4512,7 +4491,7 @@ private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxListBuilder attributes, SyntaxListBuilder modifiers) + private EnumDeclarationSyntax ParseEnumDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.EnumKeyword); @@ -4644,35 +4623,27 @@ private EnumMemberDeclarationSyntax ParseEnumMemberDeclaration() return (EnumMemberDeclarationSyntax)this.EatNode(); } - var memberAttrs = _pool.Allocate(); - try + var memberAttrs = this.ParseAttributeDeclarations(); + var memberName = this.ParseIdentifierToken(); + EqualsValueClauseSyntax equalsValue = null; + if (this.CurrentToken.Kind == SyntaxKind.EqualsToken) { - 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) { - var equals = this.EatToken(SyntaxKind.EqualsToken); - ExpressionSyntax value; - if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) - { - //an identifier is a valid expression - value = this.ParseIdentifierName(ErrorCode.ERR_ConstantExpected); - } - else - { - value = this.ParseExpressionCore(); - } - - equalsValue = _syntaxFactory.EqualsValueClause(equals, value: value); + //an identifier is a valid expression + value = this.ParseIdentifierName(ErrorCode.ERR_ConstantExpected); + } + else + { + value = this.ParseExpressionCore(); } - return _syntaxFactory.EnumMemberDeclaration(memberAttrs, modifiers: default, memberName, equalsValue); - } - finally - { - _pool.Free(memberAttrs); + equalsValue = _syntaxFactory.EqualsValueClause(equals, value: value); } + + return _syntaxFactory.EnumMemberDeclaration(memberAttrs, modifiers: default, memberName, equalsValue); } private bool IsPossibleEnumMemberDeclaration() @@ -4878,30 +4849,23 @@ private TypeParameterSyntax ParseTypeParameter() this.AddError(CreateMissingIdentifierToken(), ErrorCode.ERR_IdentifierExpected)); } - var attrs = _pool.Allocate(); - try + var attrs = default(SyntaxList); + if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind != SyntaxKind.CloseBracketToken) { - 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) - { - varianceToken = CheckFeatureAvailability(this.EatToken(), MessageID.IDS_FeatureTypeVariance); - } - - return _syntaxFactory.TypeParameter(attrs, varianceToken, this.ParseIdentifierToken()); + var saveTerm = _termState; + _termState = TerminatorState.IsEndOfTypeArgumentList; + attrs = this.ParseAttributeDeclarations(); + _termState = saveTerm; } - finally + + SyntaxToken varianceToken = null; + if (this.CurrentToken.Kind == SyntaxKind.InKeyword || + this.CurrentToken.Kind == SyntaxKind.OutKeyword) { - _pool.Free(attrs); + varianceToken = CheckFeatureAvailability(this.EatToken(), MessageID.IDS_FeatureTypeVariance); } + + return _syntaxFactory.TypeParameter(attrs, varianceToken, this.ParseIdentifierToken()); } // Parses the parts of the names between Dots and ColonColons. @@ -5274,82 +5238,75 @@ private PostSkipAction SkipBadTypeArgumentListTokens(SeparatedSyntaxListBuilder< // Parses the individual generic parameter/arguments in a name. private TypeSyntax ParseTypeArgument() { - var attrs = _pool.Allocate(); - try + var attrs = default(SyntaxList); + if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind != SyntaxKind.CloseBracketToken) { - 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? + // 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(); + var saveTerm = _termState; + _termState = TerminatorState.IsEndOfTypeArgumentList; + attrs = this.ParseAttributeDeclarations(); + _termState = saveTerm; + } - // Consider the case where someone supplies an invalid type argument - // Such as Action<0> or Action. In this case we generate a missing - // identifier in ParseType, but if we continue as is we'll immediately start to - // interpret 0 as the start of a new expression when we can tell it's most likely - // meant to be part of the type list. - // - // To solve this we check if the current token is not comma or greater than and - // the next token is a comma or greater than. If so we assume that the found - // token is part of this expression and we attempt to recover. This does open - // the door for cases where we have an incomplete line to be interpretted as - // a single expression. For example: - // - // Action< // Incomplete line - // a>b; - // - // However, this only happens when the following expression is of the form a>... - // or a,... which means this case should happen less frequently than what we're - // trying to solve here so we err on the side of better error messages - // for the majority of cases. - SyntaxKind nextTokenKind = SyntaxKind.None; + 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); + } - if (result.IsMissing && - (this.CurrentToken.Kind != SyntaxKind.CommaToken && this.CurrentToken.Kind != SyntaxKind.GreaterThanToken) && - ((nextTokenKind = this.PeekToken(1).Kind) == SyntaxKind.CommaToken || nextTokenKind == SyntaxKind.GreaterThanToken)) - { - // Eat the current token and add it as skipped so we recover - result = AddTrailingSkippedSyntax(result, this.EatToken()); - } + var result = this.ParseType(); - if (varianceToken != null) - { - result = AddLeadingSkippedSyntax(result, varianceToken); - } + // Consider the case where someone supplies an invalid type argument + // Such as Action<0> or Action. In this case we generate a missing + // identifier in ParseType, but if we continue as is we'll immediately start to + // interpret 0 as the start of a new expression when we can tell it's most likely + // meant to be part of the type list. + // + // To solve this we check if the current token is not comma or greater than and + // the next token is a comma or greater than. If so we assume that the found + // token is part of this expression and we attempt to recover. This does open + // the door for cases where we have an incomplete line to be interpretted as + // a single expression. For example: + // + // Action< // Incomplete line + // a>b; + // + // However, this only happens when the following expression is of the form a>... + // or a,... which means this case should happen less frequently than what we're + // trying to solve here so we err on the side of better error messages + // for the majority of cases. + SyntaxKind nextTokenKind = SyntaxKind.None; - if (attrs.Count > 0) - { - result = AddLeadingSkippedSyntax(result, attrs.ToListNode()); - result = this.AddError(result, ErrorCode.ERR_TypeExpected); - } + if (result.IsMissing && + (this.CurrentToken.Kind != SyntaxKind.CommaToken && this.CurrentToken.Kind != SyntaxKind.GreaterThanToken) && + ((nextTokenKind = this.PeekToken(1).Kind) == SyntaxKind.CommaToken || nextTokenKind == SyntaxKind.GreaterThanToken)) + { + // Eat the current token and add it as skipped so we recover + result = AddTrailingSkippedSyntax(result, this.EatToken()); + } - return result; + if (varianceToken != null) + { + result = AddLeadingSkippedSyntax(result, varianceToken); } - finally + + if (attrs.Count > 0) { - _pool.Free(attrs); + result = AddLeadingSkippedSyntax(result, attrs.Node); + result = this.AddError(result, ErrorCode.ERR_TypeExpected); } + + return result; } private bool IsEndOfTypeArgumentList()