From 8bb33ccdca190cb168fc758b9790566df5f07399 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 31 Mar 2013 23:12:19 +0200 Subject: [PATCH] Type annotations on C-style arrays are recognized now --- .../lang/java/parser/DeclarationParser.java | 37 +++++++----- .../impl/source/tree/JavaSharedImplUtil.java | 59 ++++++++++++++----- .../source/tree/java/PsiAnnotationImpl.java | 6 ++ .../annotations/typeAnnotations.java | 2 + .../annotation/TypeAnnotations.java | 2 + .../annotation/TypeAnnotations.txt | 51 ++++++++++++++++ .../com/intellij/psi/JavaStubBuilderTest.java | 12 +++- 7 files changed, 139 insertions(+), 30 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 10db352f3aa7..54e23dd156b3 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -405,7 +405,7 @@ public class DeclarationParser { private PsiBuilder.Marker parseMethodFromLeftParenth(PsiBuilder builder, PsiBuilder.Marker declaration, boolean anno, boolean constructor) { parseParameterList(builder); - eatBrackets(builder, constructor, "expected.semicolon"); + eatBrackets(builder, constructor ? "expected.semicolon" : null); myParser.getReferenceParser().parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA); @@ -600,7 +600,7 @@ public class DeclarationParser { if (expect(builder, JavaTokenType.IDENTIFIER)) { if (!resource) { - eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, "expected.rparen"); + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg ? "expected.rparen" : null); done(param, JavaElementType.PARAMETER); return param; } @@ -644,7 +644,7 @@ public class DeclarationParser { while (true) { shouldRollback = true; - if (!eatBrackets(builder, false, null)) { + if (!eatBrackets(builder, null)) { unclosed = true; } @@ -706,26 +706,33 @@ public class DeclarationParser { return declaration; } - private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, - @Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) { - if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; + private boolean eatBrackets(PsiBuilder builder, @Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) { + IElementType tokenType = builder.getTokenType(); + if (tokenType != JavaTokenType.LBRACKET && tokenType != JavaTokenType.AT) return true; - final PsiBuilder.Marker marker = isError ? builder.mark() : null; + PsiBuilder.Marker marker = errorKey != null ? 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; + int count = 0; + while (true) { + parseAnnotations(builder); + if (!expect(builder, JavaTokenType.LBRACKET)) { break; } + ++count; + if (!expect(builder, JavaTokenType.RBRACKET)) { + break; + } + ++count; } - if (marker != null && errorKey != null) { + boolean paired = count % 2 == 0; + if (marker != null) { marker.error(JavaErrorMessages.message(errorKey)); } - - return result; + else if (!paired) { + error(builder, JavaErrorMessages.message("expected.rbracket")); + } + return paired; } @Nullable diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java index 917458d55f92..50b2f5b45647 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 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. @@ -27,7 +27,12 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; public class JavaSharedImplUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaSharedImplUtil"); @@ -35,7 +40,6 @@ public class JavaSharedImplUtil { private JavaSharedImplUtil() { } public static PsiType getType(@NotNull PsiTypeElement typeElement, @NotNull PsiElement anchor, @NotNull PsiElement context) { - int cStyleArrayCount = countBrackets(anchor); PsiType type; if (typeElement instanceof PsiTypeElementImpl) { type = ((PsiTypeElementImpl)typeElement).getDetachedType(context); @@ -43,34 +47,61 @@ public class JavaSharedImplUtil { else { type = typeElement.getType(); } - for (int i = 0; i < cStyleArrayCount; i++) { - type = type.createArrayType(); + + List allAnnotations = collectAnnotations(anchor); + for (PsiAnnotation[] annotations : allAnnotations) { + type = type.createArrayType(annotations); } + return type; } public static PsiType getTypeNoResolve(@NotNull PsiTypeElement typeElement, @NotNull PsiElement anchor, @NotNull PsiElement context) { - int cStyleArrayCount = countBrackets(anchor); PsiType type = typeElement.getTypeNoResolve(context); - for (int i = 0; i < cStyleArrayCount; i++) { - type = type.createArrayType(); + + List allAnnotations = collectAnnotations(anchor); + for (PsiAnnotation[] annotations : allAnnotations) { + type = type.createArrayType(annotations); } + return type; } - private static int countBrackets(PsiElement anchor) { - int cStyleArrayCount = 0; - ASTNode name = SourceTreeToPsiMap.psiToTreeNotNull(anchor); - for (ASTNode child = name.getTreeNext(); child != null; child = child.getTreeNext()) { - IElementType i = child.getElementType(); + private static List collectAnnotations(PsiElement anchor) { + List annotations = new SmartList(); + + List current = null; + for (PsiElement child = anchor.getNextSibling(); child != null; child = child.getNextSibling()) { + if (child instanceof PsiAnnotation) { + if (current == null) current = new SmartList(); + current.add((PsiAnnotation)child); + continue; + } + + IElementType i = child.getNode().getElementType(); if (i == JavaTokenType.LBRACKET) { - cStyleArrayCount++; + annotations.add(ContainerUtil.toArray(current, PsiAnnotation.ARRAY_FACTORY)); + current = null; } else if (i != JavaTokenType.RBRACKET && !ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(i)) { break; } } - return cStyleArrayCount; + + return annotations; + } + + @Nullable + public static PsiType findAnnotatedSubtype(@Nullable PsiType type, @NotNull PsiAnnotation annotation) { + while (type instanceof PsiArrayType) { + for (PsiAnnotation a : type.getAnnotations()) { + if (a == annotation) { + return type; + } + } + type = ((PsiArrayType)type).getComponentType(); + } + return null; } public static void normalizeBrackets(PsiVariable variable) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java index 7f3dd00caac9..b597d09c81a5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java @@ -24,6 +24,7 @@ import com.intellij.psi.impl.java.stubs.PsiAnnotationStub; import com.intellij.psi.impl.meta.MetaRegistry; import com.intellij.psi.impl.source.JavaStubPsiElement; import com.intellij.psi.impl.source.PsiClassReferenceType; +import com.intellij.psi.impl.source.tree.JavaSharedImplUtil; import com.intellij.psi.meta.PsiMetaData; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.PairFunction; @@ -123,6 +124,11 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp return ((PsiNewExpression)parent).getOwner(this); } + if (parent instanceof PsiMethod) { + PsiType type = ((PsiMethod)parent).getReturnType(); + return JavaSharedImplUtil.findAnnotatedSubtype(type, this); + } + if (parent instanceof PsiReferenceExpression) { PsiElement ctx = parent.getParent(); if (ctx instanceof PsiMethodReferenceExpression) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java index bea2fc3c6fdb..5b8eae7b9014 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java @@ -108,6 +108,8 @@ class Outer { new Boolean @TA @TA [42]; } + int @TA [] mixedArrays() @TA [] @TA { return new int[0][0]; } + @TA Outer() { } class MyClass<@TA @TPA T> { } diff --git a/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.java b/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.java index 3c1bc40022c9..a74d4d6bbfad 100644 --- a/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.java +++ b/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.java @@ -65,6 +65,8 @@ class SpecSamples { @NonNegative int @NonEmpty [] ints = new @NonNegative int @MinSize(2) [2]; } + int m() @Slowpoke [] @Slowbro [] { return null; } + // // 4. A type annotation is permitted in front of a constructor declaration ... // diff --git a/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.txt b/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.txt index 11fde7c10c3a..54cf4b873ea8 100644 --- a/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.txt +++ b/java/java-tests/testData/psi/parser-full/annotationParsing/annotation/TypeAnnotations.txt @@ -1404,6 +1404,57 @@ PsiJavaFile:TypeAnnotations.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n\n ') + PsiMethod:m + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:m('m') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Slowpoke + PsiIdentifier:Slowpoke('Slowpoke') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiWhiteSpace(' ') + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Slowbro + PsiIdentifier:Slowbro('Slowbro') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiReturnStatement + PsiKeyword:return('return') + PsiWhiteSpace(' ') + PsiLiteralExpression:null + PsiJavaToken:NULL_KEYWORD('null') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n\n ') PsiComment(END_OF_LINE_COMMENT)('//') PsiWhiteSpace('\n ') PsiComment(END_OF_LINE_COMMENT)('// 4. A type annotation is permitted in front of a constructor declaration ...') diff --git a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java index cb56f18194c6..13116a024979 100644 --- a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java @@ -380,6 +380,7 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { " m(@A6 C::m);\n" + " @A7 T @A8[] @A9[] a = new @A7 T @A8[0] @A9[0];\n" + " }\n" + + " int @A [] v() @A [] { }\n" + "}", "PsiJavaFileStub []\n" + @@ -447,7 +448,16 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { " ANNOTATION:PsiAnnotationStub[@A8]\n" + " ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" + " ANNOTATION:PsiAnnotationStub[@A9]\n" + - " ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n"); + " ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" + + " METHOD:PsiMethodStub[v:int[][]]\n" + + " MODIFIER_LIST:PsiModifierListStub[mask=4096]\n" + + " TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" + + " ANNOTATION:PsiAnnotationStub[@A]\n" + + " ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" + + " PARAMETER_LIST:PsiParameterListStub\n" + + " ANNOTATION:PsiAnnotationStub[@A]\n" + + " ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" + + " THROWS_LIST:PsiRefListStub[THROWS_LIST:]\n"); } public void testSOEProof() {