/* * Copyright 2000-2016 JetBrains s.r.o. * * 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 org.jetbrains.plugins.groovy.lang.parser.parsing.statements.declaration; import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyBundle; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyParser; import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.ThrowClause; import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.annotations.AnnotationArguments; import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.parameters.ParameterList; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.TupleParse; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.blocks.OpenOrClosableBlock; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.constructor.ConstructorBody; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.AssignmentExpression; import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils; /** * @autor: Dmitry.Krasilschikov * @date: 16.03.2007 */ public class VariableDefinitions { public static IElementType parseDefinitions(@NotNull PsiBuilder builder, boolean isInClass, boolean isInAnnotation, @Nullable String typeDefinitionName, boolean hasModifiers, boolean canBeTuple, @NotNull GroovyParser parser) { boolean isLParenth = builder.getTokenType() == GroovyTokenTypes.mLPAREN; boolean isStringName = builder.getTokenType() == GroovyTokenTypes.mSTRING_LITERAL || builder.getTokenType() == GroovyTokenTypes.mGSTRING_LITERAL; if (builder.getTokenType() != GroovyTokenTypes.mIDENT && !isStringName && !isLParenth) { builder.error(GroovyBundle.message("indentifier.or.string.or.left.parenth.literal.expected")); return GroovyElementTypes.WRONGWAY; } if (isLParenth && !canBeTuple) { builder.error(GroovyBundle.message("indentifier.or.string.or.left.parenth.literal.expected")); return GroovyElementTypes.WRONGWAY; } if (isInAnnotation && isStringName) { builder.error(GroovyBundle.message("string.name.unexpected")); } if (!isLParenth) { //id or string => method name PsiBuilder.Marker varMarker = builder.mark(); final boolean isConstructor = isInClass && !isInAnnotation && typeDefinitionName != null && builder.getTokenType() == GroovyTokenTypes.mIDENT && typeDefinitionName.equals(builder.getTokenText()); builder.advanceLexer(); if (GroovyTokenTypes.mLPAREN != builder.getTokenType()) { varMarker.rollbackTo(); } else { varMarker.drop(); return parseMethod(builder, isInAnnotation, hasModifiers, parser, isConstructor); } } return parseVar(builder, isInClass, hasModifiers, parser, isLParenth); } private static IElementType parseVar(PsiBuilder builder, boolean isInClass, boolean hasModifiers, GroovyParser parser, boolean LParenth) { // a = b, c = d PsiBuilder.Marker varAssMarker = builder.mark(); final IElementType declarator = parseDeclarator(builder, LParenth); if (declarator != GroovyElementTypes.WRONGWAY) { final boolean wasAssignment = parseAssignment(builder, parser); if (declarator == GroovyElementTypes.TUPLE_DECLARATION) { varAssMarker.drop(); if (!wasAssignment) { builder.error(GroovyBundle.message("assignment.expected")); return GroovyElementTypes.WRONGWAY; } } else if (isInClass) { // a = b, c = d varAssMarker.done(GroovyElementTypes.FIELD); } else { varAssMarker.done(GroovyElementTypes.VARIABLE); } if (declarator != GroovyElementTypes.TUPLE_DECLARATION) { while (ParserUtils.getToken(builder, GroovyTokenTypes.mCOMMA)) { ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (GroovyElementTypes.WRONGWAY.equals(parseVariableOrField(builder, isInClass, parser)) && declarator == GroovyTokenTypes.mIDENT) { return GroovyElementTypes.VARIABLE_DEFINITION_ERROR; //parse b = d } } } if (isInClass && declarator == GroovyElementTypes.TUPLE_DECLARATION) { builder.error(GroovyBundle.message("tuple.cant.be.placed.in.class")); } return GroovyElementTypes.VARIABLE_DEFINITION; } else { varAssMarker.drop(); builder.error(GroovyBundle.message("identifier.expected")); return GroovyElementTypes.WRONGWAY; } } private static IElementType parseMethod(PsiBuilder builder, boolean isAnnotationMember, boolean hasModifiers, GroovyParser parser, boolean constructor) { //if we have no modifiers and current method is not constructor there is something wrong if (!hasModifiers && !constructor) { builder.error(GroovyBundle.message("method.definition.without.modifier")); return GroovyElementTypes.WRONGWAY; } builder.advanceLexer(); ParameterList.parse(builder, GroovyTokenTypes.mRPAREN, parser); ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (!ParserUtils.getToken(builder, GroovyTokenTypes.mRPAREN)) { builder.error(GroovyBundle.message("rparen.expected")); ThrowClause.parse(builder); return methodType(isAnnotationMember, constructor); } if (isAnnotationMember && builder.getTokenType() == GroovyTokenTypes.kDEFAULT) { ParserUtils.getToken(builder, GroovyTokenTypes.kDEFAULT); ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (!AnnotationArguments.parseAnnotationMemberValueInitializer(builder, parser)) { builder.error(GroovyBundle.message("annotation.initializer.expected")); } } if (ParserUtils.lookAhead(builder, GroovyTokenTypes.mNLS, GroovyTokenTypes.kTHROWS) || ParserUtils.lookAhead(builder, GroovyTokenTypes.mNLS, GroovyTokenTypes.mLCURLY)) { ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); } if (isAnnotationMember && builder.getTokenType() == GroovyTokenTypes.kTHROWS) { builder.error(GroovyBundle.message("throws.clause.is.not.allowed.in.at.interface")); } ThrowClause.parse(builder); if (builder.getTokenType() == GroovyTokenTypes.mLCURLY || ParserUtils.lookAhead(builder, GroovyTokenTypes.mNLS, GroovyTokenTypes.mLCURLY)) { ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (isAnnotationMember) { builder.error(GroovyBundle.message("separator.or.rcurly.expected")); } if (constructor) { ConstructorBody.parseConstructorBody(builder, parser); } else { OpenOrClosableBlock.parseOpenBlock(builder, parser); } } return methodType(isAnnotationMember, constructor); } private static IElementType methodType(boolean isAnnotationMember, final boolean isConstructor) { return isAnnotationMember ? GroovyElementTypes.ANNOTATION_METHOD : isConstructor ? GroovyElementTypes.CONSTRUCTOR_DEFINITION : GroovyElementTypes.METHOD_DEFINITION; } //a, a = b private static IElementType parseVariableOrField(PsiBuilder builder, boolean isInClass, GroovyParser parser) { PsiBuilder.Marker varAssMarker = builder.mark(); if (ParserUtils.getToken(builder, GroovyTokenTypes.mIDENT)) { parseAssignment(builder, parser); if (isInClass) { varAssMarker.done(GroovyElementTypes.FIELD); return GroovyElementTypes.FIELD; } else { varAssMarker.done(GroovyElementTypes.VARIABLE); return GroovyElementTypes.VARIABLE; } } else { varAssMarker.drop(); builder.error("Identifier expected"); return GroovyElementTypes.WRONGWAY; } } private static IElementType parseDeclarator(PsiBuilder builder, boolean isTuple) { if (isTuple && builder.getTokenType() == GroovyTokenTypes.mLPAREN && TupleParse.parseTupleForVariableDeclaration(builder)) { return GroovyElementTypes.TUPLE_DECLARATION; } if (!isTuple && ParserUtils.getToken(builder, GroovyTokenTypes.mIDENT)) { return GroovyTokenTypes.mIDENT; } return GroovyElementTypes.WRONGWAY; } private static boolean parseAssignment(PsiBuilder builder, GroovyParser parser) { if (ParserUtils.getToken(builder, GroovyTokenTypes.mASSIGN)) { PsiBuilder.Marker marker = builder.mark(); ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (!AssignmentExpression.parse(builder, parser, true)) { marker.rollbackTo(); builder.error(GroovyBundle.message("expression.expected")); return false; } else { marker.drop(); return true; } } return false; } }