diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 376f31b01280..d1d91be0ee21 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -1,843 +1,843 @@ -/* - * Copyright 2000-2010 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 com.intellij.lang.java.parser; - -import com.intellij.codeInsight.daemon.JavaErrorMessages; -import com.intellij.lang.PsiBuilder; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.impl.source.tree.JavaElementType; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.ILazyParseableElementType; -import com.intellij.psi.tree.TokenSet; -import com.intellij.util.text.CharArrayUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.intellij.lang.PsiBuilderUtil.expect; -import static com.intellij.lang.PsiBuilderUtil.nextTokenType; -import static com.intellij.lang.java.parser.JavaParserUtil.*; - - -public class DeclarationParser { - public enum Context { - FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE - } - - private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); - private static final TokenSet BEFORE_LBRACE_ELEMENTS_SET = TokenSet.create( - JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.IMPLEMENTS_KEYWORD); - private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create( - JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD); - - private static final String WHITESPACES = "\n\r \t"; - private static final String LINE_ENDS = "\n\r"; - - private DeclarationParser() { } - - public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) { - assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); - builder.advanceLexer(); - - final PsiBuilder builderWrapper = braceMatchingBuilder(builder); - if (isEnum) { - parseEnumConstants(builderWrapper); - } - parseClassBodyDeclarations(builderWrapper, isAnnotation); - - expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); - } - - @Nullable - public static PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker declaration, - final boolean isAnnotation, final Context context) { - final IElementType keywordTokenType = builder.getTokenType(); - assert ElementType.CLASS_KEYWORD_BIT_SET.contains(keywordTokenType) : keywordTokenType; - builder.advanceLexer(); - final boolean isEnum = (keywordTokenType == JavaTokenType.ENUM_KEYWORD); - - if (!expect(builder, JavaTokenType.IDENTIFIER)) { - error(builder, JavaErrorMessages.message("expected.identifier")); - declaration.drop(); - return null; - } - - ReferenceParser.parseTypeParameters(builder); - ReferenceParser.parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_LIST, JavaTokenType.COMMA); - ReferenceParser.parseReferenceList(builder, JavaTokenType.IMPLEMENTS_KEYWORD, JavaElementType.IMPLEMENTS_LIST, JavaTokenType.COMMA); - - if (builder.getTokenType() != JavaTokenType.LBRACE) { - final PsiBuilder.Marker error = builder.mark(); - while (BEFORE_LBRACE_ELEMENTS_SET.contains(builder.getTokenType())) { - builder.advanceLexer(); - } - error.error(JavaErrorMessages.message("expected.lbrace")); - } - - if (builder.getTokenType() == JavaTokenType.LBRACE) { - parseClassBodyWithBraces(builder, isAnnotation, isEnum); - } - - if (context == Context.FILE) { - boolean declarationsAfterEnd = false; - - while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) { - final PsiBuilder.Marker position = builder.mark(); - final PsiBuilder.Marker extra = parse(builder, Context.CLASS); - if (extra != null && AFTER_END_DECLARATION_SET.contains(exprType(extra))) { - if (!declarationsAfterEnd) { - error(builder, JavaErrorMessages.message("expected.class.or.interface"), extra); - } - declarationsAfterEnd = true; - position.drop(); - } - else { - position.rollbackTo(); - break; - } - } - - if (declarationsAfterEnd) { - expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); - } - } - - done(declaration, JavaElementType.CLASS); - return declaration; - } - - private static void parseEnumConstants(final PsiBuilder builder) { - while (builder.getTokenType() != null) { - if (expect(builder, JavaTokenType.SEMICOLON)) { - return; - } - - if (builder.getTokenType() == JavaTokenType.PRIVATE_KEYWORD || builder.getTokenType() == JavaTokenType.PROTECTED_KEYWORD) { - error(builder, JavaErrorMessages.message("expected.semicolon")); - return; - } - - final PsiBuilder.Marker enumConstant = parseEnumConstant(builder); - if (enumConstant == null) { - error(builder, JavaErrorMessages.message("expected.identifier")); - } - - if (!expect(builder, JavaTokenType.COMMA)) { - if (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.SEMICOLON) { - error(builder, JavaErrorMessages.message("expected.comma.or.semicolon")); - return; - } - } - } - } - - @Nullable - private static PsiBuilder.Marker parseEnumConstant(final PsiBuilder builder) { - final PsiBuilder.Marker constant = builder.mark(); - - parseModifierList(builder); - - if (expect(builder, JavaTokenType.IDENTIFIER)) { - if (builder.getTokenType() == JavaTokenType.LPARENTH) { - ExpressionParser.parseArgumentList(builder); - } - else { - emptyElement(builder, JavaElementType.EXPRESSION_LIST); - } - - if (builder.getTokenType() == JavaTokenType.LBRACE) { - final PsiBuilder.Marker constantInit = builder.mark(); - parseClassBodyWithBraces(builder, false, false); - done(constantInit, JavaElementType.ENUM_CONSTANT_INITIALIZER); - } - - done(constant, JavaElementType.ENUM_CONSTANT); - return constant; - } - else { - constant.rollbackTo(); - return null; - } - } - - private static void parseClassBodyDeclarations(final PsiBuilder builder, final boolean isAnnotation) { - final Context context = isAnnotation ? Context.ANNOTATION_INTERFACE : Context.CLASS; - - PsiBuilder.Marker invalidElements = null; - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == null || tokenType == JavaTokenType.RBRACE) break; - - if (tokenType == JavaTokenType.SEMICOLON) { - if (invalidElements != null) { - invalidElements.error(JavaErrorMessages.message("unexpected.token")); - invalidElements = null; - } - builder.advanceLexer(); - continue; - } - - final PsiBuilder.Marker declaration = parse(builder, context); - if (declaration != null) { - if (invalidElements != null) { - invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), declaration); - invalidElements = null; - } - continue; - } - - if (invalidElements == null) { - invalidElements = builder.mark(); - } - - // adding a reference, not simple tokens allows "Browse ..." to work well - final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, true, false, false); - if (ref == null) { - builder.advanceLexer(); - } - } - - if (invalidElements != null) { - invalidElements.error(JavaErrorMessages.message("unexpected.token")); - } - } - - @Nullable - public static PsiBuilder.Marker parse(final PsiBuilder builder, final Context context) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == null) return null; - - if (tokenType == JavaTokenType.LBRACE) { - if (context == Context.FILE || context == Context.CODE_BLOCK) return null; - } - else if (tokenType == JavaTokenType.IDENTIFIER || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - if (context == Context.FILE) return null; - } - else if (tokenType instanceof ILazyParseableElementType) { - builder.advanceLexer(); - return null; - } - else if (!ElementType.MODIFIER_BIT_SET.contains(tokenType) && - !ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType) && - tokenType != JavaTokenType.AT && - (context == Context.CODE_BLOCK || tokenType != JavaTokenType.LT)) { - return null; - } - - final PsiBuilder.Marker declaration = builder.mark(); - final int declarationStart = builder.getCurrentOffset(); - - final Pair modListInfo = parseModifierList(builder); - final PsiBuilder.Marker modList = modListInfo.first; - - if (expect(builder, JavaTokenType.AT)) { - if (builder.getTokenType() == JavaTokenType.INTERFACE_KEYWORD) { - final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, true, context); - return result != null ? result : modList; - } - else { - declaration.rollbackTo(); - return null; - } - } - else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) { - final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, false, context); - return result != null ? result : modList; - } - - PsiBuilder.Marker typeParams = null; - if (builder.getTokenType() == JavaTokenType.LT) { - typeParams = ReferenceParser.parseTypeParameters(builder); - } - - if (context == Context.FILE) { - error(builder, JavaErrorMessages.message("expected.class.or.interface"), typeParams); - declaration.drop(); - return modList; - } - - PsiBuilder.Marker type; - if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType())) { - type = parseTypeNotNull(builder); - } - else if (builder.getTokenType() == JavaTokenType.IDENTIFIER) { - final PsiBuilder.Marker idPos = builder.mark(); - type = parseTypeNotNull(builder); - if (builder.getTokenType() == JavaTokenType.LPARENTH) { // constructor - if (context == Context.CODE_BLOCK) { - declaration.rollbackTo(); - return null; - } - idPos.rollbackTo(); - if (typeParams == null) { - emptyElement(builder, JavaElementType.TYPE_PARAMETER_LIST); - } - builder.advanceLexer(); - if (builder.getTokenType() != JavaTokenType.LPARENTH) { - declaration.rollbackTo(); - return null; - } - return parseMethodFromLeftParenth(builder, declaration, false, true); - } - idPos.drop(); - } - else if (builder.getTokenType() == JavaTokenType.LBRACE) { - if (context == Context.CODE_BLOCK) { - error(builder, JavaErrorMessages.message("expected.identifier.or.type"), typeParams); - declaration.drop(); - return modList; - } - - final PsiBuilder.Marker codeBlock = StatementParser.parseCodeBlock(builder); - assert codeBlock != null : builder.getOriginalText(); - - if (typeParams != null) { - final PsiBuilder.Marker error = typeParams.precede(); - error.errorBefore(JavaErrorMessages.message("unexpected.token"), codeBlock); - } - done(declaration, JavaElementType.CLASS_INITIALIZER); - return declaration; - } - else { - final PsiBuilder.Marker error; - if (typeParams != null) { - error = typeParams.precede(); - } - else { - error = builder.mark(); - } - error.error(JavaErrorMessages.message("expected.identifier.or.type")); - declaration.drop(); - return modList; - } - - if (!expect(builder, JavaTokenType.IDENTIFIER)) { - if (context == Context.CODE_BLOCK && modListInfo.second) { - declaration.rollbackTo(); - return null; - } - else { - if (typeParams != null) { - typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); - } - builder.error(JavaErrorMessages.message("expected.identifier")); - declaration.drop(); - return modList; - } - } - - if (builder.getTokenType() == JavaTokenType.LPARENTH) { - if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { // method - if (typeParams == null) { - emptyElement(type, JavaElementType.TYPE_PARAMETER_LIST); - } - return parseMethodFromLeftParenth(builder, declaration, (context == Context.ANNOTATION_INTERFACE), false); - } - } - - if (typeParams != null) { - typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); - } - return parseFieldOrLocalVariable(builder, declaration, declarationStart, context); - } - - @NotNull - private static PsiBuilder.Marker parseTypeNotNull(final PsiBuilder builder) { - final PsiBuilder.Marker type = ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); - assert type != null : builder.getOriginalText(); - return type; - } - - @NotNull - private static Pair parseModifierList(final PsiBuilder builder) { - return parseModifierList(builder, ElementType.MODIFIER_BIT_SET); - } - - @NotNull - public static Pair parseModifierList(final PsiBuilder builder, final TokenSet modifiers) { - final PsiBuilder.Marker modList = builder.mark(); - boolean isEmpty = true; - - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == null) break; - if (modifiers.contains(tokenType)) { - builder.advanceLexer(); - isEmpty = false; - } - else if (tokenType == JavaTokenType.AT) { - if (ElementType.KEYWORD_BIT_SET.contains(nextTokenType(builder))) { - break; - } - parseAnnotation(builder); - isEmpty = false; - } - else { - break; - } - } - - done(modList, JavaElementType.MODIFIER_LIST); - return Pair.create(modList, isEmpty); - } - - private static PsiBuilder.Marker parseMethodFromLeftParenth(final PsiBuilder builder, final PsiBuilder.Marker declaration, - final boolean anno, final boolean constructor) { - parseParameterList(builder); - - eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon")); - - if (areTypeAnnotationsSupported(builder)) { - final PsiBuilder.Marker receiver = builder.mark(); - final PsiBuilder.Marker annotations = parseAnnotations(builder); - if (annotations != null) { - done(receiver, JavaElementType.METHOD_RECEIVER); - } - else { - receiver.drop(); - } - } - - ReferenceParser.parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA); - - if (anno && expect(builder, JavaTokenType.DEFAULT_KEYWORD)) { - parseAnnotationValue(builder); - } - - final IElementType tokenType = builder.getTokenType(); - if (tokenType != JavaTokenType.SEMICOLON && tokenType != JavaTokenType.LBRACE) { - final PsiBuilder.Marker error = builder.mark(); - // heuristic: going to next line obviously means method signature is over, starting new method (actually, another one completion hack) - final CharSequence text = builder.getOriginalText(); - Loop: - while (true) { - for (int i = builder.getCurrentOffset() - 1; i >= 0; i--) { - final char ch = text.charAt(i); - if (ch == '\n') break Loop; - else if (ch != ' ' && ch != '\t') break; - } - if (!expect(builder, APPEND_TO_METHOD_SET)) break; - } - error.error(JavaErrorMessages.message("expected.lbrace.or.semicolon")); - } - - if (!expect(builder, JavaTokenType.SEMICOLON)) { - if (builder.getTokenType() == JavaTokenType.LBRACE) { - StatementParser.parseCodeBlock(builder); - } - } - - done(declaration, anno ? JavaElementType.ANNOTATION_METHOD : JavaElementType.METHOD); - return declaration; - } - - @NotNull - public static PsiBuilder.Marker parseParameterList(final PsiBuilder builder) { - assert builder.getTokenType() == JavaTokenType.LPARENTH : builder.getTokenType(); - final PsiBuilder.Marker paramList = builder.mark(); - builder.advanceLexer(); - - PsiBuilder.Marker invalidElements = null; - String errorMessage = null; - boolean commaExpected = false; - int paramCount = 0; - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == null || tokenType == JavaTokenType.RPARENTH) { - boolean noLastParam = !commaExpected && paramCount > 0; - if (noLastParam) { - error(builder, JavaErrorMessages.message("expected.identifier.or.type")); - } - if (tokenType == JavaTokenType.RPARENTH) { - if (invalidElements != null) { - invalidElements.error(errorMessage); - } - invalidElements = null; - builder.advanceLexer(); - } - else { - if (!noLastParam) { - if (invalidElements != null) { - invalidElements.error(errorMessage); - } - invalidElements = null; - error(builder, JavaErrorMessages.message("expected.rparen")); - } - } - break; - } - - if (commaExpected) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - commaExpected = false; - if (invalidElements != null) { - invalidElements.error(errorMessage); - invalidElements = null; - } - builder.advanceLexer(); - continue; - } - } - else { - final PsiBuilder.Marker param = parseParameter(builder, true); - if (param != null) { - commaExpected = true; - if (invalidElements != null) { - invalidElements.errorBefore(errorMessage, param); - invalidElements = null; - } - paramCount++; - continue; - } - } - - if (invalidElements == null) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - error(builder, JavaErrorMessages.message("expected.parameter")); - builder.advanceLexer(); - continue; - } - else { - invalidElements = builder.mark(); - errorMessage = commaExpected ? JavaErrorMessages.message("expected.comma") : JavaErrorMessages.message("expected.parameter"); - } - } - - // adding a reference, not simple tokens allows "Browse .." to work well - final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, true, false, false); - if (ref == null && builder.getTokenType() != null) { - builder.advanceLexer(); - } - } - - if (invalidElements != null) { - invalidElements.error(errorMessage); - } - - done(paramList, JavaElementType.PARAMETER_LIST); - return paramList; - } - - @Nullable - public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis) { - final PsiBuilder.Marker param = builder.mark(); - - final Pair modListInfo = parseModifierList(builder); - - int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; - if (ellipsis) flags |= ReferenceParser.ELLIPSIS; - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); - - if (typeInfo == null && modListInfo.second) { - param.rollbackTo(); - return null; - } - - if (typeInfo == null) { - error(builder, JavaErrorMessages.message("expected.type")); - emptyElement(builder, JavaElementType.TYPE); - } - - if (expect(builder, JavaTokenType.IDENTIFIER)) { - eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); - done(param, JavaElementType.PARAMETER); - return param; - } - else { - error(builder, JavaErrorMessages.message("expected.identifier")); - param.drop(); - return modListInfo.first; - } - } - - @Nullable - private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration, - final int declarationStart, final Context context) { - final IElementType varType; - if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { - varType = JavaElementType.FIELD; - } - else if (context == Context.CODE_BLOCK) { - varType = JavaElementType.LOCAL_VARIABLE; - } - else { - declaration.drop(); - assert false : "Unexpected context: " + context; - return null; - } - - PsiBuilder.Marker variable = declaration; - boolean unclosed = false; - boolean eatSemicolon = true; - boolean shouldRollback; - boolean openMarker = true; - while (true) { - shouldRollback = true; - - if (!eatBrackets(builder, false, null)) { - unclosed = true; - } - - if (expect(builder, JavaTokenType.EQ)) { - final PsiBuilder.Marker expr = ExpressionParser.parse(builder); - if (expr != null) { - shouldRollback = false; - } - else { - error(builder, JavaErrorMessages.message("expected.expression")); - unclosed = true; - break; - } - } - - if (builder.getTokenType() != JavaTokenType.COMMA) break; - done(variable, varType); - builder.advanceLexer(); - - if (builder.getTokenType() != JavaTokenType.IDENTIFIER) { - error(builder, JavaErrorMessages.message("expected.identifier")); - unclosed = true; - eatSemicolon = false; - openMarker = false; - break; - } - - variable = builder.mark(); - builder.advanceLexer(); - } - - if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) { - builder.advanceLexer(); - } - else { - // special treatment (see DeclarationParserTest.testMultiLineUnclosed()) - if (!builder.eof() && shouldRollback) { - final CharSequence text = builder.getOriginalText(); - final int spaceEnd = builder.getCurrentOffset(); - final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES); - final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS); - - if (declarationStart < lineStart && lineStart < spaceStart) { - final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES); - declaration.rollbackTo(); - return parse(stoppingBuilder(builder, newBufferEnd), context); - } - } - - if (!unclosed) { - error(builder, JavaErrorMessages.message("expected.semicolon")); - } - } - - if (openMarker) { - done(variable, varType); - } - - return declaration; - } - - private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) { - if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; - - final PsiBuilder.Marker marker = isError ? builder.mark() : null; - - boolean result = true; - while (expect(builder, JavaTokenType.LBRACKET)) { - if (!expect(builder, JavaTokenType.RBRACKET)) { - if (!isError) error(builder, JavaErrorMessages.message("expected.rbracket")); - result = false; - break; - } - } - - if (marker != null) { - marker.error(error); - } - - return result; - } - - @Nullable - public static PsiBuilder.Marker parseAnnotations(final PsiBuilder builder) { - PsiBuilder.Marker firstAnno = null; - - while (builder.getTokenType() == JavaTokenType.AT) { - final PsiBuilder.Marker anno = parseAnnotation(builder); - if (firstAnno == null) firstAnno = anno; - } - - return firstAnno; - } - - @NotNull - public static PsiBuilder.Marker parseAnnotation(final PsiBuilder builder) { - assert builder.getTokenType() == JavaTokenType.AT : builder.getTokenType(); - final PsiBuilder.Marker anno = builder.mark(); - builder.advanceLexer(); - - final PsiBuilder.Marker classRef = ReferenceParser.parseJavaCodeReference(builder, true, false, false, false); - if (classRef == null) { - error(builder, JavaErrorMessages.message("expected.class.reference")); - } - - parseAnnotationParameterList(builder); - - done(anno, JavaElementType.ANNOTATION); - return anno; - } - - @NotNull - private static PsiBuilder.Marker parseAnnotationParameterList(final PsiBuilder builder) { - PsiBuilder.Marker list = builder.mark(); - - if (!expect(builder, JavaTokenType.LPARENTH)) { - done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); - return list; - } - - if (expect(builder, JavaTokenType.RPARENTH)) { - done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); - return list; - } - - final boolean isFirstParamNamed = parseAnnotationParameter(builder, true); - boolean isFirstParamWarned = false; - - boolean afterBad = false; - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == null) { - error(builder, JavaErrorMessages.message("expected.parameter")); - break; - } - else if (expect(builder, JavaTokenType.RPARENTH)) { - break; - } - else if (tokenType == JavaTokenType.COMMA) { - final PsiBuilder.Marker errorStart = builder.mark(); - final PsiBuilder.Marker errorEnd = builder.mark(); - builder.advanceLexer(); - final boolean hasParamName = parseAnnotationParameter(builder, false); - if (!isFirstParamNamed && hasParamName && !isFirstParamWarned) { - errorStart.errorBefore(JavaErrorMessages.message("annotation.name.is.missing"), errorEnd); - isFirstParamWarned = true; - } - else { - errorStart.drop(); - } - errorEnd.drop(); - } - else if (!afterBad) { - error(builder, JavaErrorMessages.message("expected.comma.or.rparen")); - builder.advanceLexer(); - afterBad = true; - } - else { - afterBad = false; - parseAnnotationParameter(builder, false); - } - } - - done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); - return list; - } - - private static boolean parseAnnotationParameter(final PsiBuilder builder, final boolean mayBeSimple) { - PsiBuilder.Marker pair = builder.mark(); - - if (mayBeSimple) { - parseAnnotationValue(builder); - if (builder.getTokenType() != JavaTokenType.EQ) { - done(pair, JavaElementType.NAME_VALUE_PAIR); - return false; - } - - pair.rollbackTo(); - pair = builder.mark(); - } - - final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier")); - - expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq")); - - parseAnnotationValue(builder); - - done(pair, JavaElementType.NAME_VALUE_PAIR); - - return hasName; - } - - @NotNull - private static PsiBuilder.Marker parseAnnotationValue(final PsiBuilder builder) { - PsiBuilder.Marker result; - - final IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.AT) { - result = parseAnnotation(builder); - } - else if (tokenType == JavaTokenType.LBRACE) { - result = parseAnnotationArrayInitializer(builder); - } - else { - result = ExpressionParser.parseConditional(builder); - } - - if (result == null) { - result = builder.mark(); - result.error(JavaErrorMessages.message("expected.value")); - } - - return result; - } - - @NotNull - private static PsiBuilder.Marker parseAnnotationArrayInitializer(final PsiBuilder builder) { - assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); - final PsiBuilder.Marker annoArray = builder.mark(); - builder.advanceLexer(); - - if (expect(builder, JavaTokenType.RBRACE)) { - done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER); - return annoArray; - } - - parseAnnotationValue(builder); - - boolean unclosed = false; - while (true) { - if (expect(builder, JavaTokenType.RBRACE)) { - break; - } - else if (expect(builder, JavaTokenType.COMMA)) { - if (builder.getTokenType() != JavaTokenType.RBRACE) { - parseAnnotationValue(builder); - } - } - else { - error(builder, JavaErrorMessages.message("expected.rbrace")); - unclosed = true; - break; - } - } - - done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER); - if (unclosed) { - annoArray.setCustomEdgeTokenBinders(null, GREEDY_RIGHT_EDGE_PROCESSOR); - } - return annoArray; - } -} +/* + * Copyright 2000-2010 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 com.intellij.lang.java.parser; + +import com.intellij.codeInsight.daemon.JavaErrorMessages; +import com.intellij.lang.PsiBuilder; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.ILazyParseableElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.lang.PsiBuilderUtil.expect; +import static com.intellij.lang.PsiBuilderUtil.nextTokenType; +import static com.intellij.lang.java.parser.JavaParserUtil.*; + + +public class DeclarationParser { + public enum Context { + FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE + } + + private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); + private static final TokenSet BEFORE_LBRACE_ELEMENTS_SET = TokenSet.create( + JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.IMPLEMENTS_KEYWORD); + private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create( + JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD); + + private static final String WHITESPACES = "\n\r \t"; + private static final String LINE_ENDS = "\n\r"; + + private DeclarationParser() { } + + public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) { + assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); + builder.advanceLexer(); + + final PsiBuilder builderWrapper = braceMatchingBuilder(builder); + if (isEnum) { + parseEnumConstants(builderWrapper); + } + parseClassBodyDeclarations(builderWrapper, isAnnotation); + + expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); + } + + @Nullable + public static PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final boolean isAnnotation, final Context context) { + final IElementType keywordTokenType = builder.getTokenType(); + assert ElementType.CLASS_KEYWORD_BIT_SET.contains(keywordTokenType) : keywordTokenType; + builder.advanceLexer(); + final boolean isEnum = (keywordTokenType == JavaTokenType.ENUM_KEYWORD); + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + error(builder, JavaErrorMessages.message("expected.identifier")); + declaration.drop(); + return null; + } + + ReferenceParser.parseTypeParameters(builder); + ReferenceParser.parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_LIST, JavaTokenType.COMMA); + ReferenceParser.parseReferenceList(builder, JavaTokenType.IMPLEMENTS_KEYWORD, JavaElementType.IMPLEMENTS_LIST, JavaTokenType.COMMA); + + if (builder.getTokenType() != JavaTokenType.LBRACE) { + final PsiBuilder.Marker error = builder.mark(); + while (BEFORE_LBRACE_ELEMENTS_SET.contains(builder.getTokenType())) { + builder.advanceLexer(); + } + error.error(JavaErrorMessages.message("expected.lbrace")); + } + + if (builder.getTokenType() == JavaTokenType.LBRACE) { + parseClassBodyWithBraces(builder, isAnnotation, isEnum); + } + + if (context == Context.FILE) { + boolean declarationsAfterEnd = false; + + while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) { + final PsiBuilder.Marker position = builder.mark(); + final PsiBuilder.Marker extra = parse(builder, Context.CLASS); + if (extra != null && AFTER_END_DECLARATION_SET.contains(exprType(extra))) { + if (!declarationsAfterEnd) { + error(builder, JavaErrorMessages.message("expected.class.or.interface"), extra); + } + declarationsAfterEnd = true; + position.drop(); + } + else { + position.rollbackTo(); + break; + } + } + + if (declarationsAfterEnd) { + expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); + } + } + + done(declaration, JavaElementType.CLASS); + return declaration; + } + + private static void parseEnumConstants(final PsiBuilder builder) { + while (builder.getTokenType() != null) { + if (expect(builder, JavaTokenType.SEMICOLON)) { + return; + } + + if (builder.getTokenType() == JavaTokenType.PRIVATE_KEYWORD || builder.getTokenType() == JavaTokenType.PROTECTED_KEYWORD) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + return; + } + + final PsiBuilder.Marker enumConstant = parseEnumConstant(builder); + if (enumConstant == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + + if (!expect(builder, JavaTokenType.COMMA)) { + if (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.SEMICOLON) { + error(builder, JavaErrorMessages.message("expected.comma.or.semicolon")); + return; + } + } + } + } + + @Nullable + private static PsiBuilder.Marker parseEnumConstant(final PsiBuilder builder) { + final PsiBuilder.Marker constant = builder.mark(); + + parseModifierList(builder); + + if (expect(builder, JavaTokenType.IDENTIFIER)) { + if (builder.getTokenType() == JavaTokenType.LPARENTH) { + ExpressionParser.parseArgumentList(builder); + } + else { + emptyElement(builder, JavaElementType.EXPRESSION_LIST); + } + + if (builder.getTokenType() == JavaTokenType.LBRACE) { + final PsiBuilder.Marker constantInit = builder.mark(); + parseClassBodyWithBraces(builder, false, false); + done(constantInit, JavaElementType.ENUM_CONSTANT_INITIALIZER); + } + + done(constant, JavaElementType.ENUM_CONSTANT); + return constant; + } + else { + constant.rollbackTo(); + return null; + } + } + + private static void parseClassBodyDeclarations(final PsiBuilder builder, final boolean isAnnotation) { + final Context context = isAnnotation ? Context.ANNOTATION_INTERFACE : Context.CLASS; + + PsiBuilder.Marker invalidElements = null; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null || tokenType == JavaTokenType.RBRACE) break; + + if (tokenType == JavaTokenType.SEMICOLON) { + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("unexpected.token")); + invalidElements = null; + } + builder.advanceLexer(); + continue; + } + + final PsiBuilder.Marker declaration = parse(builder, context); + if (declaration != null) { + if (invalidElements != null) { + invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), declaration); + invalidElements = null; + } + continue; + } + + if (invalidElements == null) { + invalidElements = builder.mark(); + } + + // adding a reference, not simple tokens allows "Browse ..." to work well + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, true, false, false, false); + if (ref == null) { + builder.advanceLexer(); + } + } + + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("unexpected.token")); + } + } + + @Nullable + public static PsiBuilder.Marker parse(final PsiBuilder builder, final Context context) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null) return null; + + if (tokenType == JavaTokenType.LBRACE) { + if (context == Context.FILE || context == Context.CODE_BLOCK) return null; + } + else if (tokenType == JavaTokenType.IDENTIFIER || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { + if (context == Context.FILE) return null; + } + else if (tokenType instanceof ILazyParseableElementType) { + builder.advanceLexer(); + return null; + } + else if (!ElementType.MODIFIER_BIT_SET.contains(tokenType) && + !ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType) && + tokenType != JavaTokenType.AT && + (context == Context.CODE_BLOCK || tokenType != JavaTokenType.LT)) { + return null; + } + + final PsiBuilder.Marker declaration = builder.mark(); + final int declarationStart = builder.getCurrentOffset(); + + final Pair modListInfo = parseModifierList(builder); + final PsiBuilder.Marker modList = modListInfo.first; + + if (expect(builder, JavaTokenType.AT)) { + if (builder.getTokenType() == JavaTokenType.INTERFACE_KEYWORD) { + final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, true, context); + return result != null ? result : modList; + } + else { + declaration.rollbackTo(); + return null; + } + } + else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) { + final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, false, context); + return result != null ? result : modList; + } + + PsiBuilder.Marker typeParams = null; + if (builder.getTokenType() == JavaTokenType.LT) { + typeParams = ReferenceParser.parseTypeParameters(builder); + } + + if (context == Context.FILE) { + error(builder, JavaErrorMessages.message("expected.class.or.interface"), typeParams); + declaration.drop(); + return modList; + } + + PsiBuilder.Marker type; + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType())) { + type = parseTypeNotNull(builder); + } + else if (builder.getTokenType() == JavaTokenType.IDENTIFIER) { + final PsiBuilder.Marker idPos = builder.mark(); + type = parseTypeNotNull(builder); + if (builder.getTokenType() == JavaTokenType.LPARENTH) { // constructor + if (context == Context.CODE_BLOCK) { + declaration.rollbackTo(); + return null; + } + idPos.rollbackTo(); + if (typeParams == null) { + emptyElement(builder, JavaElementType.TYPE_PARAMETER_LIST); + } + builder.advanceLexer(); + if (builder.getTokenType() != JavaTokenType.LPARENTH) { + declaration.rollbackTo(); + return null; + } + return parseMethodFromLeftParenth(builder, declaration, false, true); + } + idPos.drop(); + } + else if (builder.getTokenType() == JavaTokenType.LBRACE) { + if (context == Context.CODE_BLOCK) { + error(builder, JavaErrorMessages.message("expected.identifier.or.type"), typeParams); + declaration.drop(); + return modList; + } + + final PsiBuilder.Marker codeBlock = StatementParser.parseCodeBlock(builder); + assert codeBlock != null : builder.getOriginalText(); + + if (typeParams != null) { + final PsiBuilder.Marker error = typeParams.precede(); + error.errorBefore(JavaErrorMessages.message("unexpected.token"), codeBlock); + } + done(declaration, JavaElementType.CLASS_INITIALIZER); + return declaration; + } + else { + final PsiBuilder.Marker error; + if (typeParams != null) { + error = typeParams.precede(); + } + else { + error = builder.mark(); + } + error.error(JavaErrorMessages.message("expected.identifier.or.type")); + declaration.drop(); + return modList; + } + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + if (context == Context.CODE_BLOCK && modListInfo.second) { + declaration.rollbackTo(); + return null; + } + else { + if (typeParams != null) { + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); + } + builder.error(JavaErrorMessages.message("expected.identifier")); + declaration.drop(); + return modList; + } + } + + if (builder.getTokenType() == JavaTokenType.LPARENTH) { + if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { // method + if (typeParams == null) { + emptyElement(type, JavaElementType.TYPE_PARAMETER_LIST); + } + return parseMethodFromLeftParenth(builder, declaration, (context == Context.ANNOTATION_INTERFACE), false); + } + } + + if (typeParams != null) { + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); + } + return parseFieldOrLocalVariable(builder, declaration, declarationStart, context); + } + + @NotNull + private static PsiBuilder.Marker parseTypeNotNull(final PsiBuilder builder) { + final PsiBuilder.Marker type = ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + assert type != null : builder.getOriginalText(); + return type; + } + + @NotNull + private static Pair parseModifierList(final PsiBuilder builder) { + return parseModifierList(builder, ElementType.MODIFIER_BIT_SET); + } + + @NotNull + public static Pair parseModifierList(final PsiBuilder builder, final TokenSet modifiers) { + final PsiBuilder.Marker modList = builder.mark(); + boolean isEmpty = true; + + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null) break; + if (modifiers.contains(tokenType)) { + builder.advanceLexer(); + isEmpty = false; + } + else if (tokenType == JavaTokenType.AT) { + if (ElementType.KEYWORD_BIT_SET.contains(nextTokenType(builder))) { + break; + } + parseAnnotation(builder); + isEmpty = false; + } + else { + break; + } + } + + done(modList, JavaElementType.MODIFIER_LIST); + return Pair.create(modList, isEmpty); + } + + private static PsiBuilder.Marker parseMethodFromLeftParenth(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final boolean anno, final boolean constructor) { + parseParameterList(builder); + + eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon")); + + if (areTypeAnnotationsSupported(builder)) { + final PsiBuilder.Marker receiver = builder.mark(); + final PsiBuilder.Marker annotations = parseAnnotations(builder); + if (annotations != null) { + done(receiver, JavaElementType.METHOD_RECEIVER); + } + else { + receiver.drop(); + } + } + + ReferenceParser.parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA); + + if (anno && expect(builder, JavaTokenType.DEFAULT_KEYWORD)) { + parseAnnotationValue(builder); + } + + final IElementType tokenType = builder.getTokenType(); + if (tokenType != JavaTokenType.SEMICOLON && tokenType != JavaTokenType.LBRACE) { + final PsiBuilder.Marker error = builder.mark(); + // heuristic: going to next line obviously means method signature is over, starting new method (actually, another one completion hack) + final CharSequence text = builder.getOriginalText(); + Loop: + while (true) { + for (int i = builder.getCurrentOffset() - 1; i >= 0; i--) { + final char ch = text.charAt(i); + if (ch == '\n') break Loop; + else if (ch != ' ' && ch != '\t') break; + } + if (!expect(builder, APPEND_TO_METHOD_SET)) break; + } + error.error(JavaErrorMessages.message("expected.lbrace.or.semicolon")); + } + + if (!expect(builder, JavaTokenType.SEMICOLON)) { + if (builder.getTokenType() == JavaTokenType.LBRACE) { + StatementParser.parseCodeBlock(builder); + } + } + + done(declaration, anno ? JavaElementType.ANNOTATION_METHOD : JavaElementType.METHOD); + return declaration; + } + + @NotNull + public static PsiBuilder.Marker parseParameterList(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.LPARENTH : builder.getTokenType(); + final PsiBuilder.Marker paramList = builder.mark(); + builder.advanceLexer(); + + PsiBuilder.Marker invalidElements = null; + String errorMessage = null; + boolean commaExpected = false; + int paramCount = 0; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null || tokenType == JavaTokenType.RPARENTH) { + boolean noLastParam = !commaExpected && paramCount > 0; + if (noLastParam) { + error(builder, JavaErrorMessages.message("expected.identifier.or.type")); + } + if (tokenType == JavaTokenType.RPARENTH) { + if (invalidElements != null) { + invalidElements.error(errorMessage); + } + invalidElements = null; + builder.advanceLexer(); + } + else { + if (!noLastParam) { + if (invalidElements != null) { + invalidElements.error(errorMessage); + } + invalidElements = null; + error(builder, JavaErrorMessages.message("expected.rparen")); + } + } + break; + } + + if (commaExpected) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + commaExpected = false; + if (invalidElements != null) { + invalidElements.error(errorMessage); + invalidElements = null; + } + builder.advanceLexer(); + continue; + } + } + else { + final PsiBuilder.Marker param = parseParameter(builder, true); + if (param != null) { + commaExpected = true; + if (invalidElements != null) { + invalidElements.errorBefore(errorMessage, param); + invalidElements = null; + } + paramCount++; + continue; + } + } + + if (invalidElements == null) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + error(builder, JavaErrorMessages.message("expected.parameter")); + builder.advanceLexer(); + continue; + } + else { + invalidElements = builder.mark(); + errorMessage = commaExpected ? JavaErrorMessages.message("expected.comma") : JavaErrorMessages.message("expected.parameter"); + } + } + + // adding a reference, not simple tokens allows "Browse .." to work well + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, true, false, false, false); + if (ref == null && builder.getTokenType() != null) { + builder.advanceLexer(); + } + } + + if (invalidElements != null) { + invalidElements.error(errorMessage); + } + + done(paramList, JavaElementType.PARAMETER_LIST); + return paramList; + } + + @Nullable + public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis) { + final PsiBuilder.Marker param = builder.mark(); + + final Pair modListInfo = parseModifierList(builder); + + int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; + if (ellipsis) flags |= ReferenceParser.ELLIPSIS; + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); + + if (typeInfo == null && modListInfo.second) { + param.rollbackTo(); + return null; + } + + if (typeInfo == null) { + error(builder, JavaErrorMessages.message("expected.type")); + emptyElement(builder, JavaElementType.TYPE); + } + + if (expect(builder, JavaTokenType.IDENTIFIER)) { + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); + done(param, JavaElementType.PARAMETER); + return param; + } + else { + error(builder, JavaErrorMessages.message("expected.identifier")); + param.drop(); + return modListInfo.first; + } + } + + @Nullable + private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final int declarationStart, final Context context) { + final IElementType varType; + if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { + varType = JavaElementType.FIELD; + } + else if (context == Context.CODE_BLOCK) { + varType = JavaElementType.LOCAL_VARIABLE; + } + else { + declaration.drop(); + assert false : "Unexpected context: " + context; + return null; + } + + PsiBuilder.Marker variable = declaration; + boolean unclosed = false; + boolean eatSemicolon = true; + boolean shouldRollback; + boolean openMarker = true; + while (true) { + shouldRollback = true; + + if (!eatBrackets(builder, false, null)) { + unclosed = true; + } + + if (expect(builder, JavaTokenType.EQ)) { + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr != null) { + shouldRollback = false; + } + else { + error(builder, JavaErrorMessages.message("expected.expression")); + unclosed = true; + break; + } + } + + if (builder.getTokenType() != JavaTokenType.COMMA) break; + done(variable, varType); + builder.advanceLexer(); + + if (builder.getTokenType() != JavaTokenType.IDENTIFIER) { + error(builder, JavaErrorMessages.message("expected.identifier")); + unclosed = true; + eatSemicolon = false; + openMarker = false; + break; + } + + variable = builder.mark(); + builder.advanceLexer(); + } + + if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) { + builder.advanceLexer(); + } + else { + // special treatment (see DeclarationParserTest.testMultiLineUnclosed()) + if (!builder.eof() && shouldRollback) { + final CharSequence text = builder.getOriginalText(); + final int spaceEnd = builder.getCurrentOffset(); + final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES); + final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS); + + if (declarationStart < lineStart && lineStart < spaceStart) { + final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES); + declaration.rollbackTo(); + return parse(stoppingBuilder(builder, newBufferEnd), context); + } + } + + if (!unclosed) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + } + } + + if (openMarker) { + done(variable, varType); + } + + return declaration; + } + + private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) { + if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; + + final PsiBuilder.Marker marker = isError ? builder.mark() : null; + + boolean result = true; + while (expect(builder, JavaTokenType.LBRACKET)) { + if (!expect(builder, JavaTokenType.RBRACKET)) { + if (!isError) error(builder, JavaErrorMessages.message("expected.rbracket")); + result = false; + break; + } + } + + if (marker != null) { + marker.error(error); + } + + return result; + } + + @Nullable + public static PsiBuilder.Marker parseAnnotations(final PsiBuilder builder) { + PsiBuilder.Marker firstAnno = null; + + while (builder.getTokenType() == JavaTokenType.AT) { + final PsiBuilder.Marker anno = parseAnnotation(builder); + if (firstAnno == null) firstAnno = anno; + } + + return firstAnno; + } + + @NotNull + public static PsiBuilder.Marker parseAnnotation(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.AT : builder.getTokenType(); + final PsiBuilder.Marker anno = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker classRef = ReferenceParser.parseJavaCodeReference(builder, true, false, false, false, false); + if (classRef == null) { + error(builder, JavaErrorMessages.message("expected.class.reference")); + } + + parseAnnotationParameterList(builder); + + done(anno, JavaElementType.ANNOTATION); + return anno; + } + + @NotNull + private static PsiBuilder.Marker parseAnnotationParameterList(final PsiBuilder builder) { + PsiBuilder.Marker list = builder.mark(); + + if (!expect(builder, JavaTokenType.LPARENTH)) { + done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); + return list; + } + + if (expect(builder, JavaTokenType.RPARENTH)) { + done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); + return list; + } + + final boolean isFirstParamNamed = parseAnnotationParameter(builder, true); + boolean isFirstParamWarned = false; + + boolean afterBad = false; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null) { + error(builder, JavaErrorMessages.message("expected.parameter")); + break; + } + else if (expect(builder, JavaTokenType.RPARENTH)) { + break; + } + else if (tokenType == JavaTokenType.COMMA) { + final PsiBuilder.Marker errorStart = builder.mark(); + final PsiBuilder.Marker errorEnd = builder.mark(); + builder.advanceLexer(); + final boolean hasParamName = parseAnnotationParameter(builder, false); + if (!isFirstParamNamed && hasParamName && !isFirstParamWarned) { + errorStart.errorBefore(JavaErrorMessages.message("annotation.name.is.missing"), errorEnd); + isFirstParamWarned = true; + } + else { + errorStart.drop(); + } + errorEnd.drop(); + } + else if (!afterBad) { + error(builder, JavaErrorMessages.message("expected.comma.or.rparen")); + builder.advanceLexer(); + afterBad = true; + } + else { + afterBad = false; + parseAnnotationParameter(builder, false); + } + } + + done(list, JavaElementType.ANNOTATION_PARAMETER_LIST); + return list; + } + + private static boolean parseAnnotationParameter(final PsiBuilder builder, final boolean mayBeSimple) { + PsiBuilder.Marker pair = builder.mark(); + + if (mayBeSimple) { + parseAnnotationValue(builder); + if (builder.getTokenType() != JavaTokenType.EQ) { + done(pair, JavaElementType.NAME_VALUE_PAIR); + return false; + } + + pair.rollbackTo(); + pair = builder.mark(); + } + + final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier")); + + expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq")); + + parseAnnotationValue(builder); + + done(pair, JavaElementType.NAME_VALUE_PAIR); + + return hasName; + } + + @NotNull + private static PsiBuilder.Marker parseAnnotationValue(final PsiBuilder builder) { + PsiBuilder.Marker result; + + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.AT) { + result = parseAnnotation(builder); + } + else if (tokenType == JavaTokenType.LBRACE) { + result = parseAnnotationArrayInitializer(builder); + } + else { + result = ExpressionParser.parseConditional(builder); + } + + if (result == null) { + result = builder.mark(); + result.error(JavaErrorMessages.message("expected.value")); + } + + return result; + } + + @NotNull + private static PsiBuilder.Marker parseAnnotationArrayInitializer(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); + final PsiBuilder.Marker annoArray = builder.mark(); + builder.advanceLexer(); + + if (expect(builder, JavaTokenType.RBRACE)) { + done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER); + return annoArray; + } + + parseAnnotationValue(builder); + + boolean unclosed = false; + while (true) { + if (expect(builder, JavaTokenType.RBRACE)) { + break; + } + else if (expect(builder, JavaTokenType.COMMA)) { + if (builder.getTokenType() != JavaTokenType.RBRACE) { + parseAnnotationValue(builder); + } + } + else { + error(builder, JavaErrorMessages.message("expected.rbrace")); + unclosed = true; + break; + } + } + + done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER); + if (unclosed) { + annoArray.setCustomEdgeTokenBinders(null, GREEDY_RIGHT_EDGE_PROCESSOR); + } + return annoArray; + } +} diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 67f273a8d7da..d48486858153 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -1,860 +1,860 @@ -/* - * Copyright 2000-2010 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 com.intellij.lang.java.parser; - -import com.intellij.codeInsight.daemon.JavaErrorMessages; -import com.intellij.lang.PsiBuilder; -import com.intellij.lang.PsiBuilderUtil; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.TokenType; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.impl.source.tree.JavaElementType; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.TokenSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.intellij.lang.PsiBuilderUtil.expect; -import static com.intellij.lang.java.parser.JavaParserUtil.*; - - -public class ExpressionParser { - private enum ExprType { - CONDITIONAL_OR, CONDITIONAL_AND, OR, XOR, AND, EQUALITY, RELATIONAL, SHIFT, ADDITIVE, MULTIPLICATIVE, UNARY, TYPE - } - - private static final TokenSet ASSIGNMENT_OPS = TokenSet.create( - JavaTokenType.EQ, JavaTokenType.ASTERISKEQ, JavaTokenType.DIVEQ, JavaTokenType.PERCEQ, JavaTokenType.PLUSEQ, JavaTokenType.MINUSEQ, - JavaTokenType.LTLTEQ, JavaTokenType.GTGTEQ, JavaTokenType.GTGTGTEQ, JavaTokenType.ANDEQ, JavaTokenType.OREQ, JavaTokenType.XOREQ); - private static final TokenSet RELATIONAL_OPS = TokenSet.create(JavaTokenType.LT, JavaTokenType.GT, JavaTokenType.LE, JavaTokenType.GE); - private static final TokenSet POSTFIX_OPS = TokenSet.create(JavaTokenType.PLUSPLUS, JavaTokenType.MINUSMINUS); - private static final TokenSet PREF_ARITHMETIC_OPS = TokenSet.orSet(POSTFIX_OPS, TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS)); - private static final TokenSet PREFIX_OPS = TokenSet.orSet(PREF_ARITHMETIC_OPS, TokenSet.create(JavaTokenType.TILDE, JavaTokenType.EXCL)); - private static final TokenSet LITERALS = TokenSet.create( - JavaTokenType.TRUE_KEYWORD, JavaTokenType.FALSE_KEYWORD, JavaTokenType.NULL_KEYWORD, JavaTokenType.INTEGER_LITERAL, - JavaTokenType.LONG_LITERAL, JavaTokenType.FLOAT_LITERAL, JavaTokenType.DOUBLE_LITERAL, JavaTokenType.CHARACTER_LITERAL, - JavaTokenType.STRING_LITERAL); - private static final TokenSet CONDITIONAL_OR_OPS = TokenSet.create(JavaTokenType.OROR); - private static final TokenSet CONDITIONAL_AND_OPS = TokenSet.create(JavaTokenType.ANDAND); - private static final TokenSet OR_OPS = TokenSet.create(JavaTokenType.OR); - private static final TokenSet XOR_OPS = TokenSet.create(JavaTokenType.XOR); - private static final TokenSet AND_OPS = TokenSet.create(JavaTokenType.AND); - private static final TokenSet EQUALITY_OPS = TokenSet.create(JavaTokenType.EQEQ, JavaTokenType.NE); - private static final TokenSet SHIFT_OPS = TokenSet.create(JavaTokenType.LTLT, JavaTokenType.GTGT, JavaTokenType.GTGTGT); - private static final TokenSet ADDITIVE_OPS = TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS); - private static final TokenSet MULTIPLICATIVE_OPS = TokenSet.create(JavaTokenType.ASTERISK, JavaTokenType.DIV, JavaTokenType.PERC); - private static final TokenSet ARGS_LIST_END = TokenSet.create(JavaTokenType.RPARENTH, JavaTokenType.RBRACE, JavaTokenType.RBRACKET); - private static final TokenSet ARGS_LIST_CONTINUE = TokenSet.create( - JavaTokenType.IDENTIFIER, TokenType.BAD_CHARACTER, JavaTokenType.COMMA, JavaTokenType.INTEGER_LITERAL, JavaTokenType.STRING_LITERAL); - private static final TokenSet CONSTRUCTOR_CALL = TokenSet.create(JavaTokenType.THIS_KEYWORD, JavaTokenType.SUPER_KEYWORD); - - private ExpressionParser() { } - - @Nullable - public static PsiBuilder.Marker parse(final PsiBuilder builder) { - return parseAssignment(builder); - } - - @Nullable - private static PsiBuilder.Marker parseAssignment(final PsiBuilder builder) { - final PsiBuilder.Marker left = parseConditional(builder); - if (left == null) return null; - - final IElementType tokenType = getGtTokenType(builder); - if (ASSIGNMENT_OPS.contains(tokenType)) { - final PsiBuilder.Marker assignment = left.precede(); - advanceGtToken(builder, tokenType); - - final PsiBuilder.Marker right = parse(builder); - if (right == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - } - - assignment.done(JavaElementType.ASSIGNMENT_EXPRESSION); - return assignment; - } - - return left; - } - - @Nullable - public static PsiBuilder.Marker parseConditional(final PsiBuilder builder) { - final PsiBuilder.Marker condition = parseExpression(builder, ExprType.CONDITIONAL_OR); - if (condition == null) return null; - - if (builder.getTokenType() != JavaTokenType.QUEST) return condition; - final PsiBuilder.Marker ternary = condition.precede(); - builder.advanceLexer(); - - final PsiBuilder.Marker truePart = parse(builder); - if (truePart == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); - return ternary; - } - - if (builder.getTokenType() != JavaTokenType.COLON) { - error(builder, JavaErrorMessages.message("expected.colon")); - ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); - return ternary; - } - builder.advanceLexer(); - - final PsiBuilder.Marker falsePart = parseConditional(builder); - if (falsePart == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); - return ternary; - } - - ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); - return ternary; - } - - @Nullable - private static PsiBuilder.Marker parseExpression(final PsiBuilder builder, final ExprType type) { - switch (type) { - case CONDITIONAL_OR: - return parseBinary(builder, ExprType.CONDITIONAL_AND, CONDITIONAL_OR_OPS); - - case CONDITIONAL_AND: - return parseBinary(builder, ExprType.OR, CONDITIONAL_AND_OPS); - - case OR: - return parseBinary(builder, ExprType.XOR, OR_OPS); - - case XOR: - return parseBinary(builder, ExprType.AND, XOR_OPS); - - case AND: - return parseBinary(builder, ExprType.EQUALITY, AND_OPS); - - case EQUALITY: - return parseBinary(builder, ExprType.RELATIONAL, EQUALITY_OPS); - - case RELATIONAL: - return parseRelational(builder); - - case SHIFT: - return parseBinary(builder, ExprType.ADDITIVE, SHIFT_OPS); - - case ADDITIVE: - return parseBinary(builder, ExprType.MULTIPLICATIVE, ADDITIVE_OPS); - - case MULTIPLICATIVE: - return parseBinary(builder, ExprType.UNARY, MULTIPLICATIVE_OPS); - - case UNARY: - return parseUnary(builder); - - case TYPE: - return ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); - - default: - assert false : "Unexpected type: " + type; - return null; - } - } - - @Nullable - private static PsiBuilder.Marker parseBinary(final PsiBuilder builder, final ExprType type, final TokenSet ops) { - PsiBuilder.Marker left = parseExpression(builder, type); - if (left == null) return null; - - while (true) { - final IElementType tokenType = getGtTokenType(builder); - if (tokenType == null || !ops.contains(tokenType)) break; - - final PsiBuilder.Marker binary = left.precede(); - advanceGtToken(builder, tokenType); - - final PsiBuilder.Marker right = parseExpression(builder, type); - if (right == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - binary.done(JavaElementType.BINARY_EXPRESSION); - return binary; - } - - binary.done(JavaElementType.BINARY_EXPRESSION); - left = binary; - } - - return left; - } - - @Nullable - private static PsiBuilder.Marker parseRelational(final PsiBuilder builder) { - PsiBuilder.Marker left = parseExpression(builder, ExprType.SHIFT); - if (left == null) return null; - - while (true) { - final IElementType toCreate; - final ExprType toParse; - final IElementType tokenType = getGtTokenType(builder); - if (RELATIONAL_OPS.contains(tokenType)) { - toCreate = JavaElementType.BINARY_EXPRESSION; - toParse = ExprType.SHIFT; - } - else if (tokenType == JavaTokenType.INSTANCEOF_KEYWORD) { - toCreate = JavaElementType.INSTANCE_OF_EXPRESSION; - toParse = ExprType.TYPE; - } - else { - break; - } - - final PsiBuilder.Marker expression = left.precede(); - advanceGtToken(builder, tokenType); - - final PsiBuilder.Marker right = parseExpression(builder, toParse); - if (right == null) { - error(builder, toParse == ExprType.TYPE ? - JavaErrorMessages.message("expected.type") : JavaErrorMessages.message("expected.expression")); - expression.done(toCreate); - return expression; - } - - expression.done(toCreate); - left = expression; - } - - return left; - } - - @Nullable - private static PsiBuilder.Marker parseUnary(final PsiBuilder builder) { - final IElementType tokenType = builder.getTokenType(); - - if (PREFIX_OPS.contains(tokenType)) { - final PsiBuilder.Marker unary = builder.mark(); - builder.advanceLexer(); - - final PsiBuilder.Marker operand = parseUnary(builder); - if (operand == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - } - - unary.done(JavaElementType.PREFIX_EXPRESSION); - return unary; - } - else if (tokenType == JavaTokenType.LPARENTH) { - final PsiBuilder.Marker typeCast = builder.mark(); - builder.advanceLexer(); - - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); - - if (typeInfo == null || builder.getTokenType() != JavaTokenType.RPARENTH) { - typeCast.rollbackTo(); - return parsePostfix(builder); - } - builder.advanceLexer(); - - if (PREF_ARITHMETIC_OPS.contains(builder.getTokenType())) { - if (!typeInfo.isPrimitive) { - typeCast.rollbackTo(); - return parsePostfix(builder); - } - } - - final PsiBuilder.Marker expr = parseUnary(builder); - if (expr == null) { - if (!typeInfo.isParameterized) { // cannot parse correct parenthesized expression after correct parameterized type - typeCast.rollbackTo(); - return parsePostfix(builder); - } - else { - error(builder, JavaErrorMessages.message("expected.expression")); - } - } - - typeCast.done(JavaElementType.TYPE_CAST_EXPRESSION); - return typeCast; - } - else { - return parsePostfix(builder); - } - } - - @Nullable - private static PsiBuilder.Marker parsePostfix(final PsiBuilder builder) { - PsiBuilder.Marker operand = parsePrimary(builder, null, -1); - if (operand == null) return null; - - while (POSTFIX_OPS.contains(builder.getTokenType())) { - final PsiBuilder.Marker postfix = operand.precede(); - builder.advanceLexer(); - postfix.done(JavaElementType.POSTFIX_EXPRESSION); - operand = postfix; - } - - return operand; - } - - private enum BreakPoint {P1, P2, P3, P4} - - @Nullable - private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint, final int breakOffset) { - PsiBuilder.Marker startMarker = builder.mark(); - - PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder); - if (expr == null) { - startMarker.drop(); - return null; - } - - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.DOT) { - final PsiBuilder.Marker dotPos = builder.mark(); - builder.advanceLexer(); - - final IElementType dotTokenType = builder.getTokenType(); - if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { - if (breakPoint == BreakPoint.P1 && builder.getCurrentOffset() == breakOffset) { - error(builder, JavaErrorMessages.message("expected.identifier")); - PsiBuilderUtil.drop(startMarker, dotPos); - return expr; - } - - final PsiBuilder.Marker copy = startMarker.precede(); - final int offset = builder.getCurrentOffset(); - startMarker.rollbackTo(); - - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); - if (classObjAccess == null || builder.getCurrentOffset() < offset) { - copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P1, offset); - } - - startMarker = copy; - expr = classObjAccess; - } - else if (dotTokenType == JavaTokenType.NEW_KEYWORD) { - dotPos.drop(); - expr = parseNew(builder, expr); - } - else if ((dotTokenType == JavaTokenType.THIS_KEYWORD || dotTokenType == JavaTokenType.SUPER_KEYWORD) && - exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { - if (breakPoint == BreakPoint.P2) { - dotPos.rollbackTo(); - startMarker.drop(); - return expr; - } - - final PsiBuilder.Marker copy = startMarker.precede(); - startMarker.rollbackTo(); - - final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, false, true, false, false); - if (ref == null || builder.getTokenType() != JavaTokenType.DOT) { - copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2, -1); - } - builder.advanceLexer(); - - if (builder.getTokenType() != dotTokenType) { - copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2, -1); - } - builder.advanceLexer(); - - startMarker = copy; - expr = ref.precede(); - expr.done(dotTokenType == JavaTokenType.THIS_KEYWORD ? JavaElementType.THIS_EXPRESSION : JavaElementType.SUPER_EXPRESSION); - } - else if (dotTokenType == JavaTokenType.SUPER_KEYWORD) { - dotPos.drop(); - final PsiBuilder.Marker refExpr = expr.precede(); - builder.advanceLexer(); - refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - expr = refExpr; - } - else { - dotPos.drop(); - final PsiBuilder.Marker refExpr = expr.precede(); - ReferenceParser.parseReferenceParameterList(builder, false, false); - - if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier"))) { - refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - startMarker.drop(); - return refExpr; - } - - refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - expr = refExpr; - } - } - else if (tokenType == JavaTokenType.LPARENTH) { - if (exprType(expr) != JavaElementType.REFERENCE_EXPRESSION) { - if (exprType(expr) == JavaElementType.SUPER_EXPRESSION) { - if (breakPoint == BreakPoint.P3) { - startMarker.drop(); - return expr; - } - - final PsiBuilder.Marker copy = startMarker.precede(); - startMarker.rollbackTo(); - - final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder); - if (qualifier != null) { - final PsiBuilder.Marker refExpr = qualifier.precede(); - if (builder.getTokenType() == JavaTokenType.DOT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.SUPER_KEYWORD) { - builder.advanceLexer(); - refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - expr = refExpr; - startMarker = copy; - continue; - } - } - } - - copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P3, -1); - } - else { - startMarker.drop(); - return expr; - } - } - - final PsiBuilder.Marker callExpr = expr.precede(); - parseArgumentList(builder); - callExpr.done(JavaElementType.METHOD_CALL_EXPRESSION); - expr = callExpr; - } - else if (tokenType == JavaTokenType.LBRACKET) { - if (breakPoint == BreakPoint.P4) { - startMarker.drop(); - return expr; - } - - builder.advanceLexer(); - - if (builder.getTokenType() == JavaTokenType.RBRACKET && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { - final int pos = builder.getCurrentOffset(); - final PsiBuilder.Marker copy = startMarker.precede(); - startMarker.rollbackTo(); - - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); - if (classObjAccess == null || builder.getCurrentOffset() <= pos) { - copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P4, -1); - } - - startMarker = copy; - expr = classObjAccess; - } - else { - final PsiBuilder.Marker arrayAccess = expr.precede(); - - final PsiBuilder.Marker index = parse(builder); - if (index == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); - startMarker.drop(); - return arrayAccess; - } - - if (builder.getTokenType() != JavaTokenType.RBRACKET) { - error(builder, JavaErrorMessages.message("expected.rbracket")); - arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); - startMarker.drop(); - return arrayAccess; - } - builder.advanceLexer(); - - arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); - expr = arrayAccess; - } - } - else { - startMarker.drop(); - return expr; - } - } - } - - @Nullable - private static PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder) { - IElementType tokenType = builder.getTokenType(); - - if (LITERALS.contains(tokenType)) { - final PsiBuilder.Marker literal = builder.mark(); - builder.advanceLexer(); - literal.done(JavaElementType.LITERAL_EXPRESSION); - return literal; - } - if (tokenType == JavaTokenType.LPARENTH) { - final PsiBuilder.Marker parenth = builder.mark(); - builder.advanceLexer(); - - final PsiBuilder.Marker inner = parse(builder); - if (inner == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - } - - if (!expect(builder, JavaTokenType.RPARENTH)) { - if (inner != null) { - error(builder, JavaErrorMessages.message("expected.rparen")); - } - } - - parenth.done(JavaElementType.PARENTH_EXPRESSION); - return parenth; - } - if (tokenType == JavaTokenType.LBRACE) { - return parseArrayInitializer(builder); - } - - PsiBuilder.Marker annotation = null; - final PsiBuilder.Marker beforeAnnotation = builder.mark(); - if (tokenType == JavaTokenType.AT) { - annotation = DeclarationParser.parseAnnotations(builder); - tokenType = builder.getTokenType(); - } - - if (tokenType == JavaTokenType.IDENTIFIER) { - final PsiBuilder.Marker refExpr; - if (annotation != null) { - final PsiBuilder.Marker refParam = annotation.precede(); - refParam.doneBefore(JavaElementType.REFERENCE_PARAMETER_LIST, annotation); - refExpr = refParam.precede(); - } - else { - refExpr = builder.mark(); - builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); - } - - builder.advanceLexer(); - refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - beforeAnnotation.drop(); - return refExpr; - } - - if (annotation != null) { - beforeAnnotation.rollbackTo(); - tokenType = builder.getTokenType(); - } - else { - beforeAnnotation.drop(); - } - - PsiBuilder.Marker expr = null; - if (tokenType == JavaTokenType.LT) { - expr = builder.mark(); - - if (!ReferenceParser.parseReferenceParameterList(builder, false, false)) { - expr.rollbackTo(); - return null; - } - - tokenType = builder.getTokenType(); - if (!CONSTRUCTOR_CALL.contains(tokenType)) { - expr.rollbackTo(); - return null; - } - } - - if (CONSTRUCTOR_CALL.contains(tokenType)) { - if (expr == null) { - expr = builder.mark(); - builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); - } - builder.advanceLexer(); - expr.done(builder.getTokenType() == JavaTokenType.LPARENTH - ? JavaElementType.REFERENCE_EXPRESSION - : tokenType == JavaTokenType.THIS_KEYWORD - ? JavaElementType.THIS_EXPRESSION - : JavaElementType.SUPER_EXPRESSION); - return expr; - } - if (tokenType == JavaTokenType.NEW_KEYWORD) { - return parseNew(builder, null); - } - if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - return parseClassObjectAccess(builder); - } - - return null; - } - - @Nullable - private static PsiBuilder.Marker parseArrayInitializer(final PsiBuilder builder) { - if (builder.getTokenType() != JavaTokenType.LBRACE) return null; - - final PsiBuilder.Marker arrayInit = builder.mark(); - builder.advanceLexer(); - - boolean expressionMissed = false; - PsiBuilder.Marker lastComma = null; - while (true) { - if (builder.getTokenType() == JavaTokenType.RBRACE) { - builder.advanceLexer(); - break; - } - - if (builder.getTokenType() == null) { - error(builder, JavaErrorMessages.message("expected.rbrace")); - break; - } - - if (expressionMissed && lastComma != null) { - // before comma must be an expression - lastComma.precede().errorBefore(JavaErrorMessages.message("expected.expression"), lastComma); - lastComma.drop(); - lastComma = null; - } - - final PsiBuilder.Marker arg = parse(builder); - if (arg == null) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - expressionMissed = true; - lastComma = builder.mark(); - } - else { - error(builder, JavaErrorMessages.message("expected.rbrace")); - break; - } - } - - final IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.COMMA) { - builder.advanceLexer(); - } - else if (tokenType != JavaTokenType.RBRACE) { - error(builder, JavaErrorMessages.message("expected.comma")); - } - } - - if (lastComma != null) { - lastComma.drop(); - } - - arrayInit.done(JavaElementType.ARRAY_INITIALIZER_EXPRESSION); - return arrayInit; - } - - @NotNull - private static PsiBuilder.Marker parseNew(final PsiBuilder builder, final PsiBuilder.Marker start) { - final PsiBuilder.Marker newExpr = (start != null ? start.precede() : builder.mark()); - builder.advanceLexer(); - - final boolean parseDiamonds = areDiamondsSupported(builder); - ReferenceParser.parseReferenceParameterList(builder, false, parseDiamonds); - - final PsiBuilder.Marker refOrType; - final boolean parseAnnotations = areTypeAnnotationsSupported(builder) && builder.getTokenType() == JavaTokenType.AT; - - final IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.IDENTIFIER || parseAnnotations) { - refOrType = ReferenceParser.parseJavaCodeReference(builder, true, true, parseAnnotations, parseDiamonds); - if (refOrType == null) { - error(builder, JavaErrorMessages.message("expected.identifier")); - newExpr.done(JavaElementType.NEW_EXPRESSION); - return newExpr; - } - } - else if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - refOrType = null; - builder.advanceLexer(); - } - else { - error(builder, JavaErrorMessages.message("expected.identifier")); - newExpr.done(JavaElementType.NEW_EXPRESSION); - return newExpr; - } - - if (refOrType != null && builder.getTokenType() == JavaTokenType.LPARENTH) { - parseArgumentList(builder); - if (builder.getTokenType() == JavaTokenType.LBRACE) { - final PsiBuilder.Marker classElement = refOrType.precede(); - DeclarationParser.parseClassBodyWithBraces(builder, false, false); - classElement.done(JavaElementType.ANONYMOUS_CLASS); - } - } - else { - if (builder.getTokenType() != JavaTokenType.LBRACKET) { - error(builder, refOrType == null ? - JavaErrorMessages.message("expected.lbracket") : JavaErrorMessages.message("expected.lparen.or.lbracket")); - newExpr.done(JavaElementType.NEW_EXPRESSION); - return newExpr; - } - - int bracketCount = 0; - int dimCount = 0; - while (true) { - if (builder.getTokenType() != JavaTokenType.LBRACKET) break; - builder.advanceLexer(); - - if (bracketCount == dimCount) { - final PsiBuilder.Marker dimExpr = parse(builder); - if (dimExpr != null) { - dimCount++; - } - } - bracketCount++; - - if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, JavaErrorMessages.message("expected.rbracket"))) { - newExpr.done(JavaElementType.NEW_EXPRESSION); - return newExpr; - } - } - - if (dimCount == 0) { - if (builder.getTokenType() == JavaTokenType.LBRACE) { - parseArrayInitializer(builder); - } - else { - error(builder, JavaErrorMessages.message("expected.array.initializer")); - } - } - } - - newExpr.done(JavaElementType.NEW_EXPRESSION); - return newExpr; - } - - @Nullable - private static PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { - final PsiBuilder.Marker expr = builder.mark(); - - if (ReferenceParser.parseType(builder, 0) == null) { - expr.drop(); - return null; - } - - if (builder.getTokenType() != JavaTokenType.DOT) { - expr.rollbackTo(); - return null; - } - builder.advanceLexer(); - - if (builder.getTokenType() != JavaTokenType.CLASS_KEYWORD) { - expr.rollbackTo(); - return null; - } - builder.advanceLexer(); - - expr.done(JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION); - return expr; - } - - @NotNull - public static PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) { - final PsiBuilder.Marker list = builder.mark(); - builder.advanceLexer(); - - boolean first = true; - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (first && (ARGS_LIST_END.contains(tokenType) || builder.eof())) break; - if (!first && !ARGS_LIST_CONTINUE.contains(tokenType)) break; - - boolean hasError = false; - if (!first) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - builder.advanceLexer(); - } - else { - hasError = true; - error(builder, JavaErrorMessages.message("expected.comma.or.rparen")); - emptyExpression(builder); - } - } - first = false; - - final PsiBuilder.Marker arg = parse(builder); - if (arg == null) { - if (!hasError) { - error(builder, JavaErrorMessages.message("expected.expression")); - emptyExpression(builder); - } - if (!ARGS_LIST_CONTINUE.contains(builder.getTokenType())) break; - if (builder.getTokenType() != JavaTokenType.COMMA && !builder.eof()) { - builder.advanceLexer(); - } - } - } - - final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, JavaErrorMessages.message("expected.rparen")); - - list.done(JavaElementType.EXPRESSION_LIST); - if (!closed) { - list.setCustomEdgeTokenBinders(null, GREEDY_RIGHT_EDGE_PROCESSOR); - } - return list; - } - - private static void emptyExpression(final PsiBuilder builder) { - emptyElement(builder, JavaElementType.EMPTY_EXPRESSION); - } - - @Nullable - private static IElementType getGtTokenType(final PsiBuilder builder) { - final PsiBuilder.Marker sp = builder.mark(); - - IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GTGTGTEQ; - } - else { - tokenType = JavaTokenType.GTGTGT; - } - } - else if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GTGTEQ; - } - else { - tokenType = JavaTokenType.GTGT; - } - } - else if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GE; - } - } - - sp.rollbackTo(); - return tokenType; - } - - private static void advanceGtToken(final PsiBuilder builder, final IElementType type) { - final PsiBuilder.Marker gtToken = builder.mark(); - - if (type == JavaTokenType.GTGTGTEQ) { - PsiBuilderUtil.advance(builder, 4); - } - else if (type == JavaTokenType.GTGTGT || type == JavaTokenType.GTGTEQ) { - PsiBuilderUtil.advance(builder, 3); - } - else if (type == JavaTokenType.GTGT || type == JavaTokenType.GE) { - PsiBuilderUtil.advance(builder, 2); - } - else { - gtToken.drop(); - builder.advanceLexer(); - return; - } - - gtToken.collapse(type); - } -} +/* + * Copyright 2000-2010 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 com.intellij.lang.java.parser; + +import com.intellij.codeInsight.daemon.JavaErrorMessages; +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilderUtil; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.TokenType; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.lang.PsiBuilderUtil.expect; +import static com.intellij.lang.java.parser.JavaParserUtil.*; + + +public class ExpressionParser { + private enum ExprType { + CONDITIONAL_OR, CONDITIONAL_AND, OR, XOR, AND, EQUALITY, RELATIONAL, SHIFT, ADDITIVE, MULTIPLICATIVE, UNARY, TYPE + } + + private static final TokenSet ASSIGNMENT_OPS = TokenSet.create( + JavaTokenType.EQ, JavaTokenType.ASTERISKEQ, JavaTokenType.DIVEQ, JavaTokenType.PERCEQ, JavaTokenType.PLUSEQ, JavaTokenType.MINUSEQ, + JavaTokenType.LTLTEQ, JavaTokenType.GTGTEQ, JavaTokenType.GTGTGTEQ, JavaTokenType.ANDEQ, JavaTokenType.OREQ, JavaTokenType.XOREQ); + private static final TokenSet RELATIONAL_OPS = TokenSet.create(JavaTokenType.LT, JavaTokenType.GT, JavaTokenType.LE, JavaTokenType.GE); + private static final TokenSet POSTFIX_OPS = TokenSet.create(JavaTokenType.PLUSPLUS, JavaTokenType.MINUSMINUS); + private static final TokenSet PREF_ARITHMETIC_OPS = TokenSet.orSet(POSTFIX_OPS, TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS)); + private static final TokenSet PREFIX_OPS = TokenSet.orSet(PREF_ARITHMETIC_OPS, TokenSet.create(JavaTokenType.TILDE, JavaTokenType.EXCL)); + private static final TokenSet LITERALS = TokenSet.create( + JavaTokenType.TRUE_KEYWORD, JavaTokenType.FALSE_KEYWORD, JavaTokenType.NULL_KEYWORD, JavaTokenType.INTEGER_LITERAL, + JavaTokenType.LONG_LITERAL, JavaTokenType.FLOAT_LITERAL, JavaTokenType.DOUBLE_LITERAL, JavaTokenType.CHARACTER_LITERAL, + JavaTokenType.STRING_LITERAL); + private static final TokenSet CONDITIONAL_OR_OPS = TokenSet.create(JavaTokenType.OROR); + private static final TokenSet CONDITIONAL_AND_OPS = TokenSet.create(JavaTokenType.ANDAND); + private static final TokenSet OR_OPS = TokenSet.create(JavaTokenType.OR); + private static final TokenSet XOR_OPS = TokenSet.create(JavaTokenType.XOR); + private static final TokenSet AND_OPS = TokenSet.create(JavaTokenType.AND); + private static final TokenSet EQUALITY_OPS = TokenSet.create(JavaTokenType.EQEQ, JavaTokenType.NE); + private static final TokenSet SHIFT_OPS = TokenSet.create(JavaTokenType.LTLT, JavaTokenType.GTGT, JavaTokenType.GTGTGT); + private static final TokenSet ADDITIVE_OPS = TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS); + private static final TokenSet MULTIPLICATIVE_OPS = TokenSet.create(JavaTokenType.ASTERISK, JavaTokenType.DIV, JavaTokenType.PERC); + private static final TokenSet ARGS_LIST_END = TokenSet.create(JavaTokenType.RPARENTH, JavaTokenType.RBRACE, JavaTokenType.RBRACKET); + private static final TokenSet ARGS_LIST_CONTINUE = TokenSet.create( + JavaTokenType.IDENTIFIER, TokenType.BAD_CHARACTER, JavaTokenType.COMMA, JavaTokenType.INTEGER_LITERAL, JavaTokenType.STRING_LITERAL); + private static final TokenSet CONSTRUCTOR_CALL = TokenSet.create(JavaTokenType.THIS_KEYWORD, JavaTokenType.SUPER_KEYWORD); + + private ExpressionParser() { } + + @Nullable + public static PsiBuilder.Marker parse(final PsiBuilder builder) { + return parseAssignment(builder); + } + + @Nullable + private static PsiBuilder.Marker parseAssignment(final PsiBuilder builder) { + final PsiBuilder.Marker left = parseConditional(builder); + if (left == null) return null; + + final IElementType tokenType = getGtTokenType(builder); + if (ASSIGNMENT_OPS.contains(tokenType)) { + final PsiBuilder.Marker assignment = left.precede(); + advanceGtToken(builder, tokenType); + + final PsiBuilder.Marker right = parse(builder); + if (right == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + + assignment.done(JavaElementType.ASSIGNMENT_EXPRESSION); + return assignment; + } + + return left; + } + + @Nullable + public static PsiBuilder.Marker parseConditional(final PsiBuilder builder) { + final PsiBuilder.Marker condition = parseExpression(builder, ExprType.CONDITIONAL_OR); + if (condition == null) return null; + + if (builder.getTokenType() != JavaTokenType.QUEST) return condition; + final PsiBuilder.Marker ternary = condition.precede(); + builder.advanceLexer(); + + final PsiBuilder.Marker truePart = parse(builder); + if (truePart == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); + return ternary; + } + + if (builder.getTokenType() != JavaTokenType.COLON) { + error(builder, JavaErrorMessages.message("expected.colon")); + ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); + return ternary; + } + builder.advanceLexer(); + + final PsiBuilder.Marker falsePart = parseConditional(builder); + if (falsePart == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); + return ternary; + } + + ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); + return ternary; + } + + @Nullable + private static PsiBuilder.Marker parseExpression(final PsiBuilder builder, final ExprType type) { + switch (type) { + case CONDITIONAL_OR: + return parseBinary(builder, ExprType.CONDITIONAL_AND, CONDITIONAL_OR_OPS); + + case CONDITIONAL_AND: + return parseBinary(builder, ExprType.OR, CONDITIONAL_AND_OPS); + + case OR: + return parseBinary(builder, ExprType.XOR, OR_OPS); + + case XOR: + return parseBinary(builder, ExprType.AND, XOR_OPS); + + case AND: + return parseBinary(builder, ExprType.EQUALITY, AND_OPS); + + case EQUALITY: + return parseBinary(builder, ExprType.RELATIONAL, EQUALITY_OPS); + + case RELATIONAL: + return parseRelational(builder); + + case SHIFT: + return parseBinary(builder, ExprType.ADDITIVE, SHIFT_OPS); + + case ADDITIVE: + return parseBinary(builder, ExprType.MULTIPLICATIVE, ADDITIVE_OPS); + + case MULTIPLICATIVE: + return parseBinary(builder, ExprType.UNARY, MULTIPLICATIVE_OPS); + + case UNARY: + return parseUnary(builder); + + case TYPE: + return ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + + default: + assert false : "Unexpected type: " + type; + return null; + } + } + + @Nullable + private static PsiBuilder.Marker parseBinary(final PsiBuilder builder, final ExprType type, final TokenSet ops) { + PsiBuilder.Marker left = parseExpression(builder, type); + if (left == null) return null; + + while (true) { + final IElementType tokenType = getGtTokenType(builder); + if (tokenType == null || !ops.contains(tokenType)) break; + + final PsiBuilder.Marker binary = left.precede(); + advanceGtToken(builder, tokenType); + + final PsiBuilder.Marker right = parseExpression(builder, type); + if (right == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + binary.done(JavaElementType.BINARY_EXPRESSION); + return binary; + } + + binary.done(JavaElementType.BINARY_EXPRESSION); + left = binary; + } + + return left; + } + + @Nullable + private static PsiBuilder.Marker parseRelational(final PsiBuilder builder) { + PsiBuilder.Marker left = parseExpression(builder, ExprType.SHIFT); + if (left == null) return null; + + while (true) { + final IElementType toCreate; + final ExprType toParse; + final IElementType tokenType = getGtTokenType(builder); + if (RELATIONAL_OPS.contains(tokenType)) { + toCreate = JavaElementType.BINARY_EXPRESSION; + toParse = ExprType.SHIFT; + } + else if (tokenType == JavaTokenType.INSTANCEOF_KEYWORD) { + toCreate = JavaElementType.INSTANCE_OF_EXPRESSION; + toParse = ExprType.TYPE; + } + else { + break; + } + + final PsiBuilder.Marker expression = left.precede(); + advanceGtToken(builder, tokenType); + + final PsiBuilder.Marker right = parseExpression(builder, toParse); + if (right == null) { + error(builder, toParse == ExprType.TYPE ? + JavaErrorMessages.message("expected.type") : JavaErrorMessages.message("expected.expression")); + expression.done(toCreate); + return expression; + } + + expression.done(toCreate); + left = expression; + } + + return left; + } + + @Nullable + private static PsiBuilder.Marker parseUnary(final PsiBuilder builder) { + final IElementType tokenType = builder.getTokenType(); + + if (PREFIX_OPS.contains(tokenType)) { + final PsiBuilder.Marker unary = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker operand = parseUnary(builder); + if (operand == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + + unary.done(JavaElementType.PREFIX_EXPRESSION); + return unary; + } + else if (tokenType == JavaTokenType.LPARENTH) { + final PsiBuilder.Marker typeCast = builder.mark(); + builder.advanceLexer(); + + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + + if (typeInfo == null || builder.getTokenType() != JavaTokenType.RPARENTH) { + typeCast.rollbackTo(); + return parsePostfix(builder); + } + builder.advanceLexer(); + + if (PREF_ARITHMETIC_OPS.contains(builder.getTokenType())) { + if (!typeInfo.isPrimitive) { + typeCast.rollbackTo(); + return parsePostfix(builder); + } + } + + final PsiBuilder.Marker expr = parseUnary(builder); + if (expr == null) { + if (!typeInfo.isParameterized) { // cannot parse correct parenthesized expression after correct parameterized type + typeCast.rollbackTo(); + return parsePostfix(builder); + } + else { + error(builder, JavaErrorMessages.message("expected.expression")); + } + } + + typeCast.done(JavaElementType.TYPE_CAST_EXPRESSION); + return typeCast; + } + else { + return parsePostfix(builder); + } + } + + @Nullable + private static PsiBuilder.Marker parsePostfix(final PsiBuilder builder) { + PsiBuilder.Marker operand = parsePrimary(builder, null, -1); + if (operand == null) return null; + + while (POSTFIX_OPS.contains(builder.getTokenType())) { + final PsiBuilder.Marker postfix = operand.precede(); + builder.advanceLexer(); + postfix.done(JavaElementType.POSTFIX_EXPRESSION); + operand = postfix; + } + + return operand; + } + + private enum BreakPoint {P1, P2, P3, P4} + + @Nullable + private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint, final int breakOffset) { + PsiBuilder.Marker startMarker = builder.mark(); + + PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder); + if (expr == null) { + startMarker.drop(); + return null; + } + + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.DOT) { + final PsiBuilder.Marker dotPos = builder.mark(); + builder.advanceLexer(); + + final IElementType dotTokenType = builder.getTokenType(); + if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { + if (breakPoint == BreakPoint.P1 && builder.getCurrentOffset() == breakOffset) { + error(builder, JavaErrorMessages.message("expected.identifier")); + PsiBuilderUtil.drop(startMarker, dotPos); + return expr; + } + + final PsiBuilder.Marker copy = startMarker.precede(); + final int offset = builder.getCurrentOffset(); + startMarker.rollbackTo(); + + final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + if (classObjAccess == null || builder.getCurrentOffset() < offset) { + copy.rollbackTo(); + return parsePrimary(builder, BreakPoint.P1, offset); + } + + startMarker = copy; + expr = classObjAccess; + } + else if (dotTokenType == JavaTokenType.NEW_KEYWORD) { + dotPos.drop(); + expr = parseNew(builder, expr); + } + else if ((dotTokenType == JavaTokenType.THIS_KEYWORD || dotTokenType == JavaTokenType.SUPER_KEYWORD) && + exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { + if (breakPoint == BreakPoint.P2) { + dotPos.rollbackTo(); + startMarker.drop(); + return expr; + } + + final PsiBuilder.Marker copy = startMarker.precede(); + startMarker.rollbackTo(); + + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, false, true, false, false, false); + if (ref == null || builder.getTokenType() != JavaTokenType.DOT) { + copy.rollbackTo(); + return parsePrimary(builder, BreakPoint.P2, -1); + } + builder.advanceLexer(); + + if (builder.getTokenType() != dotTokenType) { + copy.rollbackTo(); + return parsePrimary(builder, BreakPoint.P2, -1); + } + builder.advanceLexer(); + + startMarker = copy; + expr = ref.precede(); + expr.done(dotTokenType == JavaTokenType.THIS_KEYWORD ? JavaElementType.THIS_EXPRESSION : JavaElementType.SUPER_EXPRESSION); + } + else if (dotTokenType == JavaTokenType.SUPER_KEYWORD) { + dotPos.drop(); + final PsiBuilder.Marker refExpr = expr.precede(); + builder.advanceLexer(); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); + expr = refExpr; + } + else { + dotPos.drop(); + final PsiBuilder.Marker refExpr = expr.precede(); + ReferenceParser.parseReferenceParameterList(builder, false, false); + + if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier"))) { + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); + startMarker.drop(); + return refExpr; + } + + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); + expr = refExpr; + } + } + else if (tokenType == JavaTokenType.LPARENTH) { + if (exprType(expr) != JavaElementType.REFERENCE_EXPRESSION) { + if (exprType(expr) == JavaElementType.SUPER_EXPRESSION) { + if (breakPoint == BreakPoint.P3) { + startMarker.drop(); + return expr; + } + + final PsiBuilder.Marker copy = startMarker.precede(); + startMarker.rollbackTo(); + + final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder); + if (qualifier != null) { + final PsiBuilder.Marker refExpr = qualifier.precede(); + if (builder.getTokenType() == JavaTokenType.DOT) { + builder.advanceLexer(); + if (builder.getTokenType() == JavaTokenType.SUPER_KEYWORD) { + builder.advanceLexer(); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); + expr = refExpr; + startMarker = copy; + continue; + } + } + } + + copy.rollbackTo(); + return parsePrimary(builder, BreakPoint.P3, -1); + } + else { + startMarker.drop(); + return expr; + } + } + + final PsiBuilder.Marker callExpr = expr.precede(); + parseArgumentList(builder); + callExpr.done(JavaElementType.METHOD_CALL_EXPRESSION); + expr = callExpr; + } + else if (tokenType == JavaTokenType.LBRACKET) { + if (breakPoint == BreakPoint.P4) { + startMarker.drop(); + return expr; + } + + builder.advanceLexer(); + + if (builder.getTokenType() == JavaTokenType.RBRACKET && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { + final int pos = builder.getCurrentOffset(); + final PsiBuilder.Marker copy = startMarker.precede(); + startMarker.rollbackTo(); + + final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + if (classObjAccess == null || builder.getCurrentOffset() <= pos) { + copy.rollbackTo(); + return parsePrimary(builder, BreakPoint.P4, -1); + } + + startMarker = copy; + expr = classObjAccess; + } + else { + final PsiBuilder.Marker arrayAccess = expr.precede(); + + final PsiBuilder.Marker index = parse(builder); + if (index == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); + startMarker.drop(); + return arrayAccess; + } + + if (builder.getTokenType() != JavaTokenType.RBRACKET) { + error(builder, JavaErrorMessages.message("expected.rbracket")); + arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); + startMarker.drop(); + return arrayAccess; + } + builder.advanceLexer(); + + arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); + expr = arrayAccess; + } + } + else { + startMarker.drop(); + return expr; + } + } + } + + @Nullable + private static PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder) { + IElementType tokenType = builder.getTokenType(); + + if (LITERALS.contains(tokenType)) { + final PsiBuilder.Marker literal = builder.mark(); + builder.advanceLexer(); + literal.done(JavaElementType.LITERAL_EXPRESSION); + return literal; + } + if (tokenType == JavaTokenType.LPARENTH) { + final PsiBuilder.Marker parenth = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker inner = parse(builder); + if (inner == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + + if (!expect(builder, JavaTokenType.RPARENTH)) { + if (inner != null) { + error(builder, JavaErrorMessages.message("expected.rparen")); + } + } + + parenth.done(JavaElementType.PARENTH_EXPRESSION); + return parenth; + } + if (tokenType == JavaTokenType.LBRACE) { + return parseArrayInitializer(builder); + } + + PsiBuilder.Marker annotation = null; + final PsiBuilder.Marker beforeAnnotation = builder.mark(); + if (tokenType == JavaTokenType.AT) { + annotation = DeclarationParser.parseAnnotations(builder); + tokenType = builder.getTokenType(); + } + + if (tokenType == JavaTokenType.IDENTIFIER) { + final PsiBuilder.Marker refExpr; + if (annotation != null) { + final PsiBuilder.Marker refParam = annotation.precede(); + refParam.doneBefore(JavaElementType.REFERENCE_PARAMETER_LIST, annotation); + refExpr = refParam.precede(); + } + else { + refExpr = builder.mark(); + builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); + } + + builder.advanceLexer(); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); + beforeAnnotation.drop(); + return refExpr; + } + + if (annotation != null) { + beforeAnnotation.rollbackTo(); + tokenType = builder.getTokenType(); + } + else { + beforeAnnotation.drop(); + } + + PsiBuilder.Marker expr = null; + if (tokenType == JavaTokenType.LT) { + expr = builder.mark(); + + if (!ReferenceParser.parseReferenceParameterList(builder, false, false)) { + expr.rollbackTo(); + return null; + } + + tokenType = builder.getTokenType(); + if (!CONSTRUCTOR_CALL.contains(tokenType)) { + expr.rollbackTo(); + return null; + } + } + + if (CONSTRUCTOR_CALL.contains(tokenType)) { + if (expr == null) { + expr = builder.mark(); + builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); + } + builder.advanceLexer(); + expr.done(builder.getTokenType() == JavaTokenType.LPARENTH + ? JavaElementType.REFERENCE_EXPRESSION + : tokenType == JavaTokenType.THIS_KEYWORD + ? JavaElementType.THIS_EXPRESSION + : JavaElementType.SUPER_EXPRESSION); + return expr; + } + if (tokenType == JavaTokenType.NEW_KEYWORD) { + return parseNew(builder, null); + } + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { + return parseClassObjectAccess(builder); + } + + return null; + } + + @Nullable + private static PsiBuilder.Marker parseArrayInitializer(final PsiBuilder builder) { + if (builder.getTokenType() != JavaTokenType.LBRACE) return null; + + final PsiBuilder.Marker arrayInit = builder.mark(); + builder.advanceLexer(); + + boolean expressionMissed = false; + PsiBuilder.Marker lastComma = null; + while (true) { + if (builder.getTokenType() == JavaTokenType.RBRACE) { + builder.advanceLexer(); + break; + } + + if (builder.getTokenType() == null) { + error(builder, JavaErrorMessages.message("expected.rbrace")); + break; + } + + if (expressionMissed && lastComma != null) { + // before comma must be an expression + lastComma.precede().errorBefore(JavaErrorMessages.message("expected.expression"), lastComma); + lastComma.drop(); + lastComma = null; + } + + final PsiBuilder.Marker arg = parse(builder); + if (arg == null) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + expressionMissed = true; + lastComma = builder.mark(); + } + else { + error(builder, JavaErrorMessages.message("expected.rbrace")); + break; + } + } + + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.COMMA) { + builder.advanceLexer(); + } + else if (tokenType != JavaTokenType.RBRACE) { + error(builder, JavaErrorMessages.message("expected.comma")); + } + } + + if (lastComma != null) { + lastComma.drop(); + } + + arrayInit.done(JavaElementType.ARRAY_INITIALIZER_EXPRESSION); + return arrayInit; + } + + @NotNull + private static PsiBuilder.Marker parseNew(final PsiBuilder builder, final PsiBuilder.Marker start) { + final PsiBuilder.Marker newExpr = (start != null ? start.precede() : builder.mark()); + builder.advanceLexer(); + + final boolean parseDiamonds = areDiamondsSupported(builder); + ReferenceParser.parseReferenceParameterList(builder, false, parseDiamonds); + + final PsiBuilder.Marker refOrType; + final boolean parseAnnotations = areTypeAnnotationsSupported(builder) && builder.getTokenType() == JavaTokenType.AT; + + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.IDENTIFIER || parseAnnotations) { + refOrType = ReferenceParser.parseJavaCodeReference(builder, true, true, parseAnnotations, true, parseDiamonds); + if (refOrType == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + newExpr.done(JavaElementType.NEW_EXPRESSION); + return newExpr; + } + } + else if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { + refOrType = null; + builder.advanceLexer(); + } + else { + error(builder, JavaErrorMessages.message("expected.identifier")); + newExpr.done(JavaElementType.NEW_EXPRESSION); + return newExpr; + } + + if (refOrType != null && builder.getTokenType() == JavaTokenType.LPARENTH) { + parseArgumentList(builder); + if (builder.getTokenType() == JavaTokenType.LBRACE) { + final PsiBuilder.Marker classElement = refOrType.precede(); + DeclarationParser.parseClassBodyWithBraces(builder, false, false); + classElement.done(JavaElementType.ANONYMOUS_CLASS); + } + } + else { + if (builder.getTokenType() != JavaTokenType.LBRACKET) { + error(builder, refOrType == null ? + JavaErrorMessages.message("expected.lbracket") : JavaErrorMessages.message("expected.lparen.or.lbracket")); + newExpr.done(JavaElementType.NEW_EXPRESSION); + return newExpr; + } + + int bracketCount = 0; + int dimCount = 0; + while (true) { + if (builder.getTokenType() != JavaTokenType.LBRACKET) break; + builder.advanceLexer(); + + if (bracketCount == dimCount) { + final PsiBuilder.Marker dimExpr = parse(builder); + if (dimExpr != null) { + dimCount++; + } + } + bracketCount++; + + if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, JavaErrorMessages.message("expected.rbracket"))) { + newExpr.done(JavaElementType.NEW_EXPRESSION); + return newExpr; + } + } + + if (dimCount == 0) { + if (builder.getTokenType() == JavaTokenType.LBRACE) { + parseArrayInitializer(builder); + } + else { + error(builder, JavaErrorMessages.message("expected.array.initializer")); + } + } + } + + newExpr.done(JavaElementType.NEW_EXPRESSION); + return newExpr; + } + + @Nullable + private static PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { + final PsiBuilder.Marker expr = builder.mark(); + + if (ReferenceParser.parseType(builder, 0) == null) { + expr.drop(); + return null; + } + + if (builder.getTokenType() != JavaTokenType.DOT) { + expr.rollbackTo(); + return null; + } + builder.advanceLexer(); + + if (builder.getTokenType() != JavaTokenType.CLASS_KEYWORD) { + expr.rollbackTo(); + return null; + } + builder.advanceLexer(); + + expr.done(JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION); + return expr; + } + + @NotNull + public static PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) { + final PsiBuilder.Marker list = builder.mark(); + builder.advanceLexer(); + + boolean first = true; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (first && (ARGS_LIST_END.contains(tokenType) || builder.eof())) break; + if (!first && !ARGS_LIST_CONTINUE.contains(tokenType)) break; + + boolean hasError = false; + if (!first) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + builder.advanceLexer(); + } + else { + hasError = true; + error(builder, JavaErrorMessages.message("expected.comma.or.rparen")); + emptyExpression(builder); + } + } + first = false; + + final PsiBuilder.Marker arg = parse(builder); + if (arg == null) { + if (!hasError) { + error(builder, JavaErrorMessages.message("expected.expression")); + emptyExpression(builder); + } + if (!ARGS_LIST_CONTINUE.contains(builder.getTokenType())) break; + if (builder.getTokenType() != JavaTokenType.COMMA && !builder.eof()) { + builder.advanceLexer(); + } + } + } + + final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, JavaErrorMessages.message("expected.rparen")); + + list.done(JavaElementType.EXPRESSION_LIST); + if (!closed) { + list.setCustomEdgeTokenBinders(null, GREEDY_RIGHT_EDGE_PROCESSOR); + } + return list; + } + + private static void emptyExpression(final PsiBuilder builder) { + emptyElement(builder, JavaElementType.EMPTY_EXPRESSION); + } + + @Nullable + private static IElementType getGtTokenType(final PsiBuilder builder) { + final PsiBuilder.Marker sp = builder.mark(); + + IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.GT) { + builder.advanceLexer(); + if (builder.getTokenType() == JavaTokenType.GT) { + builder.advanceLexer(); + if (builder.getTokenType() == JavaTokenType.GT) { + builder.advanceLexer(); + if (builder.getTokenType() == JavaTokenType.EQ) { + tokenType = JavaTokenType.GTGTGTEQ; + } + else { + tokenType = JavaTokenType.GTGTGT; + } + } + else if (builder.getTokenType() == JavaTokenType.EQ) { + tokenType = JavaTokenType.GTGTEQ; + } + else { + tokenType = JavaTokenType.GTGT; + } + } + else if (builder.getTokenType() == JavaTokenType.EQ) { + tokenType = JavaTokenType.GE; + } + } + + sp.rollbackTo(); + return tokenType; + } + + private static void advanceGtToken(final PsiBuilder builder, final IElementType type) { + final PsiBuilder.Marker gtToken = builder.mark(); + + if (type == JavaTokenType.GTGTGTEQ) { + PsiBuilderUtil.advance(builder, 4); + } + else if (type == JavaTokenType.GTGTGT || type == JavaTokenType.GTGTEQ) { + PsiBuilderUtil.advance(builder, 3); + } + else if (type == JavaTokenType.GTGT || type == JavaTokenType.GE) { + PsiBuilderUtil.advance(builder, 2); + } + else { + gtToken.drop(); + builder.advanceLexer(); + return; + } + + gtToken.collapse(type); + } +} diff --git a/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java index 06ff60adde5c..d41de50bc581 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java @@ -115,7 +115,7 @@ public class FileParser { } } - final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, false, false, false); + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, false, false, false, false); if (ref == null) { statement.rollbackTo(); return null; diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index bbe28b6992a8..00eb4604df3f 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -1,346 +1,349 @@ -/* - * Copyright 2000-2010 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 com.intellij.lang.java.parser; - -import com.intellij.codeInsight.daemon.JavaErrorMessages; -import com.intellij.lang.PsiBuilder; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.impl.source.tree.JavaElementType; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.TokenSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.intellij.lang.PsiBuilderUtil.expect; -import static com.intellij.lang.PsiBuilderUtil.nextTokenType; -import static com.intellij.lang.java.parser.JavaParserUtil.*; -import static com.intellij.lang.java.parser.JavaParserUtil.emptyElement; - - -public class ReferenceParser { - public static final int EAT_LAST_DOT = 0x01; - public static final int ELLIPSIS = 0x02; - public static final int WILDCARD = 0x04; - public static final int DIAMONDS = 0x08; - - public static class TypeInfo { - public boolean isPrimitive = false; - public boolean isParameterized = false; - public boolean isArray = false; - public boolean isVarArg = false; - public boolean hasErrors = false; - public PsiBuilder.Marker marker = null; - } - - private static final TokenSet WILDCARD_KEYWORD_SET = TokenSet.create(JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.SUPER_KEYWORD); - - private ReferenceParser() { } - - @Nullable - public static PsiBuilder.Marker parseType(final PsiBuilder builder, final int flags) { - final TypeInfo typeInfo = parseTypeInfo(builder, flags); - return typeInfo != null ? typeInfo.marker : null; - } - - @Nullable - public static TypeInfo parseTypeInfo(final PsiBuilder builder, final int flags) { - return parseTypeInfo(builder, isSet(flags, EAT_LAST_DOT), isSet(flags, WILDCARD), isSet(flags, DIAMONDS), isSet(flags, ELLIPSIS)); - } - - @Nullable - private static TypeInfo parseTypeInfo(final PsiBuilder builder, - final boolean eatLastDot, final boolean wildcard, final boolean diamonds, final boolean ellipsis) { - if (builder.getTokenType() == null) return null; - - final TypeInfo typeInfo = new TypeInfo(); - - final boolean annotationsSupported = areTypeAnnotationsSupported(builder); - PsiBuilder.Marker type = builder.mark(); - if (annotationsSupported) { - DeclarationParser.parseAnnotations(builder); - } - - final IElementType tokenType = builder.getTokenType(); - if (expect(builder, ElementType.PRIMITIVE_TYPE_BIT_SET)) { - typeInfo.isPrimitive = true; - } - else if (tokenType == JavaTokenType.IDENTIFIER) { - parseJavaCodeReference(builder, eatLastDot, true, annotationsSupported, false, false, diamonds, typeInfo); - } - else if (wildcard && tokenType == JavaTokenType.QUEST) { - type.drop(); - typeInfo.marker = parseWildcardType(builder); - return typeInfo.marker != null ? typeInfo : null; - } - else if (diamonds && tokenType == JavaTokenType.GT) { - emptyElement(builder, JavaElementType.DIAMOND_TYPE); - type.done(JavaElementType.TYPE); - typeInfo.marker = type; - return typeInfo; - } - else { - type.drop(); - return null; - } - - while (true) { - type.done(JavaElementType.TYPE); - if (annotationsSupported) { - DeclarationParser.parseAnnotations(builder); - } - - final PsiBuilder.Marker bracket = builder.mark(); - if (!expect(builder, JavaTokenType.LBRACKET)) { - bracket.drop(); - break; - } - if (!expect(builder, JavaTokenType.RBRACKET)) { - bracket.rollbackTo(); - break; - } - bracket.drop(); - typeInfo.isArray = true; - - type = type.precede(); - } - - if (ellipsis && builder.getTokenType() == JavaTokenType.ELLIPSIS) { - type = type.precede(); - builder.advanceLexer(); - type.done(JavaElementType.TYPE); - typeInfo.isVarArg = true; - } - - typeInfo.marker = type; - return typeInfo; - } - - private static boolean isSet(final int mask, final int flag) { - return (mask & flag) != 0; - } - - @NotNull - private static PsiBuilder.Marker parseWildcardType(final PsiBuilder builder) { - final PsiBuilder.Marker type = builder.mark(); - builder.advanceLexer(); - - if (expect(builder, WILDCARD_KEYWORD_SET)) { - if (parseTypeInfo(builder, EAT_LAST_DOT) == null) { - error(builder, JavaErrorMessages.message("expected.type")); - } - } - - type.done(JavaElementType.TYPE); - return type; - } - - @Nullable - public static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, - final boolean parameterList, final boolean annotations, final boolean diamonds) { - return parseJavaCodeReference(builder, eatLastDot, parameterList, annotations, false, false, diamonds, new TypeInfo()); - } - - public static boolean parseImportCodeReference(final PsiBuilder builder, final boolean isStatic) { - final TypeInfo typeInfo = new TypeInfo(); - parseJavaCodeReference(builder, true, false, false, true, isStatic, false, typeInfo); - return !typeInfo.hasErrors; - } - - @Nullable - private static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, - final boolean parameterList, final boolean annotations, - final boolean isImport, final boolean isStaticImport, - final boolean diamonds, final TypeInfo typeInfo) { - PsiBuilder.Marker refElement = builder.mark(); - - if (annotations) { - DeclarationParser.parseAnnotations(builder); - } - - if (!expect(builder, JavaTokenType.IDENTIFIER)) { - refElement.rollbackTo(); - if (isImport) { - error(builder, JavaErrorMessages.message("expected.identifier")); - } - typeInfo.hasErrors = true; - return null; - } - - if (parameterList) { - typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds); - } - else { - if (!isStaticImport || builder.getTokenType() == JavaTokenType.DOT) { - emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); - } - } - - boolean hasIdentifier; - while (builder.getTokenType() == JavaTokenType.DOT) { - refElement.done(JavaElementType.JAVA_CODE_REFERENCE); - - final PsiBuilder.Marker dotPos = builder.mark(); - builder.advanceLexer(); - - if (expect(builder, JavaTokenType.IDENTIFIER)) { - hasIdentifier = true; - } - else if (isImport && expect(builder, JavaTokenType.ASTERISK)) { - dotPos.drop(); - return refElement; - } - else { - if (!eatLastDot) { - dotPos.rollbackTo(); - return refElement; - } - hasIdentifier = false; - } - dotPos.drop(); - - final PsiBuilder.Marker prevElement = refElement; - refElement = refElement.precede(); - - if (!hasIdentifier) { - typeInfo.hasErrors = true; - if (isImport) { - error(builder, JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); - refElement.drop(); - return prevElement; - } - else { - error(builder, JavaErrorMessages.message("expected.identifier")); - emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); - break; - } - } - - if (parameterList) { - typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds); - } - else if (!isStaticImport || builder.getTokenType() == JavaTokenType.DOT) { - emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); - } - } - - refElement.done(isStaticImport ? JavaElementType.IMPORT_STATIC_REFERENCE : JavaElementType.JAVA_CODE_REFERENCE); - return refElement; - } - - public static boolean parseReferenceParameterList(final PsiBuilder builder, final boolean wildcard, final boolean diamonds) { - final PsiBuilder.Marker list = builder.mark(); - if (!expect(builder, JavaTokenType.LT)) { - list.done(JavaElementType.REFERENCE_PARAMETER_LIST); - return false; - } - - boolean isOk = true; - while (true) { - if (parseTypeInfo(builder, true, wildcard, diamonds, false) == null) { - error(builder, JavaErrorMessages.message("expected.identifier")); - } - - if (expect(builder, JavaTokenType.GT)) { - break; - } - else if (!expectOrError(builder, JavaTokenType.COMMA, JavaErrorMessages.message("expected.gt.or.comma"))) { - isOk = false; - break; - } - } - - list.done(JavaElementType.REFERENCE_PARAMETER_LIST); - return isOk; - } - - @NotNull - public static PsiBuilder.Marker parseTypeParameters(final PsiBuilder builder) { - final PsiBuilder.Marker list = builder.mark(); - if (!expect(builder, JavaTokenType.LT)) { - list.done(JavaElementType.TYPE_PARAMETER_LIST); - return list; - } - - while (true) { - final PsiBuilder.Marker param = parseTypeParameter(builder); - if (param == null) { - error(builder, JavaErrorMessages.message("expected.type.parameter")); - } - if (!expect(builder, JavaTokenType.COMMA)) { - break; - } - } - - if (!expect(builder, JavaTokenType.GT)) { - // hack for completion - if (builder.getTokenType() == JavaTokenType.IDENTIFIER) { - if (nextTokenType(builder) == JavaTokenType.GT) { - final PsiBuilder.Marker errorElement = builder.mark(); - builder.advanceLexer(); - errorElement.error(JavaErrorMessages.message("unexpected.identifier")); - builder.advanceLexer(); - } - else { - error(builder, JavaErrorMessages.message("expected.gt")); - } - } - else { - error(builder, JavaErrorMessages.message("expected.gt")); - } - } - - list.done(JavaElementType.TYPE_PARAMETER_LIST); - return list; - } - - @Nullable - private static PsiBuilder.Marker parseTypeParameter(final PsiBuilder builder) { - final PsiBuilder.Marker param = builder.mark(); - - DeclarationParser.parseAnnotations(builder); - if (!expect(builder, JavaTokenType.IDENTIFIER)) { - param.rollbackTo(); - return null; - } - - parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_BOUND_LIST, JavaTokenType.AND); - - param.done(JavaElementType.TYPE_PARAMETER); - return param; - } - - @NotNull - public static PsiBuilder.Marker parseReferenceList(final PsiBuilder builder, final IElementType start, - final IElementType type, final IElementType delimiter) { - final PsiBuilder.Marker element = builder.mark(); - - if (expect(builder, start)) { - while (true) { - final PsiBuilder.Marker classReference = parseJavaCodeReference(builder, true, true, true, false); - if (classReference == null) { - error(builder, JavaErrorMessages.message("expected.identifier")); - } - if (!expect(builder, delimiter)) { - break; - } - } - } - - element.done(type); - return element; - } -} +/* + * Copyright 2000-2010 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 com.intellij.lang.java.parser; + +import com.intellij.codeInsight.daemon.JavaErrorMessages; +import com.intellij.lang.PsiBuilder; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.lang.PsiBuilderUtil.expect; +import static com.intellij.lang.PsiBuilderUtil.nextTokenType; +import static com.intellij.lang.java.parser.JavaParserUtil.*; +import static com.intellij.lang.java.parser.JavaParserUtil.emptyElement; + + +public class ReferenceParser { + public static final int EAT_LAST_DOT = 0x01; + public static final int ELLIPSIS = 0x02; + public static final int WILDCARD = 0x04; + public static final int DIAMONDS = 0x08; + + public static class TypeInfo { + public boolean isPrimitive = false; + public boolean isParameterized = false; + public boolean isArray = false; + public boolean isVarArg = false; + public boolean hasErrors = false; + public PsiBuilder.Marker marker = null; + } + + private static final TokenSet WILDCARD_KEYWORD_SET = TokenSet.create(JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.SUPER_KEYWORD); + + private ReferenceParser() { } + + @Nullable + public static PsiBuilder.Marker parseType(final PsiBuilder builder, final int flags) { + final TypeInfo typeInfo = parseTypeInfo(builder, flags); + return typeInfo != null ? typeInfo.marker : null; + } + + @Nullable + public static TypeInfo parseTypeInfo(final PsiBuilder builder, final int flags) { + return parseTypeInfo(builder, isSet(flags, EAT_LAST_DOT), isSet(flags, WILDCARD), isSet(flags, DIAMONDS), isSet(flags, ELLIPSIS)); + } + + @Nullable + private static TypeInfo parseTypeInfo(final PsiBuilder builder, + final boolean eatLastDot, final boolean wildcard, final boolean diamonds, final boolean ellipsis) { + if (builder.getTokenType() == null) return null; + + final TypeInfo typeInfo = new TypeInfo(); + + final boolean annotationsSupported = areTypeAnnotationsSupported(builder); + PsiBuilder.Marker type = builder.mark(); + if (annotationsSupported) { + DeclarationParser.parseAnnotations(builder); + } + + final IElementType tokenType = builder.getTokenType(); + if (expect(builder, ElementType.PRIMITIVE_TYPE_BIT_SET)) { + typeInfo.isPrimitive = true; + } + else if (tokenType == JavaTokenType.IDENTIFIER) { + parseJavaCodeReference(builder, eatLastDot, true, annotationsSupported, false, false, false, diamonds, typeInfo); + } + else if (wildcard && tokenType == JavaTokenType.QUEST) { + type.drop(); + typeInfo.marker = parseWildcardType(builder); + return typeInfo.marker != null ? typeInfo : null; + } + else if (diamonds && tokenType == JavaTokenType.GT) { + emptyElement(builder, JavaElementType.DIAMOND_TYPE); + type.done(JavaElementType.TYPE); + typeInfo.marker = type; + return typeInfo; + } + else { + type.drop(); + return null; + } + + while (true) { + type.done(JavaElementType.TYPE); + if (annotationsSupported) { + DeclarationParser.parseAnnotations(builder); + } + + final PsiBuilder.Marker bracket = builder.mark(); + if (!expect(builder, JavaTokenType.LBRACKET)) { + bracket.drop(); + break; + } + if (!expect(builder, JavaTokenType.RBRACKET)) { + bracket.rollbackTo(); + break; + } + bracket.drop(); + typeInfo.isArray = true; + + type = type.precede(); + } + + if (ellipsis && builder.getTokenType() == JavaTokenType.ELLIPSIS) { + type = type.precede(); + builder.advanceLexer(); + type.done(JavaElementType.TYPE); + typeInfo.isVarArg = true; + } + + typeInfo.marker = type; + return typeInfo; + } + + private static boolean isSet(final int mask, final int flag) { + return (mask & flag) != 0; + } + + @NotNull + private static PsiBuilder.Marker parseWildcardType(final PsiBuilder builder) { + final PsiBuilder.Marker type = builder.mark(); + builder.advanceLexer(); + + if (expect(builder, WILDCARD_KEYWORD_SET)) { + if (parseTypeInfo(builder, EAT_LAST_DOT) == null) { + error(builder, JavaErrorMessages.message("expected.type")); + } + } + + type.done(JavaElementType.TYPE); + return type; + } + + @Nullable + public static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, final boolean parameterList, + final boolean annotations, final boolean isNew, final boolean diamonds) { + return parseJavaCodeReference(builder, eatLastDot, parameterList, annotations, false, false, isNew, diamonds, new TypeInfo()); + } + + public static boolean parseImportCodeReference(final PsiBuilder builder, final boolean isStatic) { + final TypeInfo typeInfo = new TypeInfo(); + parseJavaCodeReference(builder, true, false, false, true, isStatic, false, false, typeInfo); + return !typeInfo.hasErrors; + } + + @Nullable + private static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, final boolean parameterList, + final boolean annotations, final boolean isImport, final boolean isStaticImport, + final boolean isNew, final boolean diamonds, final TypeInfo typeInfo) { + PsiBuilder.Marker refElement = builder.mark(); + + if (annotations) { + DeclarationParser.parseAnnotations(builder); + } + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + refElement.rollbackTo(); + if (isImport) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + typeInfo.hasErrors = true; + return null; + } + + if (parameterList) { + typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds); + } + else { + if (!isStaticImport || builder.getTokenType() == JavaTokenType.DOT) { + emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); + } + } + + boolean hasIdentifier; + while (builder.getTokenType() == JavaTokenType.DOT) { + refElement.done(JavaElementType.JAVA_CODE_REFERENCE); + + if (isNew && typeInfo.isParameterized) { + return refElement; + } + + final PsiBuilder.Marker dotPos = builder.mark(); + builder.advanceLexer(); + + if (expect(builder, JavaTokenType.IDENTIFIER)) { + hasIdentifier = true; + } + else if (isImport && expect(builder, JavaTokenType.ASTERISK)) { + dotPos.drop(); + return refElement; + } + else { + if (!eatLastDot) { + dotPos.rollbackTo(); + return refElement; + } + hasIdentifier = false; + } + dotPos.drop(); + + final PsiBuilder.Marker prevElement = refElement; + refElement = refElement.precede(); + + if (!hasIdentifier) { + typeInfo.hasErrors = true; + if (isImport) { + error(builder, JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); + refElement.drop(); + return prevElement; + } + else { + error(builder, JavaErrorMessages.message("expected.identifier")); + emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); + break; + } + } + + if (parameterList) { + typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds); + } + else if (!isStaticImport || builder.getTokenType() == JavaTokenType.DOT) { + emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); + } + } + + refElement.done(isStaticImport ? JavaElementType.IMPORT_STATIC_REFERENCE : JavaElementType.JAVA_CODE_REFERENCE); + return refElement; + } + + public static boolean parseReferenceParameterList(final PsiBuilder builder, final boolean wildcard, final boolean diamonds) { + final PsiBuilder.Marker list = builder.mark(); + if (!expect(builder, JavaTokenType.LT)) { + list.done(JavaElementType.REFERENCE_PARAMETER_LIST); + return false; + } + + boolean isOk = true; + while (true) { + if (parseTypeInfo(builder, true, wildcard, diamonds, false) == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + + if (expect(builder, JavaTokenType.GT)) { + break; + } + else if (!expectOrError(builder, JavaTokenType.COMMA, JavaErrorMessages.message("expected.gt.or.comma"))) { + isOk = false; + break; + } + } + + list.done(JavaElementType.REFERENCE_PARAMETER_LIST); + return isOk; + } + + @NotNull + public static PsiBuilder.Marker parseTypeParameters(final PsiBuilder builder) { + final PsiBuilder.Marker list = builder.mark(); + if (!expect(builder, JavaTokenType.LT)) { + list.done(JavaElementType.TYPE_PARAMETER_LIST); + return list; + } + + while (true) { + final PsiBuilder.Marker param = parseTypeParameter(builder); + if (param == null) { + error(builder, JavaErrorMessages.message("expected.type.parameter")); + } + if (!expect(builder, JavaTokenType.COMMA)) { + break; + } + } + + if (!expect(builder, JavaTokenType.GT)) { + // hack for completion + if (builder.getTokenType() == JavaTokenType.IDENTIFIER) { + if (nextTokenType(builder) == JavaTokenType.GT) { + final PsiBuilder.Marker errorElement = builder.mark(); + builder.advanceLexer(); + errorElement.error(JavaErrorMessages.message("unexpected.identifier")); + builder.advanceLexer(); + } + else { + error(builder, JavaErrorMessages.message("expected.gt")); + } + } + else { + error(builder, JavaErrorMessages.message("expected.gt")); + } + } + + list.done(JavaElementType.TYPE_PARAMETER_LIST); + return list; + } + + @Nullable + private static PsiBuilder.Marker parseTypeParameter(final PsiBuilder builder) { + final PsiBuilder.Marker param = builder.mark(); + + DeclarationParser.parseAnnotations(builder); + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + param.rollbackTo(); + return null; + } + + parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_BOUND_LIST, JavaTokenType.AND); + + param.done(JavaElementType.TYPE_PARAMETER); + return param; + } + + @NotNull + public static PsiBuilder.Marker parseReferenceList(final PsiBuilder builder, final IElementType start, + final IElementType type, final IElementType delimiter) { + final PsiBuilder.Marker element = builder.mark(); + + if (expect(builder, start)) { + while (true) { + final PsiBuilder.Marker classReference = parseJavaCodeReference(builder, true, true, true, false, false); + if (classReference == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + if (!expect(builder, delimiter)) { + break; + } + } + } + + element.done(type); + return element; + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 4ad58eb2d125..0af5952b0e1a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -1,267 +1,267 @@ -/* - * Copyright 2000-2009 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 com.intellij.psi.impl.source.tree; - -import com.intellij.lang.*; -import com.intellij.lang.java.JavaParserDefinition; -import com.intellij.lang.java.parser.ExpressionParser; -import com.intellij.lang.java.parser.JavaParserUtil; -import com.intellij.lang.java.parser.ReferenceParser; -import com.intellij.lang.java.parser.StatementParser; -import com.intellij.lexer.JavaLexer; -import com.intellij.lexer.Lexer; -import com.intellij.openapi.project.Project; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiManager; -import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; -import com.intellij.psi.impl.source.parsing.JavaParsingContext; -import com.intellij.psi.impl.source.parsing.Parsing; -import com.intellij.psi.impl.source.tree.java.PsiCodeBlockImpl; -import com.intellij.psi.text.BlockSupport; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.IErrorCounterReparseableElementType; -import com.intellij.psi.tree.ILazyParseableElementType; -import com.intellij.psi.tree.ILightLazyParseableElementType; -import com.intellij.psi.tree.java.IJavaElementType; -import com.intellij.psi.util.PsiUtil; -import com.intellij.util.CharTable; -import com.intellij.util.diff.FlyweightCapableTreeStructure; - -import static com.intellij.lang.PsiBuilderUtil.expect; - -public interface JavaElementType { - IElementType CLASS = JavaStubElementTypes.CLASS; - IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; - IElementType ENUM_CONSTANT_INITIALIZER = JavaStubElementTypes.ENUM_CONSTANT_INITIALIZER; - - IElementType TYPE_PARAMETER_LIST = JavaStubElementTypes.TYPE_PARAMETER_LIST; - IElementType TYPE_PARAMETER = JavaStubElementTypes.TYPE_PARAMETER; - - IElementType IMPORT_LIST = JavaStubElementTypes.IMPORT_LIST; - IElementType IMPORT_STATEMENT = JavaStubElementTypes.IMPORT_STATEMENT; - IElementType IMPORT_STATIC_STATEMENT = JavaStubElementTypes.IMPORT_STATIC_STATEMENT; - - IElementType MODIFIER_LIST = JavaStubElementTypes.MODIFIER_LIST; - IElementType ANNOTATION = JavaStubElementTypes.ANNOTATION; - IElementType EXTENDS_LIST = JavaStubElementTypes.EXTENDS_LIST; - IElementType IMPLEMENTS_LIST = JavaStubElementTypes.IMPLEMENTS_LIST; - IElementType FIELD = JavaStubElementTypes.FIELD; - IElementType ENUM_CONSTANT = JavaStubElementTypes.ENUM_CONSTANT; - IElementType METHOD = JavaStubElementTypes.METHOD; - IElementType ANNOTATION_METHOD = JavaStubElementTypes.ANNOTATION_METHOD; - - IElementType CLASS_INITIALIZER = JavaStubElementTypes.CLASS_INITIALIZER; - IElementType PARAMETER = JavaStubElementTypes.PARAMETER; - IElementType PARAMETER_LIST = JavaStubElementTypes.PARAMETER_LIST; - IElementType EXTENDS_BOUND_LIST = JavaStubElementTypes.EXTENDS_BOUND_LIST; - IElementType THROWS_LIST = JavaStubElementTypes.THROWS_LIST; - - IElementType IMPORT_STATIC_REFERENCE = new IJavaElementType("IMPORT_STATIC_REFERENCE"); - IElementType TYPE = new IJavaElementType("TYPE"); - IElementType DIAMOND_TYPE = new IJavaElementType("DIAMOND_TYPE"); - IElementType REFERENCE_PARAMETER_LIST = new IJavaElementType("REFERENCE_PARAMETER_LIST", true); - IElementType JAVA_CODE_REFERENCE = new IJavaElementType("JAVA_CODE_REFERENCE"); - IElementType PACKAGE_STATEMENT = new IJavaElementType("PACKAGE_STATEMENT"); - - IElementType LOCAL_VARIABLE = new IJavaElementType("LOCAL_VARIABLE"); - IElementType REFERENCE_EXPRESSION = new IJavaElementType("REFERENCE_EXPRESSION"); - IElementType LITERAL_EXPRESSION = new IJavaElementType("LITERAL_EXPRESSION"); - IElementType THIS_EXPRESSION = new IJavaElementType("THIS_EXPRESSION"); - IElementType SUPER_EXPRESSION = new IJavaElementType("SUPER_EXPRESSION"); - IElementType PARENTH_EXPRESSION = new IJavaElementType("PARENTH_EXPRESSION"); - IElementType METHOD_CALL_EXPRESSION = new IJavaElementType("METHOD_CALL_EXPRESSION"); - IElementType TYPE_CAST_EXPRESSION = new IJavaElementType("TYPE_CAST_EXPRESSION"); - IElementType PREFIX_EXPRESSION = new IJavaElementType("PREFIX_EXPRESSION"); - IElementType POSTFIX_EXPRESSION = new IJavaElementType("POSTFIX_EXPRESSION"); - IElementType BINARY_EXPRESSION = new IJavaElementType("BINARY_EXPRESSION"); - IElementType CONDITIONAL_EXPRESSION = new IJavaElementType("CONDITIONAL_EXPRESSION"); - IElementType ASSIGNMENT_EXPRESSION = new IJavaElementType("ASSIGNMENT_EXPRESSION"); - IElementType NEW_EXPRESSION = new IJavaElementType("NEW_EXPRESSION"); - IElementType ARRAY_ACCESS_EXPRESSION = new IJavaElementType("ARRAY_ACCESS_EXPRESSION"); - IElementType ARRAY_INITIALIZER_EXPRESSION = new IJavaElementType("ARRAY_INITIALIZER_EXPRESSION"); - IElementType INSTANCE_OF_EXPRESSION = new IJavaElementType("INSTANCE_OF_EXPRESSION"); - IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new IJavaElementType("CLASS_OBJECT_ACCESS_EXPRESSION"); - IElementType EMPTY_EXPRESSION = new IJavaElementType("EMPTY_EXPRESSION", true); - - IElementType EXPRESSION_LIST = new IJavaElementType("EXPRESSION_LIST", true); - - IElementType EMPTY_STATEMENT = new IJavaElementType("EMPTY_STATEMENT"); - IElementType BLOCK_STATEMENT = new IJavaElementType("BLOCK_STATEMENT"); - IElementType EXPRESSION_STATEMENT = new IJavaElementType("EXPRESSION_STATEMENT"); - IElementType EXPRESSION_LIST_STATEMENT = new IJavaElementType("EXPRESSION_LIST_STATEMENT"); - IElementType DECLARATION_STATEMENT = new IJavaElementType("DECLARATION_STATEMENT"); - IElementType IF_STATEMENT = new IJavaElementType("IF_STATEMENT"); - IElementType WHILE_STATEMENT = new IJavaElementType("WHILE_STATEMENT"); - IElementType FOR_STATEMENT = new IJavaElementType("FOR_STATEMENT"); - IElementType FOREACH_STATEMENT = new IJavaElementType("FOREACH_STATEMENT"); - IElementType DO_WHILE_STATEMENT = new IJavaElementType("DO_WHILE_STATEMENT"); - IElementType SWITCH_STATEMENT = new IJavaElementType("SWITCH_STATEMENT"); - IElementType SWITCH_LABEL_STATEMENT = new IJavaElementType("SWITCH_LABEL_STATEMENT"); - IElementType BREAK_STATEMENT = new IJavaElementType("BREAK_STATEMENT"); - IElementType CONTINUE_STATEMENT = new IJavaElementType("CONTINUE_STATEMENT"); - IElementType RETURN_STATEMENT = new IJavaElementType("RETURN_STATEMENT"); - IElementType THROW_STATEMENT = new IJavaElementType("THROW_STATEMENT"); - IElementType SYNCHRONIZED_STATEMENT = new IJavaElementType("SYNCHRONIZED_STATEMENT"); - IElementType TRY_STATEMENT = new IJavaElementType("TRY_STATEMENT"); - IElementType LABELED_STATEMENT = new IJavaElementType("LABELED_STATEMENT"); - IElementType ASSERT_STATEMENT = new IJavaElementType("ASSERT_STATEMENT"); - - IElementType CATCH_SECTION = new IJavaElementType("CATCH_SECTION"); - - IElementType ANNOTATION_ARRAY_INITIALIZER = new IJavaElementType("ANNOTATION_ARRAY_INITIALIZER"); - IElementType NAME_VALUE_PAIR = new IJavaElementType("NAME_VALUE_PAIR", true); - IElementType ANNOTATION_PARAMETER_LIST = new IJavaElementType("ANNOTATION_PARAMETER_LIST", true); - IElementType METHOD_RECEIVER = new IJavaElementType("METHOD_RECEIVER"); - - class ICodeBlockElementType extends IErrorCounterReparseableElementType { - private ICodeBlockElementType() { - super("CODE_BLOCK", StdLanguages.JAVA); - } - - @Override - public ASTNode createNode(final CharSequence text) { - return new PsiCodeBlockImpl(text); - } - - @Override - public ASTNode parseContents(final ASTNode chameleon) { - if (JavaParserDefinition.USE_NEW_PARSER) { - final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); - StatementParser.parseCodeBlockDeep(builder, true); - return builder.getTreeBuilt().getFirstChildNode(); - } - - final CharSequence seq = chameleon.getChars(); - - ASTNode original = chameleon.getUserData(BlockSupport.TREE_TO_BE_REPARSED); - ASTNode context = original != null ? original : chameleon.getTreeParent(); - - final PsiManager manager = context.getPsi().getManager(); - final CharTable table = SharedImplUtil.findCharTableByTree(context); - final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(TreeUtil.getFileElement((TreeElement)context).getPsi()); - JavaParsingContext parsingContext = new JavaParsingContext(table, languageLevel); - return parsingContext.getStatementParsing().parseCodeBlockText(manager, new JavaLexer(languageLevel), - seq, 0, seq.length(), 0).getFirstChildNode(); - } - - public int getErrorsCount(final CharSequence seq, final Project project) { - final Lexer lexer = new JavaLexer(LanguageLevel.HIGHEST); - - lexer.start(seq); - if (lexer.getTokenType() != JavaTokenType.LBRACE) return FATAL_ERROR; - lexer.advance(); - int balance = 1; - while (true) { - IElementType type = lexer.getTokenType(); - if (type == null) break; - if (balance == 0) return FATAL_ERROR; - if (type == JavaTokenType.LBRACE) { - balance++; - } - else if (type == JavaTokenType.RBRACE) { - balance--; - } - lexer.advance(); - } - return balance; - } - } - - class ICodeBlockNewElementType extends ICodeBlockElementType implements ILightLazyParseableElementType { - private ICodeBlockNewElementType() { } - - public FlyweightCapableTreeStructure parseContents(final LighterLazyParseableNode chameleon) { - final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); - StatementParser.parseCodeBlockDeep(builder, true); - return builder.getLightTree(); - } - } - - ILazyParseableElementType CODE_BLOCK = JavaParserDefinition.USE_NEW_PARSER ? new ICodeBlockNewElementType() : new ICodeBlockElementType(); - - IElementType STATEMENTS = new ICodeFragmentElementType("STATEMENTS", StdLanguages.JAVA) { - public ASTNode parseContents(final ASTNode chameleon) { - if (JavaParserDefinition.USE_NEW_PARSER) { - return JavaParserUtil.parseFragment(chameleon, - new JavaParserUtil.ParserWrapper() { - public void parse(final PsiBuilder builder) { - StatementParser.parseStatements(builder); - } - }); - } - - final CharSequence chars = chameleon.getChars(); - final PsiManager manager = ((FileElement)chameleon).getManager(); - final CharTable table = SharedImplUtil.findCharTableByTree(chameleon); - JavaParsingContext context = new JavaParsingContext(table, LanguageLevel.HIGHEST); - return context.getStatementParsing().parseStatements(manager, null, chars, 0, chars.length(), 0); - } - }; - - IElementType EXPRESSION_TEXT = new ICodeFragmentElementType("EXPRESSION_TEXT", StdLanguages.JAVA) { - public ASTNode parseContents(final ASTNode chameleon) { - if (JavaParserDefinition.USE_NEW_PARSER) { - return JavaParserUtil.parseFragment(chameleon, - new JavaParserUtil.ParserWrapper() { - public void parse(final PsiBuilder builder) { - ExpressionParser.parse(builder); - } - }); - } - - final CharSequence chars = chameleon.getChars(); - final PsiManager manager = ((FileElement)chameleon).getManager(); - final JavaParsingContext context = new JavaParsingContext(SharedImplUtil.findCharTableByTree(chameleon), LanguageLevel.HIGHEST); - return context.getExpressionParsing().parseExpressionTextFragment(manager, chars, 0, chars.length(), 0); - } - }; - - IElementType REFERENCE_TEXT = new ICodeFragmentElementType("REFERENCE_TEXT", StdLanguages.JAVA) { - public ASTNode parseContents(final ASTNode chameleon) { - if (JavaParserDefinition.USE_NEW_PARSER) { - return JavaParserUtil.parseFragment(chameleon, - new JavaParserUtil.ParserWrapper() { - public void parse(final PsiBuilder builder) { - ReferenceParser.parseJavaCodeReference(builder, false, true, false, false); - } - }); - } - - final CharSequence chars = chameleon.getChars(); - return Parsing.parseJavaCodeReferenceText(((FileElement)chameleon).getManager(), chars, 0, chars.length(), - SharedImplUtil.findCharTableByTree(chameleon), true); - } - }; - - IElementType TYPE_TEXT = new ICodeFragmentElementType("TYPE_TEXT", StdLanguages.JAVA) { - public ASTNode parseContents(final ASTNode chameleon) { - if (JavaParserDefinition.USE_NEW_PARSER) { - return JavaParserUtil.parseFragment(chameleon, - new JavaParserUtil.ParserWrapper() { - public void parse(final PsiBuilder builder) { - ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); - expect(builder, JavaTokenType.ELLIPSIS); // todo[r.sh] parse ellipsis and fix PsiTypeCodeFragmentImpl.getType() - } - }); - } - - final CharSequence chars = chameleon.getChars(); - return Parsing.parseTypeText(((FileElement)chameleon).getManager(), null, chars, 0, chars.length(), 0, - SharedImplUtil.findCharTableByTree(chameleon)); - } - }; -} +/* + * Copyright 2000-2009 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 com.intellij.psi.impl.source.tree; + +import com.intellij.lang.*; +import com.intellij.lang.java.JavaParserDefinition; +import com.intellij.lang.java.parser.ExpressionParser; +import com.intellij.lang.java.parser.JavaParserUtil; +import com.intellij.lang.java.parser.ReferenceParser; +import com.intellij.lang.java.parser.StatementParser; +import com.intellij.lexer.JavaLexer; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; +import com.intellij.psi.impl.source.parsing.JavaParsingContext; +import com.intellij.psi.impl.source.parsing.Parsing; +import com.intellij.psi.impl.source.tree.java.PsiCodeBlockImpl; +import com.intellij.psi.text.BlockSupport; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IErrorCounterReparseableElementType; +import com.intellij.psi.tree.ILazyParseableElementType; +import com.intellij.psi.tree.ILightLazyParseableElementType; +import com.intellij.psi.tree.java.IJavaElementType; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.CharTable; +import com.intellij.util.diff.FlyweightCapableTreeStructure; + +import static com.intellij.lang.PsiBuilderUtil.expect; + +public interface JavaElementType { + IElementType CLASS = JavaStubElementTypes.CLASS; + IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; + IElementType ENUM_CONSTANT_INITIALIZER = JavaStubElementTypes.ENUM_CONSTANT_INITIALIZER; + + IElementType TYPE_PARAMETER_LIST = JavaStubElementTypes.TYPE_PARAMETER_LIST; + IElementType TYPE_PARAMETER = JavaStubElementTypes.TYPE_PARAMETER; + + IElementType IMPORT_LIST = JavaStubElementTypes.IMPORT_LIST; + IElementType IMPORT_STATEMENT = JavaStubElementTypes.IMPORT_STATEMENT; + IElementType IMPORT_STATIC_STATEMENT = JavaStubElementTypes.IMPORT_STATIC_STATEMENT; + + IElementType MODIFIER_LIST = JavaStubElementTypes.MODIFIER_LIST; + IElementType ANNOTATION = JavaStubElementTypes.ANNOTATION; + IElementType EXTENDS_LIST = JavaStubElementTypes.EXTENDS_LIST; + IElementType IMPLEMENTS_LIST = JavaStubElementTypes.IMPLEMENTS_LIST; + IElementType FIELD = JavaStubElementTypes.FIELD; + IElementType ENUM_CONSTANT = JavaStubElementTypes.ENUM_CONSTANT; + IElementType METHOD = JavaStubElementTypes.METHOD; + IElementType ANNOTATION_METHOD = JavaStubElementTypes.ANNOTATION_METHOD; + + IElementType CLASS_INITIALIZER = JavaStubElementTypes.CLASS_INITIALIZER; + IElementType PARAMETER = JavaStubElementTypes.PARAMETER; + IElementType PARAMETER_LIST = JavaStubElementTypes.PARAMETER_LIST; + IElementType EXTENDS_BOUND_LIST = JavaStubElementTypes.EXTENDS_BOUND_LIST; + IElementType THROWS_LIST = JavaStubElementTypes.THROWS_LIST; + + IElementType IMPORT_STATIC_REFERENCE = new IJavaElementType("IMPORT_STATIC_REFERENCE"); + IElementType TYPE = new IJavaElementType("TYPE"); + IElementType DIAMOND_TYPE = new IJavaElementType("DIAMOND_TYPE"); + IElementType REFERENCE_PARAMETER_LIST = new IJavaElementType("REFERENCE_PARAMETER_LIST", true); + IElementType JAVA_CODE_REFERENCE = new IJavaElementType("JAVA_CODE_REFERENCE"); + IElementType PACKAGE_STATEMENT = new IJavaElementType("PACKAGE_STATEMENT"); + + IElementType LOCAL_VARIABLE = new IJavaElementType("LOCAL_VARIABLE"); + IElementType REFERENCE_EXPRESSION = new IJavaElementType("REFERENCE_EXPRESSION"); + IElementType LITERAL_EXPRESSION = new IJavaElementType("LITERAL_EXPRESSION"); + IElementType THIS_EXPRESSION = new IJavaElementType("THIS_EXPRESSION"); + IElementType SUPER_EXPRESSION = new IJavaElementType("SUPER_EXPRESSION"); + IElementType PARENTH_EXPRESSION = new IJavaElementType("PARENTH_EXPRESSION"); + IElementType METHOD_CALL_EXPRESSION = new IJavaElementType("METHOD_CALL_EXPRESSION"); + IElementType TYPE_CAST_EXPRESSION = new IJavaElementType("TYPE_CAST_EXPRESSION"); + IElementType PREFIX_EXPRESSION = new IJavaElementType("PREFIX_EXPRESSION"); + IElementType POSTFIX_EXPRESSION = new IJavaElementType("POSTFIX_EXPRESSION"); + IElementType BINARY_EXPRESSION = new IJavaElementType("BINARY_EXPRESSION"); + IElementType CONDITIONAL_EXPRESSION = new IJavaElementType("CONDITIONAL_EXPRESSION"); + IElementType ASSIGNMENT_EXPRESSION = new IJavaElementType("ASSIGNMENT_EXPRESSION"); + IElementType NEW_EXPRESSION = new IJavaElementType("NEW_EXPRESSION"); + IElementType ARRAY_ACCESS_EXPRESSION = new IJavaElementType("ARRAY_ACCESS_EXPRESSION"); + IElementType ARRAY_INITIALIZER_EXPRESSION = new IJavaElementType("ARRAY_INITIALIZER_EXPRESSION"); + IElementType INSTANCE_OF_EXPRESSION = new IJavaElementType("INSTANCE_OF_EXPRESSION"); + IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new IJavaElementType("CLASS_OBJECT_ACCESS_EXPRESSION"); + IElementType EMPTY_EXPRESSION = new IJavaElementType("EMPTY_EXPRESSION", true); + + IElementType EXPRESSION_LIST = new IJavaElementType("EXPRESSION_LIST", true); + + IElementType EMPTY_STATEMENT = new IJavaElementType("EMPTY_STATEMENT"); + IElementType BLOCK_STATEMENT = new IJavaElementType("BLOCK_STATEMENT"); + IElementType EXPRESSION_STATEMENT = new IJavaElementType("EXPRESSION_STATEMENT"); + IElementType EXPRESSION_LIST_STATEMENT = new IJavaElementType("EXPRESSION_LIST_STATEMENT"); + IElementType DECLARATION_STATEMENT = new IJavaElementType("DECLARATION_STATEMENT"); + IElementType IF_STATEMENT = new IJavaElementType("IF_STATEMENT"); + IElementType WHILE_STATEMENT = new IJavaElementType("WHILE_STATEMENT"); + IElementType FOR_STATEMENT = new IJavaElementType("FOR_STATEMENT"); + IElementType FOREACH_STATEMENT = new IJavaElementType("FOREACH_STATEMENT"); + IElementType DO_WHILE_STATEMENT = new IJavaElementType("DO_WHILE_STATEMENT"); + IElementType SWITCH_STATEMENT = new IJavaElementType("SWITCH_STATEMENT"); + IElementType SWITCH_LABEL_STATEMENT = new IJavaElementType("SWITCH_LABEL_STATEMENT"); + IElementType BREAK_STATEMENT = new IJavaElementType("BREAK_STATEMENT"); + IElementType CONTINUE_STATEMENT = new IJavaElementType("CONTINUE_STATEMENT"); + IElementType RETURN_STATEMENT = new IJavaElementType("RETURN_STATEMENT"); + IElementType THROW_STATEMENT = new IJavaElementType("THROW_STATEMENT"); + IElementType SYNCHRONIZED_STATEMENT = new IJavaElementType("SYNCHRONIZED_STATEMENT"); + IElementType TRY_STATEMENT = new IJavaElementType("TRY_STATEMENT"); + IElementType LABELED_STATEMENT = new IJavaElementType("LABELED_STATEMENT"); + IElementType ASSERT_STATEMENT = new IJavaElementType("ASSERT_STATEMENT"); + + IElementType CATCH_SECTION = new IJavaElementType("CATCH_SECTION"); + + IElementType ANNOTATION_ARRAY_INITIALIZER = new IJavaElementType("ANNOTATION_ARRAY_INITIALIZER"); + IElementType NAME_VALUE_PAIR = new IJavaElementType("NAME_VALUE_PAIR", true); + IElementType ANNOTATION_PARAMETER_LIST = new IJavaElementType("ANNOTATION_PARAMETER_LIST", true); + IElementType METHOD_RECEIVER = new IJavaElementType("METHOD_RECEIVER"); + + class ICodeBlockElementType extends IErrorCounterReparseableElementType { + private ICodeBlockElementType() { + super("CODE_BLOCK", StdLanguages.JAVA); + } + + @Override + public ASTNode createNode(final CharSequence text) { + return new PsiCodeBlockImpl(text); + } + + @Override + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); + StatementParser.parseCodeBlockDeep(builder, true); + return builder.getTreeBuilt().getFirstChildNode(); + } + + final CharSequence seq = chameleon.getChars(); + + ASTNode original = chameleon.getUserData(BlockSupport.TREE_TO_BE_REPARSED); + ASTNode context = original != null ? original : chameleon.getTreeParent(); + + final PsiManager manager = context.getPsi().getManager(); + final CharTable table = SharedImplUtil.findCharTableByTree(context); + final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(TreeUtil.getFileElement((TreeElement)context).getPsi()); + JavaParsingContext parsingContext = new JavaParsingContext(table, languageLevel); + return parsingContext.getStatementParsing().parseCodeBlockText(manager, new JavaLexer(languageLevel), + seq, 0, seq.length(), 0).getFirstChildNode(); + } + + public int getErrorsCount(final CharSequence seq, final Project project) { + final Lexer lexer = new JavaLexer(LanguageLevel.HIGHEST); + + lexer.start(seq); + if (lexer.getTokenType() != JavaTokenType.LBRACE) return FATAL_ERROR; + lexer.advance(); + int balance = 1; + while (true) { + IElementType type = lexer.getTokenType(); + if (type == null) break; + if (balance == 0) return FATAL_ERROR; + if (type == JavaTokenType.LBRACE) { + balance++; + } + else if (type == JavaTokenType.RBRACE) { + balance--; + } + lexer.advance(); + } + return balance; + } + } + + class ICodeBlockNewElementType extends ICodeBlockElementType implements ILightLazyParseableElementType { + private ICodeBlockNewElementType() { } + + public FlyweightCapableTreeStructure parseContents(final LighterLazyParseableNode chameleon) { + final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); + StatementParser.parseCodeBlockDeep(builder, true); + return builder.getLightTree(); + } + } + + ILazyParseableElementType CODE_BLOCK = JavaParserDefinition.USE_NEW_PARSER ? new ICodeBlockNewElementType() : new ICodeBlockElementType(); + + IElementType STATEMENTS = new ICodeFragmentElementType("STATEMENTS", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + StatementParser.parseStatements(builder); + } + }); + } + + final CharSequence chars = chameleon.getChars(); + final PsiManager manager = ((FileElement)chameleon).getManager(); + final CharTable table = SharedImplUtil.findCharTableByTree(chameleon); + JavaParsingContext context = new JavaParsingContext(table, LanguageLevel.HIGHEST); + return context.getStatementParsing().parseStatements(manager, null, chars, 0, chars.length(), 0); + } + }; + + IElementType EXPRESSION_TEXT = new ICodeFragmentElementType("EXPRESSION_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ExpressionParser.parse(builder); + } + }); + } + + final CharSequence chars = chameleon.getChars(); + final PsiManager manager = ((FileElement)chameleon).getManager(); + final JavaParsingContext context = new JavaParsingContext(SharedImplUtil.findCharTableByTree(chameleon), LanguageLevel.HIGHEST); + return context.getExpressionParsing().parseExpressionTextFragment(manager, chars, 0, chars.length(), 0); + } + }; + + IElementType REFERENCE_TEXT = new ICodeFragmentElementType("REFERENCE_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ReferenceParser.parseJavaCodeReference(builder, false, true, false, false, false); + } + }); + } + + final CharSequence chars = chameleon.getChars(); + return Parsing.parseJavaCodeReferenceText(((FileElement)chameleon).getManager(), chars, 0, chars.length(), + SharedImplUtil.findCharTableByTree(chameleon), true); + } + }; + + IElementType TYPE_TEXT = new ICodeFragmentElementType("TYPE_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + expect(builder, JavaTokenType.ELLIPSIS); // todo[r.sh] parse ellipsis and fix PsiTypeCodeFragmentImpl.getType() + } + }); + } + + final CharSequence chars = chameleon.getChars(); + return Parsing.parseTypeText(((FileElement)chameleon).getManager(), null, chars, 0, chars.length(), 0, + SharedImplUtil.findCharTableByTree(chameleon)); + } + }; +} diff --git a/java/java-tests/testData/psi/parser-partial/expressions/New15.txt b/java/java-tests/testData/psi/parser-partial/expressions/New15.txt new file mode 100644 index 000000000000..80407edd16b7 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/New15.txt @@ -0,0 +1,24 @@ +PsiJavaFile:New15.java + PsiMethodCallExpression:new C.B() + PsiReferenceExpression:new C.B + PsiNewExpression:new C + PsiKeyword:new('new') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:C + PsiIdentifier:C('C') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:? + PsiJavaToken:QUEST('?') + PsiJavaToken:GT('>') + PsiErrorElement:'(' or '[' expected + + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index 0843e9f72c53..0e730aeed991 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -1,119 +1,120 @@ -/* - * Copyright 2000-2010 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 com.intellij.lang.java.parser.partial; - -import com.intellij.lang.PsiBuilder; -import com.intellij.lang.java.parser.ExpressionParser; -import com.intellij.lang.java.parser.JavaParsingTestCase; - - -public class ExpressionParserTest extends JavaParsingTestCase { - public ExpressionParserTest() { - super("parser-partial/expressions"); - } - - public void testAssignment0() { doParserTest("a = 0"); } - public void testAssignment1() { doParserTest("a ="); } - - public void testBinary0() { doParserTest("a + b"); } - public void testBinary1() { doParserTest("a < b"); } - - public void testCond0() { doParserTest("cond ? true : false"); } - public void testCond1() { doParserTest("cond ?"); } - public void testCond2() { doParserTest("cond ? true"); } - public void testCond3() { doParserTest("cond ? true :"); } - - public void testCondOr0() { doParserTest("a || b || c"); } - public void testCondOr1() { doParserTest("a ||"); } - - public void testOr0() { doParserTest("a | b | c"); } - public void testOr1() { doParserTest("a |"); } - - public void testInstanceOf0() { doParserTest("a instanceof String"); } - public void testInstanceOf1() { doParserTest("a instanceof"); } - - public void testNot0() { doParserTest("!!a"); } - public void testNot1() { doParserTest("!"); } - - public void testCast() { doParserTest("(Type)var"); } - - public void testParenth0() { doParserTest("(c)"); } - public void testParenth1() { doParserTest("(this).f--"); } - public void testParenth2() { doParserTest("("); } - public void testParenth3() { doParserTest("(a < b)"); } - - public void testNewInExprList() { doParserTest("call(new)"); } - - public void testNew0() { doParserTest("new A()"); } - public void testNew1() { doParserTest("new A[1][]"); } - public void testNew2() { doParserTest("new A[]"); } - public void testNew3() { doParserTest("new A[][1]"); } - public void testNew4() { doParserTest("new A[][]{null}"); } - public void testNew5() { doParserTest("new A[1]{null}"); } - public void testNew6() { doParserTest("new"); } - public void testNew7() { doParserTest("new A"); } - public void testNew8() { doParserTest("new A["); } - public void testNew9() { doParserTest("new int"); } - public void testNew10() { doParserTest("new int()"); } - public void testNew11() { doParserTest("new String[0"); } - public void testNew12() { doParserTest("new int[1][2]"); } - public void testNew13() { doParserTest("new int[1][][2]"); } - public void testNew14() { doParserTest("Q.new A()"); } - - public void testExprList0() { doParserTest("f(1,2)"); } - public void testExprList1() { doParserTest("f("); } - public void testExprList2() { doParserTest("f(1"); } - public void testExprList3() { doParserTest("f(1,"); } - public void testExprList4() { doParserTest("f(1,)"); } - public void testExprList5() { doParserTest("f(1,,2)"); } - public void testExprList6() { doParserTest("f(,2)"); } - - public void testArrayInitializer0() { doParserTest("{ }"); } - public void testArrayInitializer1() { doParserTest("{ 1 }"); } - public void testArrayInitializer2() { doParserTest("{ 1, }"); } - public void testArrayInitializer3() { doParserTest("{ 1,2 }"); } - public void testArrayInitializer4() { doParserTest("{ 1 2 }"); } - public void testArrayInitializer5() { doParserTest("{ { }"); } - public void testArrayInitializer6() { doParserTest("{ , }"); } - public void testArrayInitializer7() { doParserTest("{ , , 7 }"); } - - public void testPinesInReferenceExpression0() { doParserTest("Collections.sort(null)"); } - public void testPinesInReferenceExpression1() { doParserTest("this.sort(null)"); } - public void testPinesInReferenceExpression2() { doParserTest("super(null)"); } - - public void testGE0() { doParserTest("x >>>= 8 >> 2"); } - public void testGE1() { doParserTest("x >= 2"); } - - public void testIncompleteCast() { doParserTest("f((ArrayList) )"); } - public void testShiftRight() { doParserTest("x >>= 2"); } - - public void testIllegalWildcard() { doParserTest("this.foo()"); } - - public void testQualifiedSuperMethodCall0() { doParserTest("new D().super(0)"); } - public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } - public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } - public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } - public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } - - private void doParserTest(final String text) { - doParserTest(text, new TestParser() { - @Override - public void parse(final PsiBuilder builder) { - ExpressionParser.parse(builder); - } - }); - } -} +/* + * Copyright 2000-2010 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 com.intellij.lang.java.parser.partial; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.ExpressionParser; +import com.intellij.lang.java.parser.JavaParsingTestCase; + + +public class ExpressionParserTest extends JavaParsingTestCase { + public ExpressionParserTest() { + super("parser-partial/expressions"); + } + + public void testAssignment0() { doParserTest("a = 0"); } + public void testAssignment1() { doParserTest("a ="); } + + public void testBinary0() { doParserTest("a + b"); } + public void testBinary1() { doParserTest("a < b"); } + + public void testCond0() { doParserTest("cond ? true : false"); } + public void testCond1() { doParserTest("cond ?"); } + public void testCond2() { doParserTest("cond ? true"); } + public void testCond3() { doParserTest("cond ? true :"); } + + public void testCondOr0() { doParserTest("a || b || c"); } + public void testCondOr1() { doParserTest("a ||"); } + + public void testOr0() { doParserTest("a | b | c"); } + public void testOr1() { doParserTest("a |"); } + + public void testInstanceOf0() { doParserTest("a instanceof String"); } + public void testInstanceOf1() { doParserTest("a instanceof"); } + + public void testNot0() { doParserTest("!!a"); } + public void testNot1() { doParserTest("!"); } + + public void testCast() { doParserTest("(Type)var"); } + + public void testParenth0() { doParserTest("(c)"); } + public void testParenth1() { doParserTest("(this).f--"); } + public void testParenth2() { doParserTest("("); } + public void testParenth3() { doParserTest("(a < b)"); } + + public void testNewInExprList() { doParserTest("call(new)"); } + + public void testNew0() { doParserTest("new A()"); } + public void testNew1() { doParserTest("new A[1][]"); } + public void testNew2() { doParserTest("new A[]"); } + public void testNew3() { doParserTest("new A[][1]"); } + public void testNew4() { doParserTest("new A[][]{null}"); } + public void testNew5() { doParserTest("new A[1]{null}"); } + public void testNew6() { doParserTest("new"); } + public void testNew7() { doParserTest("new A"); } + public void testNew8() { doParserTest("new A["); } + public void testNew9() { doParserTest("new int"); } + public void testNew10() { doParserTest("new int()"); } + public void testNew11() { doParserTest("new String[0"); } + public void testNew12() { doParserTest("new int[1][2]"); } + public void testNew13() { doParserTest("new int[1][][2]"); } + public void testNew14() { doParserTest("Q.new A()"); } + public void testNew15() { doParserTest("new C.B()"); } + + public void testExprList0() { doParserTest("f(1,2)"); } + public void testExprList1() { doParserTest("f("); } + public void testExprList2() { doParserTest("f(1"); } + public void testExprList3() { doParserTest("f(1,"); } + public void testExprList4() { doParserTest("f(1,)"); } + public void testExprList5() { doParserTest("f(1,,2)"); } + public void testExprList6() { doParserTest("f(,2)"); } + + public void testArrayInitializer0() { doParserTest("{ }"); } + public void testArrayInitializer1() { doParserTest("{ 1 }"); } + public void testArrayInitializer2() { doParserTest("{ 1, }"); } + public void testArrayInitializer3() { doParserTest("{ 1,2 }"); } + public void testArrayInitializer4() { doParserTest("{ 1 2 }"); } + public void testArrayInitializer5() { doParserTest("{ { }"); } + public void testArrayInitializer6() { doParserTest("{ , }"); } + public void testArrayInitializer7() { doParserTest("{ , , 7 }"); } + + public void testPinesInReferenceExpression0() { doParserTest("Collections.sort(null)"); } + public void testPinesInReferenceExpression1() { doParserTest("this.sort(null)"); } + public void testPinesInReferenceExpression2() { doParserTest("super(null)"); } + + public void testGE0() { doParserTest("x >>>= 8 >> 2"); } + public void testGE1() { doParserTest("x >= 2"); } + + public void testIncompleteCast() { doParserTest("f((ArrayList) )"); } + public void testShiftRight() { doParserTest("x >>= 2"); } + + public void testIllegalWildcard() { doParserTest("this.foo()"); } + + public void testQualifiedSuperMethodCall0() { doParserTest("new D().super(0)"); } + public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } + public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } + public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } + public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } + + private void doParserTest(final String text) { + doParserTest(text, new TestParser() { + @Override + public void parse(final PsiBuilder builder) { + ExpressionParser.parse(builder); + } + }); + } +} diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index 19ae42eca34a..4a5662fcaedb 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -1,81 +1,81 @@ -/* - * Copyright 2000-2010 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 com.intellij.lang.java.parser.partial; - -import com.intellij.lang.PsiBuilder; -import com.intellij.lang.java.parser.JavaParsingTestCase; -import com.intellij.lang.java.parser.ReferenceParser; -import com.intellij.pom.java.LanguageLevel; - - -public class ReferenceParserTest extends JavaParsingTestCase { - public ReferenceParserTest() { - super("parser-partial/references"); - } - - public void testReference0() { doRefParserTest("a", false); } - public void testReference1() { doRefParserTest("a.", true); } - public void testReference2() { doRefParserTest("a.b", false); } - - public void testType0() { doTypeParserTest("int"); } - public void testType1() { doTypeParserTest("a.b"); } - public void testType2() { doTypeParserTest("int[]"); } - public void testType3() { doTypeParserTest("int[]["); } - public void testType4() { doTypeParserTest("Map>"); } - public void testType5() { doTypeParserTest("Object[]..."); } - public void testType6() { - withLevel(LanguageLevel.JDK_1_7, - new Runnable() { @Override - public void run() { doTypeParserTest("@English String @NonEmpty []"); } }); - } - public void testType7() { doTypeParserTest("Diamond<>"); } - - public void testTypeParams0() { doTypeParamsParserTest(""); } - public void testTypeParams1() { doTypeParamsParserTest(""); } - public void testTypeParams2() { doTypeParamsParserTest(""); } - public void testTypeParams4() { doTypeParamsParserTest(">"); } - public void testTypeParams6() { doTypeParamsParserTest(""); } - public void testTypeParams7() { doTypeParamsParserTest(""); } - - private void doRefParserTest(final String text, final boolean incomplete) { - doParserTest(text, new TestParser() { - @Override - public void parse(final PsiBuilder builder) { - ReferenceParser.parseJavaCodeReference(builder, incomplete, false, false, false); - } - }); - } - - private void doTypeParserTest(final String text) { - doParserTest(text, new TestParser() { - @Override - public void parse(final PsiBuilder builder) { - ReferenceParser.parseType(builder, ReferenceParser.DIAMONDS | ReferenceParser.ELLIPSIS); - } - }); - } - - private void doTypeParamsParserTest(final String text) { - doParserTest(text, new TestParser() { - @Override - public void parse(final PsiBuilder builder) { - ReferenceParser.parseTypeParameters(builder); - } - }); - } -} +/* + * Copyright 2000-2010 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 com.intellij.lang.java.parser.partial; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.JavaParsingTestCase; +import com.intellij.lang.java.parser.ReferenceParser; +import com.intellij.pom.java.LanguageLevel; + + +public class ReferenceParserTest extends JavaParsingTestCase { + public ReferenceParserTest() { + super("parser-partial/references"); + } + + public void testReference0() { doRefParserTest("a", false); } + public void testReference1() { doRefParserTest("a.", true); } + public void testReference2() { doRefParserTest("a.b", false); } + + public void testType0() { doTypeParserTest("int"); } + public void testType1() { doTypeParserTest("a.b"); } + public void testType2() { doTypeParserTest("int[]"); } + public void testType3() { doTypeParserTest("int[]["); } + public void testType4() { doTypeParserTest("Map>"); } + public void testType5() { doTypeParserTest("Object[]..."); } + public void testType6() { + withLevel(LanguageLevel.JDK_1_7, + new Runnable() { @Override + public void run() { doTypeParserTest("@English String @NonEmpty []"); } }); + } + public void testType7() { doTypeParserTest("Diamond<>"); } + + public void testTypeParams0() { doTypeParamsParserTest(""); } + public void testTypeParams1() { doTypeParamsParserTest(""); } + public void testTypeParams2() { doTypeParamsParserTest(""); } + public void testTypeParams4() { doTypeParamsParserTest(">"); } + public void testTypeParams6() { doTypeParamsParserTest(""); } + public void testTypeParams7() { doTypeParamsParserTest(""); } + + private void doRefParserTest(final String text, final boolean incomplete) { + doParserTest(text, new TestParser() { + @Override + public void parse(final PsiBuilder builder) { + ReferenceParser.parseJavaCodeReference(builder, incomplete, false, false, false, false); + } + }); + } + + private void doTypeParserTest(final String text) { + doParserTest(text, new TestParser() { + @Override + public void parse(final PsiBuilder builder) { + ReferenceParser.parseType(builder, ReferenceParser.DIAMONDS | ReferenceParser.ELLIPSIS); + } + }); + } + + private void doTypeParamsParserTest(final String text) { + doParserTest(text, new TestParser() { + @Override + public void parse(final PsiBuilder builder) { + ReferenceParser.parseTypeParameters(builder); + } + }); + } +}