/* * Copyright 2013-2017 consulo.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package consulo.csharp.lang.parser.exp; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.csharp.lang.parser.CSharpBuilderWrapper; import consulo.csharp.lang.parser.ModifierSet; import consulo.csharp.lang.parser.SharedParsingHelpers; import consulo.csharp.lang.parser.decl.MethodParsing; import consulo.csharp.lang.parser.stmt.StatementParsing; import consulo.csharp.lang.psi.CSharpElements; import consulo.csharp.lang.psi.CSharpSoftTokens; import consulo.csharp.lang.psi.CSharpStubElements; import consulo.csharp.lang.psi.CSharpTokens; import consulo.csharp.module.extension.CSharpLanguageVersion; import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.BitUtil; public class ExpressionParsing extends SharedParsingHelpers { private enum ExprType { CONDITIONAL_OR, CONDITIONAL_AND, OR, XOR, AND, EQUALITY, RELATIONAL, SHIFT, ADDITIVE, MULTIPLICATIVE, UNARY, MAYBE_NULLABLE_TYPE } private static final TokenSet CONDITIONAL_OR_OPS = TokenSet.create(OROR); private static final TokenSet CONDITIONAL_AND_OPS = TokenSet.create(ANDAND); private static final TokenSet OR_OPS = TokenSet.create(OR); private static final TokenSet XOR_OPS = TokenSet.create(XOR); private static final TokenSet AND_OPS = TokenSet.create(AND); private static final TokenSet EQUALITY_OPS = TokenSet.create(EQEQ, NTEQ); private static final TokenSet RELATIONAL_OPS = TokenSet.create(LT, GT, LTEQ, GTEQ); private static final TokenSet SHIFT_OPS = TokenSet.create(LTLT, GTGT); private static final TokenSet ADDITIVE_OPS = TokenSet.create(PLUS, MINUS); private static final TokenSet MULTIPLICATIVE_OPS = TokenSet.create(MUL, DIV, PERC); private static final TokenSet POSTFIX_OPS = TokenSet.create(PLUSPLUS, MINUSMINUS); private static final TokenSet PREF_ARITHMETIC_OPS = TokenSet.orSet(POSTFIX_OPS, TokenSet.create(PLUS, MINUS, MUL, AND)); private static final TokenSet PREFIX_OPS = TokenSet.orSet(PREF_ARITHMETIC_OPS, TokenSet.create(TILDE, EXCL)); private static final TokenSet ID_OR_SUPER = TokenSet.create(CSharpTokens.IDENTIFIER, BASE_KEYWORD); private static final TokenSet THIS_OR_BASE = TokenSet.create(THIS_KEYWORD, BASE_KEYWORD); @Nullable public static PsiBuilder.Marker parseVariableInitializer(@NotNull CSharpBuilderWrapper builder, ModifierSet set) { IElementType tokenType = builder.getTokenType(); if(tokenType == LBRACE) { return parseArrayInitializer(builder, IMPLICIT_ARRAY_INITIALIZATION_EXPRESSION, set); } else { return parse(builder, set); } } @Nullable public static PsiBuilder.Marker parse(final CSharpBuilderWrapper builder, ModifierSet set) { return parseAssignment(builder, set); } @Nullable private static PsiBuilder.Marker parseAssignment(final CSharpBuilderWrapper builder, ModifierSet set) { final PsiBuilder.Marker left = parseConditional(builder, set); if(left == null) { return null; } final IElementType tokenType = builder.getTokenTypeGGLL(); if(ASSIGNMENT_OPERATORS.contains(tokenType) && tokenType != null) { final PsiBuilder.Marker assignment = left.precede(); doneOneElementGGLL(builder, tokenType, OPERATOR_REFERENCE, null); final PsiBuilder.Marker right = parse(builder, set); if(right == null) { builder.error("Expression expected"); } assignment.done(ASSIGNMENT_EXPRESSION); return assignment; } return left; } @Nullable private static PsiBuilder.Marker parseConditional(final CSharpBuilderWrapper builder, ModifierSet set) { final PsiBuilder.Marker condition = parseExpression(builder, ExprType.CONDITIONAL_OR, set); if(condition == null) { return null; } if(builder.getTokenType() == QUESTQUEST) { final PsiBuilder.Marker nullCoalescing = condition.precede(); builder.advanceLexer(); final PsiBuilder.Marker ifNullPart = parse(builder, set); if(ifNullPart == null) { builder.error("Expression expected"); } nullCoalescing.done(NULL_COALESCING_EXPRESSION); return nullCoalescing; } else if(builder.getTokenType() == QUEST) { final PsiBuilder.Marker ternary = condition.precede(); builder.advanceLexer(); final PsiBuilder.Marker truePart = parse(builder, set); if(truePart == null) { builder.error("Expression expected"); ternary.done(CONDITIONAL_EXPRESSION); return ternary; } if(builder.getTokenType() != COLON) { builder.error("Expected colon"); ternary.done(CONDITIONAL_EXPRESSION); return ternary; } builder.advanceLexer(); final PsiBuilder.Marker falsePart = parseConditional(builder, set); if(falsePart == null) { builder.error("Expression expected"); ternary.done(CONDITIONAL_EXPRESSION); return ternary; } ternary.done(CONDITIONAL_EXPRESSION); return ternary; } { return condition; } } @Nullable private static PsiBuilder.Marker parseExpression(final CSharpBuilderWrapper builder, final ExprType type, ModifierSet set) { switch(type) { case CONDITIONAL_OR: return parseBinary(builder, ExprType.CONDITIONAL_AND, CONDITIONAL_OR_OPS, set); case CONDITIONAL_AND: return parseBinary(builder, ExprType.OR, CONDITIONAL_AND_OPS, set); case OR: return parseBinary(builder, ExprType.XOR, OR_OPS, set); case XOR: return parseBinary(builder, ExprType.AND, XOR_OPS, set); case AND: return parseBinary(builder, ExprType.EQUALITY, AND_OPS, set); case EQUALITY: return parseBinary(builder, ExprType.RELATIONAL, EQUALITY_OPS, set); case RELATIONAL: return parseRelational(builder, set); case SHIFT: return parseBinary(builder, ExprType.ADDITIVE, SHIFT_OPS, set); case ADDITIVE: return parseBinary(builder, ExprType.MULTIPLICATIVE, ADDITIVE_OPS, set); case MULTIPLICATIVE: return parseBinary(builder, ExprType.UNARY, MULTIPLICATIVE_OPS, set); case UNARY: return parseUnary(builder, set); case MAYBE_NULLABLE_TYPE: TypeInfo typeInfo = parseType(builder, NONE); if(typeInfo == null) { return null; } // if we have nullable type - need find colon, or return original marker if(typeInfo.isNullable) { if(builder.getTokenType() == QUEST) { return typeInfo.marker; } // o is int? "true" : "false" PsiBuilder.Marker marker = parseConditional(builder, set); if(marker != null) { IElementType tokenType = builder.getTokenType(); marker.rollbackTo(); if(tokenType == COLON) { typeInfo.marker.rollbackTo(); TypeInfo anotherTypeInfo = parseType(builder, WITHOUT_NULLABLE); assert anotherTypeInfo != null; return anotherTypeInfo.marker; } } return typeInfo.marker; } else { return typeInfo.marker; } default: assert false : "Unexpected type: " + type; return null; } } @Nullable private static PsiBuilder.Marker parseUnary(final CSharpBuilderWrapper builder, ModifierSet set) { final IElementType tokenType = builder.getTokenType(); if(PREFIX_OPS.contains(tokenType)) { final PsiBuilder.Marker unary = builder.mark(); doneOneElementGGLL(builder, tokenType, OPERATOR_REFERENCE, null); final PsiBuilder.Marker operand = parseUnary(builder, set); if(operand == null) { builder.error("Expression expected"); } unary.done(PREFIX_EXPRESSION); return unary; } else if(tokenType == LPAR) { final PsiBuilder.Marker typeCast = builder.mark(); builder.advanceLexer(); TypeInfo typeInfo = parseType(builder, LT_GT_HARD_REQUIRE); if(typeInfo == null || !expect(builder, RPAR, null)) { typeCast.rollbackTo(); return parsePostfix(builder, set); } if(PREF_ARITHMETIC_OPS.contains(builder.getTokenType()) && typeInfo.nativeElementType == null) { typeCast.rollbackTo(); return parsePostfix(builder, set); } final PsiBuilder.Marker expr = parseUnary(builder, set); if(expr == null) { if(!typeInfo.isParameterized) { // cannot parse correct parenthesized expression after correct parameterized type typeCast.rollbackTo(); return parsePostfix(builder, set); } else { builder.error("Expression expected"); } } typeCast.done(TYPE_CAST_EXPRESSION); return typeCast; } else { return parsePostfix(builder, set); } } @Nullable private static PsiBuilder.Marker parsePostfix(final CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker operand = parsePrimary(builder, set); if(operand == null) { return null; } while(POSTFIX_OPS.contains(builder.getTokenType())) { final PsiBuilder.Marker postfix = operand.precede(); doneOneElementGGLL(builder, builder.getTokenType(), OPERATOR_REFERENCE, null); postfix.done(POSTFIX_EXPRESSION); operand = postfix; } return operand; } @Nullable private static PsiBuilder.Marker parseBinary(final CSharpBuilderWrapper builder, final ExprType type, final TokenSet ops, ModifierSet set) { PsiBuilder.Marker result = parseExpression(builder, type, set); if(result == null) { return null; } IElementType tokenType = builder.getTokenTypeGGLL(); IElementType currentExprTokenType = tokenType; while(true) { if(tokenType == null || !ops.contains(tokenType)) { break; } doneOneElementGGLL(builder, tokenType, OPERATOR_REFERENCE, null); final PsiBuilder.Marker right = parseExpression(builder, type, set); tokenType = builder.getTokenType(); if(tokenType != null && ops.contains(tokenType) || tokenType == null || !ops.contains(tokenType) || tokenType != currentExprTokenType || right == null) { // save result = result.precede(); if(right == null) { builder.error("Expression expected"); } result.done(BINARY_EXPRESSION); if(right == null) { break; } currentExprTokenType = tokenType; } } return result; } @Nullable private static PsiBuilder.Marker parseRelational(final CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker left = parseExpression(builder, ExprType.SHIFT, set); if(left == null) { return null; } IElementType tokenType; while((tokenType = builder.getTokenTypeGGLL()) != null) { final IElementType toCreate; final ExprType toParse; boolean operatorReference = false; if(RELATIONAL_OPS.contains(tokenType)) { toCreate = BINARY_EXPRESSION; toParse = ExprType.SHIFT; operatorReference = true; } else if(tokenType == IS_KEYWORD) { toCreate = IS_EXPRESSION; toParse = ExprType.MAYBE_NULLABLE_TYPE; } else if(tokenType == AS_KEYWORD) { toCreate = AS_EXPRESSION; toParse = ExprType.MAYBE_NULLABLE_TYPE; } else { break; } final PsiBuilder.Marker expression = left.precede(); if(operatorReference) { doneOneElementGGLL(builder, builder.getTokenTypeGGLL(), OPERATOR_REFERENCE, null); } else { builder.advanceLexerGGLL(); } final PsiBuilder.Marker right = parseExpression(builder, toParse, set); if(right == null) { builder.error(toParse == ExprType.MAYBE_NULLABLE_TYPE ? "Type expected" : "Expression expected"); expression.done(toCreate); return expression; } expression.done(toCreate); left = expression; } return left; } @Nullable private static PsiBuilder.Marker parsePrimary(final CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker startMarker = builder.mark(); PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder, set); if(expr == null) { startMarker.drop(); return null; } while(true) { final IElementType tokenType = builder.getTokenType(); if(tokenType == DOT || tokenType == NULLABE_CALL) { final PsiBuilder.Marker dotPos = builder.mark(); builder.advanceLexer(); IElementType dotTokenType = builder.getTokenType(); if(dotTokenType == NEW_KEYWORD) { dotPos.drop(); expr = parseNewExpression(builder, expr, set); } else if(dotTokenType == STACKALLOC_KEYWORD) { dotPos.drop(); expr = parseStackAllocExpression(builder, expr, set); } else if(dotTokenType == BASE_KEYWORD) { dotPos.drop(); final PsiBuilder.Marker refExpr = expr.precede(); builder.advanceLexer(); refExpr.done(REFERENCE_EXPRESSION); expr = refExpr; } else { dotPos.drop(); final PsiBuilder.Marker refExpr = expr.precede(); if(!expect(builder, ID_OR_SUPER, "Expected identifier")) { refExpr.done(REFERENCE_EXPRESSION); startMarker.drop(); return refExpr; } parseReferenceTypeArgumentList(builder, NONE); refExpr.done(REFERENCE_EXPRESSION); expr = refExpr; } } else if(tokenType == ARROW) { builder.advanceLexer(); final PsiBuilder.Marker refExpr = expr.precede(); if(!expect(builder, CSharpTokens.IDENTIFIER, "Expected identifier")) { refExpr.done(REFERENCE_EXPRESSION); startMarker.drop(); return refExpr; } refExpr.done(REFERENCE_EXPRESSION); expr = refExpr; } else if(tokenType == COLONCOLON) { builder.advanceLexer(); final PsiBuilder.Marker refExpr = expr.precede(); if(!expect(builder, CSharpTokens.IDENTIFIER, "Expected identifier")) { refExpr.done(REFERENCE_EXPRESSION); startMarker.drop(); return refExpr; } parseReferenceTypeArgumentList(builder, NONE); refExpr.done(REFERENCE_EXPRESSION); expr = refExpr; } else if(tokenType == LPAR) { IElementType expType = exprType(expr); if(expType != REFERENCE_EXPRESSION && expType != INDEX_ACCESS_EXPRESSION && expType != PARENTHESES_EXPRESSION && expType != METHOD_CALL_EXPRESSION) { startMarker.drop(); return expr; } final PsiBuilder.Marker callExpr = expr.precede(); parseArgumentList(builder, false, set); callExpr.done(METHOD_CALL_EXPRESSION); expr = callExpr; } else if(tokenType == QUEST && builder.lookAhead(1) == LBRACKET || tokenType == LBRACKET) { final PsiBuilder.Marker arrayAccess = expr.precede(); if(tokenType == QUEST) { builder.advanceLexer(); } PsiBuilder.Marker argumentListMarker = builder.mark(); builder.advanceLexer(); parseArguments(builder, RBRACKET, false, set); if(builder.getTokenType() != RBRACKET) { builder.error("']' expected"); argumentListMarker.done(CALL_ARGUMENT_LIST); arrayAccess.done(INDEX_ACCESS_EXPRESSION); startMarker.drop(); return arrayAccess; } builder.advanceLexer(); argumentListMarker.done(CALL_ARGUMENT_LIST); arrayAccess.done(INDEX_ACCESS_EXPRESSION); expr = arrayAccess; } else { startMarker.drop(); return expr; } } } public static void parseArgumentList(CSharpBuilderWrapper builder, boolean fieldSet, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); if(builder.getTokenType() != LPAR) { mark.done(CALL_ARGUMENT_LIST); return; } builder.advanceLexer(); if(builder.getTokenType() == RPAR) { builder.advanceLexer(); mark.done(CALL_ARGUMENT_LIST); return; } parseArguments(builder, RPAR, fieldSet, set); expect(builder, RPAR, "')' expected"); mark.done(CALL_ARGUMENT_LIST); } private static void parseArguments(CSharpBuilderWrapper builder, IElementType stopElement, boolean fieldSet, ModifierSet set) { TokenSet stoppers = TokenSet.create(stopElement, CSharpTokens.RBRACE, CSharpTokens.SEMICOLON); boolean commaEntered = false; while(!builder.eof()) { if(stoppers.contains(builder.getTokenType())) { if(commaEntered) { PsiBuilder.Marker mark = builder.mark(); emptyElement(builder, ERROR_EXPRESSION); // call(test,) builder.error("Expression expected"); mark.done(CALL_ARGUMENT); } break; } commaEntered = false; if(builder.getTokenType() == CSharpTokens.IDENTIFIER && builder.lookAhead(1) == COLON) { PsiBuilder.Marker marker = builder.mark(); doneOneElement(builder, CSharpTokens.IDENTIFIER, REFERENCE_EXPRESSION, null); builder.advanceLexer(); // eq PsiBuilder.Marker expressionParser = parse(builder, set); if(expressionParser == null) { builder.error("Expression expected"); } marker.done(NAMED_CALL_ARGUMENT); } else if(fieldSet && builder.getTokenType() == CSharpTokens.IDENTIFIER && builder.lookAhead(1) == EQ) { PsiBuilder.Marker marker = builder.mark(); doneOneElement(builder, CSharpTokens.IDENTIFIER, REFERENCE_EXPRESSION, null); builder.advanceLexer(); // eq PsiBuilder.Marker expressionParser = parse(builder, set); if(expressionParser == null) { builder.error("Expression expected"); } marker.done(NAMED_FIELD_OR_PROPERTY_SET); } else { PsiBuilder.Marker argumentMarker = builder.mark(); PsiBuilder.Marker marker = parse(builder, set); if(marker == null) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); builder.error("Expression expected"); errorMarker.done(ERROR_EXPRESSION); } argumentMarker.done(CALL_ARGUMENT); } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); commaEntered = true; } else if(!stoppers.contains(builder.getTokenType())) { builder.error("',' expected"); } } } @Nullable private static PsiBuilder.Marker parsePrimaryExpressionStart(final CSharpBuilderWrapper builder, ModifierSet modifierSet) { CSharpLanguageVersion version = builder.getVersion(); builder.enableSoftKeyword(CSharpSoftTokens.GLOBAL_KEYWORD); boolean linqState = false; if(version.isAtLeast(CSharpLanguageVersion._3_0)) { linqState = builder.enableSoftKeyword(CSharpSoftTokens.FROM_KEYWORD); } if(version.isAtLeast(CSharpLanguageVersion._4_0)) { builder.enableSoftKeyword(CSharpSoftTokens.ASYNC_KEYWORD); } if(modifierSet.contains(CSharpSoftTokens.ASYNC_KEYWORD)) { builder.enableSoftKeyword(CSharpSoftTokens.AWAIT_KEYWORD); } if(version.isAtLeast(CSharpLanguageVersion._6_0)) { builder.enableSoftKeyword(CSharpSoftTokens.NAMEOF_KEYWORD); } IElementType tokenType = builder.getTokenType(); if(linqState) { builder.disableSoftKeyword(CSharpSoftTokens.FROM_KEYWORD); } if(version.isAtLeast(CSharpLanguageVersion._4_0)) { builder.disableSoftKeyword(CSharpSoftTokens.ASYNC_KEYWORD); } if(modifierSet.contains(CSharpSoftTokens.ASYNC_KEYWORD)) { builder.disableSoftKeyword(CSharpSoftTokens.AWAIT_KEYWORD); } if(version.isAtLeast(CSharpLanguageVersion._6_0)) { builder.disableSoftKeyword(CSharpSoftTokens.NAMEOF_KEYWORD); } builder.disableSoftKeyword(CSharpSoftTokens.GLOBAL_KEYWORD); // if not coloncolon drop if(tokenType == CSharpSoftTokens.GLOBAL_KEYWORD && builder.lookAhead(1) != CSharpTokens.COLONCOLON) { builder.remapBackIfSoft(); tokenType = builder.getTokenType(); } if(tokenType == CSharpSoftTokens.ASYNC_KEYWORD) { PsiBuilder.Marker tempMarker = builder.mark(); builder.advanceLexer(); boolean isLambdaNext = builder.getTokenType() == CSharpTokens.DELEGATE_KEYWORD || parseLambdaExpression(builder, null, modifierSet.add(CSharpSoftTokens.ASYNC_KEYWORD)) != null; tempMarker.rollbackTo(); if(!isLambdaNext) { builder.remapBackIfSoft(); tokenType = builder.getTokenType(); } } if(LITERALS.contains(tokenType)) { final PsiBuilder.Marker literal = builder.mark(); builder.advanceLexer(); literal.done(CONSTANT_EXPRESSION); return literal; } if(tokenType == NEW_KEYWORD) { return parseNewExpression(builder, null, modifierSet); } if(tokenType == STACKALLOC_KEYWORD) { return parseStackAllocExpression(builder, null, modifierSet); } if(tokenType == TYPEOF_KEYWORD) { return parseExpressionWithTypeInLParRPar(builder, ALLOW_EMPTY_TYPE_ARGUMENTS, null, TYPE_OF_EXPRESSION); } if(tokenType == NAMEOF_KEYWORD) { return parseExpressionWithExpressionInLParRPar(builder, null, NAMEOF_EXPRESSION, modifierSet); } if(tokenType == DEFAULT_KEYWORD) { return parseExpressionWithTypeInLParRPar(builder, NONE, null, DEFAULT_EXPRESSION); } if(tokenType == SIZEOF_KEYWORD) { return parseExpressionWithTypeInLParRPar(builder, NONE, null, SIZE_OF_EXPRESSION); } if(tokenType == CHECKED_KEYWORD || tokenType == UNCHECKED_KEYWORD) { return parseExpressionWithExpressionInLParRPar(builder, null, CHECKED_EXPRESSION, modifierSet); } if(tokenType == __MAKEREF_KEYWORD) { return parseExpressionWithExpressionInLParRPar(builder, null, __MAKEREF_EXPRESSION, modifierSet); } if(tokenType == __ARGLIST_KEYWORD) { return parseArglistExpression(builder, modifierSet); } if(tokenType == __REFTYPE_KEYWORD) { return parseExpressionWithExpressionInLParRPar(builder, null, __REFTYPE_EXPRESSION, modifierSet); } if(tokenType == __REFVALUE_KEYWORD) { return parseRefValueExpression(builder, modifierSet); } if(tokenType == DELEGATE_KEYWORD) { return parseDelegateExpression(builder, modifierSet.remove(CSharpSoftTokens.ASYNC_KEYWORD)); } if(tokenType == REF_KEYWORD || tokenType == OUT_KEYWORD) { return parseOutRefWrapExpression(builder, modifierSet); } if(tokenType == AWAIT_KEYWORD) { return parseAwaitExpression(builder, modifierSet); } if(tokenType == ASYNC_KEYWORD) { if(builder.lookAhead(1) == CSharpTokens.DELEGATE_KEYWORD) { return parseDelegateExpression(builder, modifierSet.add(CSharpSoftTokens.ASYNC_KEYWORD)); } return parseLambdaExpression(builder, null, modifierSet.add(CSharpSoftTokens.ASYNC_KEYWORD)); } if(tokenType == LPAR) { final PsiBuilder.Marker lambda = parseLambdaAfterParenth(builder, null, modifierSet.remove(CSharpSoftTokens.ASYNC_KEYWORD)); if(lambda != null) { return lambda; } PsiBuilder.Marker tuple = parseTupleExpressionAfterLPar(builder, modifierSet); if(tuple != null) { return tuple; } final PsiBuilder.Marker parenth = builder.mark(); builder.advanceLexer(); final PsiBuilder.Marker inner = parse(builder, modifierSet); if(inner == null) { builder.error("Expression expected"); } if(!expect(builder, RPAR, null)) { if(inner != null) { builder.error("')' expected"); } } parenth.done(PARENTHESES_EXPRESSION); return parenth; } if(tokenType == FROM_KEYWORD) { PsiBuilder.Marker marker = LinqParsing.parseLinqExpression(builder, modifierSet); if(marker == null) { assert builder.getTokenType() == FROM_KEYWORD : builder.getTokenText() + ":" + builder.getTokenType(); builder.remapBackIfSoft(); tokenType = builder.getTokenType(); } else { return marker; } } if(tokenType == GLOBAL_KEYWORD) { PsiBuilder.Marker refExpr = builder.mark(); builder.advanceLexer(); refExpr.done(REFERENCE_EXPRESSION); return refExpr; } if(tokenType == CSharpTokens.IDENTIFIER) { if(builder.lookAhead(1) == DARROW) { return parseLambdaExpression(builder, null, modifierSet.remove(CSharpSoftTokens.ASYNC_KEYWORD)); } PsiBuilder.Marker refExpr = builder.mark(); builder.advanceLexer(); parseReferenceTypeArgumentList(builder, NONE); refExpr.done(REFERENCE_EXPRESSION); return refExpr; } if(NATIVE_TYPES.contains(tokenType)) { if(tokenType == CSharpTokens.VOID_KEYWORD) { return null; } PsiBuilder.Marker refExpr = builder.mark(); builder.advanceLexer(); refExpr.done(REFERENCE_EXPRESSION); return refExpr; } if(THIS_OR_BASE.contains(tokenType)) { PsiBuilder.Marker expr = builder.mark(); builder.advanceLexer(); expr.done(REFERENCE_EXPRESSION); return expr; } return null; } private static PsiBuilder.Marker parseTupleExpressionAfterLPar(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker marker = builder.mark(); builder.advanceLexer(); // expression like '(name: test)' if(builder.getTokenType() == CSharpTokens.IDENTIFIER && builder.lookAhead(1) == CSharpTokens.COLON) { marker.rollbackTo(); return parseTupleExpression(builder, set); } else { PsiBuilder.Marker expression = parse(builder, set); if(expression == null || builder.getTokenType() != CSharpTokens.COMMA) { marker.rollbackTo(); return null; } marker.rollbackTo(); return parseTupleExpression(builder, set); } } private static PsiBuilder.Marker parseTupleExpression(CSharpBuilderWrapper builder, ModifierSet set) { if(builder.getTokenType() == LPAR) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); while(true) { parseTupleElement(builder, set); if(builder.getTokenType() != COMMA) { break; } else { builder.advanceLexer(); } } expect(builder, RPAR, "')' expected"); mark.done(TUPLE_EXPRESSION); return mark; } return null; } private static boolean parseTupleElement(CSharpBuilderWrapper builder, ModifierSet set) { if(builder.getTokenType() == CSharpTokens.COMMA || builder.getTokenType() == CSharpTokens.RPAR) { return false; } boolean valid = true; PsiBuilder.Marker mark = builder.mark(); if(builder.getTokenType() == CSharpTokens.IDENTIFIER && builder.lookAhead(1) == CSharpTokens.COLON) { doneIdentifier(builder, NONE); builder.advanceLexer(); // colon if(parse(builder, set) == null) { builder.error("Expression expected"); valid = false; } } else { if(parse(builder, set) == null) { builder.error("Expression expected"); valid = false; } } mark.done(CSharpElements.TUPLE_ELEMENT); return valid; } private static PsiBuilder.Marker parseArglistExpression(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(builder.getTokenType() == LPAR) { parseArgumentList(builder, false, set); } mark.done(__ARGLIST_EXPRESSION); return mark; } private static PsiBuilder.Marker parseRefValueExpression(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(expect(builder, LPAR, "'(' expected")) { if(parse(builder, set) == null) { builder.error("Expression expected"); } expect(builder, COMMA, "',' expected"); if(parseType(builder) == null) { builder.error("Type expected"); } expect(builder, RPAR, "')' expected"); } mark.done(__REFVALUE_EXPRESSION); return mark; } private static PsiBuilder.Marker parseOutRefWrapExpression(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(parse(builder, set) == null) { builder.error("Expression expected"); } mark.done(OUT_REF_WRAP_EXPRESSION); return mark; } private static PsiBuilder.Marker parseAwaitExpression(@NotNull CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker marker = builder.mark(); builder.advanceLexer(); if(parse(builder, set) == null) { builder.error("Expression expected"); } marker.done(AWAIT_EXPRESSION); return marker; } private static PsiBuilder.Marker parseDelegateExpression(@NotNull CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker marker = builder.mark(); if(builder.getTokenType() == CSharpSoftTokens.ASYNC_KEYWORD) { builder.advanceLexer(); } builder.advanceLexer(); if(builder.getTokenType() == LPAR) { MethodParsing.parseParameterList(builder, NONE, RPAR, set); } if(builder.getTokenType() == LBRACE) { StatementParsing.parse(builder, set); } else { builder.error("'{' expected"); } marker.done(DELEGATE_EXPRESSION); return marker; } @Nullable private static PsiBuilder.Marker parseLambdaAfterParenth(final CSharpBuilderWrapper builder, @Nullable final PsiBuilder.Marker typeList, ModifierSet set) { final boolean isLambda; final IElementType nextToken1 = builder.lookAhead(1); final IElementType nextToken2 = builder.lookAhead(2); if(nextToken1 == RPAR && nextToken2 == DARROW) { isLambda = true; } else { if(nextToken2 == COMMA || nextToken2 == RPAR && builder.lookAhead(3) == DARROW) { isLambda = true; } else if(nextToken2 == DARROW) { isLambda = false; } else { boolean arrow = false; final PsiBuilder.Marker marker = builder.mark(); while(!builder.eof()) { builder.advanceLexer(); final IElementType tokenType = builder.getTokenType(); if(tokenType == DARROW) { arrow = true; break; } if(tokenType == RPAR) { arrow = builder.lookAhead(1) == DARROW; break; } else if(tokenType == LPAR || tokenType == SEMICOLON || tokenType == LBRACE || tokenType == RBRACE) { break; } } marker.rollbackTo(); isLambda = arrow; } } return isLambda ? parseLambdaExpression(builder, typeList, set) : null; } @Nullable private static PsiBuilder.Marker parseLambdaExpression(final CSharpBuilderWrapper builder, @Nullable final PsiBuilder.Marker typeList, ModifierSet set) { PsiBuilder.Marker start = typeList != null ? typeList.precede() : builder.mark(); if(builder.getTokenType() == ASYNC_KEYWORD) { builder.advanceLexer(); } parseLambdaParameterList(builder); if(!expect(builder, DARROW, null)) { start.rollbackTo(); return null; } final PsiBuilder.Marker body; if(builder.getTokenType() == LBRACE) { body = StatementParsing.parse(builder, set); } else { body = parse(builder, set); } if(body == null) { builder.error("'}' expected"); } start.done(LAMBDA_EXPRESSION); return start; } private static void parseLambdaParameterList(final CSharpBuilderWrapper builder) { PsiBuilder.Marker mark = builder.mark(); boolean lpar = expect(builder, LPAR, null); if(!lpar) { parseLambdaParameter(builder, true); } else { if(builder.getTokenType() != CSharpTokens.RPAR) { while(!builder.eof()) { parseLambdaParameter(builder, false); if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else if(builder.getTokenType() == CSharpTokens.RPAR) { break; } else { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("Expected comma"); } } } expect(builder, RPAR, "')' expected"); } mark.done(LAMBDA_PARAMETER_LIST); } private static void parseLambdaParameter(CSharpBuilderWrapper builder, boolean single) { PsiBuilder.Marker mark = builder.mark(); if(single) { expectOrReportIdentifier(builder, 0); mark.done(CSharpElements.LAMBDA_PARAMETER); } else { boolean wasIdentifier = builder.getTokenType() == CSharpTokens.IDENTIFIER; TypeInfo typeInfo = parseType(builder); if(typeInfo != null) { if(builder.getTokenType() == CSharpTokens.IDENTIFIER) { expectOrReportIdentifier(builder, 0); mark.done(LAMBDA_PARAMETER); } else if(wasIdentifier) { typeInfo.marker.rollbackTo(); expectOrReportIdentifier(builder, 0); mark.done(LAMBDA_PARAMETER); } else { mark.drop(); reportIdentifier(builder, 0); } } else { mark.error("Identifier expected"); } } } public static void parseConstructorSuperCall(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker marker = builder.mark(); if(THIS_OR_BASE.contains(builder.getTokenType())) { doneOneElement(builder, builder.getTokenType(), REFERENCE_EXPRESSION, null); if(builder.getTokenType() == LPAR) { parseArgumentList(builder, false, set); } else { builder.error("'(' expected"); } marker.done(CONSTRUCTOR_SUPER_CALL_EXPRESSION); } else { builder.error("Expected 'base' or 'this'"); marker.drop(); } } private static enum AfterNewParsingTarget { NONE, ANONYM_PROPERTY_SET_LIST, PROPERTY_SET_LIST, ARRAY_INITIALZER, DICTIONARY_INITIALZER } private static PsiBuilder.Marker parseNewExpression(CSharpBuilderWrapper builder, PsiBuilder.Marker mark, ModifierSet set) { PsiBuilder.Marker newExpr = (mark != null ? mark.precede() : builder.mark()); builder.advanceLexer(); TypeInfo typeInfo = parseType(builder, BRACKET_RETURN_BEFORE); boolean forceArray = false; while(parseArrayLength(builder, set)) { forceArray = true; // we can eat only one [] if not type if(typeInfo == null) { break; } } boolean argumentsPassed = false; if(builder.getTokenType() == LPAR) { parseArgumentList(builder, false, set); argumentsPassed = true; } AfterNewParsingTarget target = getTarget(builder, forceArray, typeInfo); switch(target) { case NONE: if(!argumentsPassed && !forceArray) { builder.error("'(' expected"); } break; case PROPERTY_SET_LIST: parseNamedFieldOrPropertySetBlock(builder, set); break; case ANONYM_PROPERTY_SET_LIST: parseAnonymFieldOrPropertySetBlock(builder, set); break; case DICTIONARY_INITIALZER: parseDictionaryInitializerList(builder, set); break; case ARRAY_INITIALZER: parseArrayInitializer(builder, ARRAY_INITIALIZER, set); break; } newExpr.done(NEW_EXPRESSION); return newExpr; } private static boolean parseArrayLength(@NotNull CSharpBuilderWrapper builder, ModifierSet set) { if(builder.getTokenType() == LBRACKET) { PsiBuilder.Marker arrayMarker = builder.mark(); builder.advanceLexer(); while(true) { parse(builder, set); if(builder.getTokenType() != COMMA) { break; } else { builder.advanceLexer(); } } expect(builder, RBRACKET, "']' expected"); arrayMarker.done(NEW_ARRAY_LENGTH); return true; } return false; } private static PsiBuilder.Marker parseStackAllocExpression(CSharpBuilderWrapper builder, PsiBuilder.Marker mark, ModifierSet set) { PsiBuilder.Marker newExpr = (mark != null ? mark.precede() : builder.mark()); builder.advanceLexer(); TypeInfo typeMarker = parseType(builder, BRACKET_RETURN_BEFORE); if(typeMarker == null) { builder.error("Expected type"); } while(builder.getTokenType() == LBRACKET) { PsiBuilder.Marker arrayMarker = builder.mark(); builder.advanceLexer(); while(true) { parse(builder, set); if(builder.getTokenType() != COMMA) { break; } else { builder.advanceLexer(); } } expect(builder, RBRACKET, "']' expected"); arrayMarker.done(NEW_ARRAY_LENGTH); } newExpr.done(STACKALLOC_EXPRESSION); return newExpr; } @NotNull private static AfterNewParsingTarget getTarget(CSharpBuilderWrapper builderWrapper, boolean forceArray, TypeInfo typeInfo) { if(typeInfo != null && typeInfo.isArray) { return AfterNewParsingTarget.ARRAY_INITIALZER; } if(builderWrapper.getTokenType() != LBRACE) { return AfterNewParsingTarget.NONE; } if(forceArray) { return AfterNewParsingTarget.ARRAY_INITIALZER; } // force property list, anonym object if(typeInfo == null) { return AfterNewParsingTarget.ANONYM_PROPERTY_SET_LIST; } if(builderWrapper.lookAhead(1) == LBRACKET) { return AfterNewParsingTarget.DICTIONARY_INITIALZER; } if(builderWrapper.lookAhead(1) == CSharpTokens.IDENTIFIER && builderWrapper.lookAhead(2) == EQ) { return AfterNewParsingTarget.PROPERTY_SET_LIST; } else { return AfterNewParsingTarget.ARRAY_INITIALZER; } } private static PsiBuilder.Marker parseArrayInitializer(CSharpBuilderWrapper builderWrapper, IElementType to, ModifierSet set) { if(builderWrapper.getTokenType() != LBRACE) { return null; } PsiBuilder.Marker marker = builderWrapper.mark(); builderWrapper.advanceLexer(); while(!builderWrapper.eof()) { if(builderWrapper.getTokenType() == RBRACE) { break; } boolean enteredValue = false; if(builderWrapper.getTokenType() == LBRACE) { parseArrayInitializerCompositeValue(builderWrapper, set); enteredValue = true; } else { PsiBuilder.Marker temp = builderWrapper.mark(); if(parse(builderWrapper, set) != null) { enteredValue = true; temp.done(ARRAY_INITIALIZER_SINGLE_VALUE); } else { // for example we entered keyword, it cant be parsed as expression if(builderWrapper.getTokenType() != COMMA && builderWrapper.getTokenType() != RBRACE) { // we entered value enteredValue = true; builderWrapper.advanceLexer(); temp.error("Expression expected"); } else { temp.drop(); } } } if(builderWrapper.getTokenType() == COMMA) { if(!enteredValue) { builderWrapper.error("Expression expected"); } builderWrapper.advanceLexer(); } else if(builderWrapper.getTokenType() != RBRACE) { builderWrapper.error("Comma expected"); } } expect(builderWrapper, RBRACE, "'}' expected"); marker.done(to); return marker; } private static PsiBuilder.Marker parseArrayInitializerCompositeValue(CSharpBuilderWrapper builder, ModifierSet set) { if(builder.getTokenType() != LBRACE) { return null; } PsiBuilder.Marker headerMarker = builder.mark(); builder.advanceLexer(); if(builder.getTokenType() == RBRACE) { builder.advanceLexer(); } else { parseArguments(builder, RBRACE, false, set); expect(builder, RBRACE, "'}' expected"); } headerMarker.done(ARRAY_INITIALIZER_COMPOSITE_VALUE); return headerMarker; } private static void parseAnonymFieldOrPropertySetBlock(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(!expect(builder, RBRACE, null)) { while(!builder.eof()) { if(builder.getTokenType() == RBRACE) { break; } PsiBuilder.Marker tempMarker = builder.mark(); PsiBuilder.Marker expressionMarker = parse(builder, set); if(expressionMarker != null) { IElementType elementType = SharedParsingHelpers.exprType(expressionMarker); if(elementType == CSharpElements.ASSIGNMENT_EXPRESSION) { tempMarker.rollbackTo(); parseNamedFieldOrPropertySet(builder, set); } else { if(elementType != CSharpElements.REFERENCE_EXPRESSION) { tempMarker.error("Can use assign or reference expression"); } else { tempMarker.done(CSharpElements.ANONYM_FIELD_OR_PROPERTY_SET); } } } else { tempMarker.drop(); } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else if(builder.getTokenType() != RBRACE) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("',' expected"); } } expect(builder, RBRACE, "'}' expected"); } mark.done(FIELD_OR_PROPERTY_SET_BLOCK); } private static void parseNamedFieldOrPropertySetBlock(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(!expect(builder, RBRACE, null)) { while(!builder.eof()) { if(builder.getTokenType() == RBRACE) { break; } if(parseNamedFieldOrPropertySet(builder, set) == null) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("Identifier expected"); } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else if(builder.getTokenType() != RBRACE) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("',' expected"); } } expect(builder, RBRACE, "'}' expected"); } mark.done(FIELD_OR_PROPERTY_SET_BLOCK); } private static PsiBuilder.Marker parseNamedFieldOrPropertySet(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); if(doneOneElement(builder, CSharpTokens.IDENTIFIER, REFERENCE_EXPRESSION, "Identifier expected")) { if(expect(builder, EQ, "'=' expected")) { if(parse(builder, set) == null) { builder.error("Expression expected"); } } mark.done(NAMED_FIELD_OR_PROPERTY_SET); return mark; } else { mark.rollbackTo(); return null; } } private static void parseDictionaryInitializerList(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(!expect(builder, RBRACE, null)) { while(!builder.eof()) { if(builder.getTokenType() == RBRACE) { break; } if(parseDictionaryInitializer(builder, set) == null) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("'[' expected"); } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else if(builder.getTokenType() != RBRACE) { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("',' expected"); } } expect(builder, RBRACE, "'}' expected"); } mark.done(DICTIONARY_INITIALIZER_LIST); } private static PsiBuilder.Marker parseDictionaryInitializer(CSharpBuilderWrapper builder, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); if(builder.getTokenType() == LBRACKET) { PsiBuilder.Marker firstArgumentMarker = builder.mark(); builder.advanceLexer(); if(parse(builder, set) == null) { builder.error("Expression expected"); } expect(builder, RBRACKET, "']' expected"); firstArgumentMarker.done(CALL_ARGUMENT); if(expect(builder, EQ, "'=' expected")) { PsiBuilder.Marker valueArgumentMarker = builder.mark(); if(parse(builder, set) == null) { builder.error("Expression expected"); } valueArgumentMarker.done(CALL_ARGUMENT); } mark.done(DICTIONARY_INITIALIZER); return mark; } else { mark.rollbackTo(); return null; } } private static PsiBuilder.Marker parseExpressionWithTypeInLParRPar(CSharpBuilderWrapper builder, int flags, PsiBuilder.Marker mark, IElementType to) { PsiBuilder.Marker newMarker = mark == null ? builder.mark() : mark.precede(); builder.advanceLexer(); if(expect(builder, LPAR, "'(' expected")) { if(parseType(builder, flags, TokenSet.EMPTY) == null) { builder.error("Type expected"); } expect(builder, RPAR, "')' expected"); } newMarker.done(to); return newMarker; } private static PsiBuilder.Marker parseExpressionWithExpressionInLParRPar(CSharpBuilderWrapper builder, PsiBuilder.Marker mark, IElementType to, ModifierSet set) { PsiBuilder.Marker newMarker = mark == null ? builder.mark() : mark.precede(); builder.advanceLexer(); if(expect(builder, LPAR, "'(' expected")) { if(parse(builder, set) == null) { builder.error("Expression expected"); } expect(builder, RPAR, "')' expected"); } newMarker.done(to); return newMarker; } public static class ReferenceInfo { public boolean isParameterized; public PsiBuilder.Marker marker; public ReferenceInfo(boolean isParameterized, PsiBuilder.Marker marker) { this.isParameterized = isParameterized; this.marker = marker; } } public static ReferenceInfo parseQualifiedReference(@NotNull CSharpBuilderWrapper builder, @Nullable PsiBuilder.Marker prevMarker) { return parseQualifiedReference(builder, prevMarker, NONE, TokenSet.EMPTY); } public static ReferenceInfo parseQualifiedReference(@NotNull CSharpBuilderWrapper builder, @Nullable final PsiBuilder.Marker prevMarker, int flags, @NotNull TokenSet nameStopperSet) { if(prevMarker != null) { builder.advanceLexer(); // skip dot or coloncolon } PsiBuilder.Marker marker = prevMarker == null ? builder.mark() : prevMarker; ReferenceInfo referenceInfo = new ReferenceInfo(false, marker); if(prevMarker == null) { builder.enableSoftKeyword(CSharpSoftTokens.GLOBAL_KEYWORD); IElementType tokenType = builder.getTokenType(); builder.disableSoftKeyword(CSharpSoftTokens.GLOBAL_KEYWORD); if(tokenType == CSharpSoftTokens.GLOBAL_KEYWORD && builder.lookAhead(1) == CSharpTokens.COLONCOLON) { builder.advanceLexer(); // global marker.done(BitUtil.isSet(flags, STUB_SUPPORT) ? CSharpStubElements.REFERENCE_EXPRESSION : CSharpElements.REFERENCE_EXPRESSION); return parseQualifiedReference(builder, marker.precede(), flags, nameStopperSet); } else { builder.remapBackIfSoft(); } } if(expect(builder, CSharpTokens.IDENTIFIER, "Identifier expected")) { referenceInfo.isParameterized = parseReferenceTypeArgumentList(builder, flags) != null; marker.done(BitUtil.isSet(flags, STUB_SUPPORT) ? CSharpStubElements.REFERENCE_EXPRESSION : CSharpElements.REFERENCE_EXPRESSION); // inside doc - PLUS used for nested type reference if(builder.getTokenType() == DOT || BitUtil.isSet(flags, INSIDE_DOC) && builder.getTokenType() == PLUS) { // if after dot we found stoppers, name expected - but we done if(nameStopperSet.contains(builder.lookAhead(1)) || nameStopperSet.contains(builder.lookAhead(2))) { return referenceInfo; } referenceInfo = parseQualifiedReference(builder, marker.precede(), flags, nameStopperSet); } } else { marker.drop(); return null; } return referenceInfo; } @Nullable private static PsiBuilder.Marker parseReferenceTypeArgumentList(@NotNull CSharpBuilderWrapper builder, int flags) { IElementType startElementType = BitUtil.isSet(flags, INSIDE_DOC) ? LBRACE : LT; if(builder.getTokenType() != startElementType) { return null; } if(BitUtil.isSet(flags, ALLOW_EMPTY_TYPE_ARGUMENTS)) { if(BitUtil.isSet(flags, STUB_SUPPORT)) { throw new IllegalArgumentException("Empty type arguments is not allowed inside stub tree"); } PsiBuilder.Marker marker = parseReferenceEmptyTypeArgumentListImpl(builder); if(marker != null) { return marker; } } return parseReferenceTypeArgumentListImpl(builder, flags); } @Nullable private static PsiBuilder.Marker parseReferenceEmptyTypeArgumentListImpl(@NotNull CSharpBuilderWrapper builder) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(builder.getTokenType() == GT) { builder.advanceLexer(); } else { while(!builder.eof()) { if(builder.getTokenType() == GT) { break; } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else { mark.rollbackTo(); return null; } } if(builder.getTokenType() == GT) { builder.advanceLexer(); } else { mark.rollbackTo(); return null; } } mark.done(CSharpElements.EMPTY_TYPE_ARGUMENTS); return mark; } @Nullable private static PsiBuilder.Marker parseReferenceTypeArgumentListImpl(@NotNull CSharpBuilderWrapper builder, int flags) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); IElementType stopElementType = BitUtil.isSet(flags, INSIDE_DOC) ? RBRACE : GT; if(builder.getTokenType() == stopElementType) { builder.error("Expected type"); builder.advanceLexer(); } else { while(!builder.eof()) { TypeInfo marker = parseType(builder, flags); if(marker == null) { builder.error("Expected type"); } if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else { break; } } if(builder.getTokenType() == stopElementType) { builder.advanceLexer(); } else { mark.rollbackTo(); return null; } } mark.done(BitUtil.isSet(flags, STUB_SUPPORT) ? CSharpStubElements.TYPE_ARGUMENTS : CSharpElements.TYPE_ARGUMENTS); return mark; } }