From ddbc9d9b695a237f4fe850c24a85dc6bd3af59f8 Mon Sep 17 00:00:00 2001 From: "Maxim.Medvedev" Date: Fri, 6 Apr 2012 11:59:21 +0400 Subject: [PATCH] IDEA-66792 groovy: bad code green: applicability of annotation is not verified with regard to target element --- .../src/com/intellij/psi/PsiAnnotation.java | 1 + .../source/tree/java/PsiAnnotationImpl.java | 23 +++++--- .../groovy/annotator/GroovyAnnotator.java | 24 ++++++-- .../groovy/lang/parser/GroovyPsiCreator.java | 4 +- .../GrAnnotationArrayInitializer.java | 5 +- .../annotation/GrAnnotationMemberValue.java | 4 +- ... => GrAnnotationArrayInitializerImpl.java} | 4 +- .../annotation/GrAnnotationImpl.java | 59 +++++++++++++++++++ .../groovy/lang/GroovyHighlightingTest.groovy | 50 ++++++++++++++++ 9 files changed, 153 insertions(+), 21 deletions(-) rename plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/{GrAnnotationArrrayInitializerImpl.java => GrAnnotationArrayInitializerImpl.java} (91%) diff --git a/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java b/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java index 6b448421c6f2..25b0ea9242ac 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java @@ -101,5 +101,6 @@ public interface PsiAnnotation extends PsiAnnotationMemberValue, PsiMetaOwner { * * @return annotation owner */ + @Nullable PsiAnnotationOwner getOwner(); } 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 68a4e468623f..b6fe75031c4e 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 @@ -26,6 +26,7 @@ import com.intellij.psi.impl.meta.MetaRegistry; import com.intellij.psi.impl.source.JavaStubPsiElement; import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.meta.PsiMetaData; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.PairFunction; import org.jetbrains.annotations.NonNls; @@ -120,6 +121,7 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp return MetaRegistry.getMetaBase(this); } + @Nullable @Override public PsiAnnotationOwner getOwner() { PsiElement parent = getParent(); @@ -160,6 +162,14 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp return !strict; } PsiClass annotationType = (PsiClass)resolved; + return isAnnotationApplicable(strict, annotationType, elementTypeFields, annotation.getManager(), annotation.getResolveScope()); + } + + public static boolean isAnnotationApplicable(boolean strict, + PsiClass annotationType, + String[] elementTypeFields, + PsiManager manager, + GlobalSearchScope resolveScope) { PsiAnnotation target = annotationType.getModifierList().findAnnotation(CommonClassNames.TARGET_ANNOTATION_FQ_NAME); if (target == null) { //todo hack: ambiguity in spec @@ -173,8 +183,7 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp PsiAnnotationMemberValue value = attributes[0].getValue(); LOG.assertTrue(elementTypeFields.length > 0); - PsiManager manager = annotation.getManager(); - PsiClass elementTypeClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.annotation.ElementType", annotation.getResolveScope()); + PsiClass elementTypeClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.annotation.ElementType", resolveScope); if (elementTypeClass == null) { //todo hack return !strict; @@ -187,19 +196,19 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp if (value instanceof PsiArrayInitializerMemberValue) { PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)value).getInitializers(); for (PsiAnnotationMemberValue initializer : initializers) { - if (initializer instanceof PsiReferenceExpression) { - PsiReferenceExpression refExpr = (PsiReferenceExpression)initializer; - if (refExpr.isReferenceTo(field)) return true; + if (initializer instanceof PsiReference) { + if (((PsiReference)initializer).isReferenceTo(field)) return true; } } } - else if (value instanceof PsiReferenceExpression) { - if (((PsiReferenceExpression)value).isReferenceTo(field)) return true; + else if (value instanceof PsiReference) { + if (((PsiReference)value).isReferenceTo(field)) return true; } } return false; } + @Nullable public static String[] getApplicableElementTypeFields(PsiElement owner) { if (owner instanceof PsiClass) { PsiClass aClass = (PsiClass)owner; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index dbad4d4ee81e..a64741297257 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.annotator; import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.daemon.impl.quickfix.AddMethodBodyFix; import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix; @@ -100,6 +101,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDef import org.jetbrains.plugins.groovy.lang.psi.api.types.*; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner; import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; @@ -162,7 +164,6 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { final PsiElement parent = refElement.getParent(); GroovyResolveResult resolveResult = refElement.advancedResolve(); - highlightAnnotation(myHolder, refElement, resolveResult); if (refElement.getReferenceName() != null) { if (parent instanceof GrImportStatement && ((GrImportStatement)parent).isStatic() && refElement.multiResolve(false).length > 0) { @@ -1092,15 +1093,30 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { } } - @Override + public void visitAnnotation(GrAnnotation annotation) { super.visitAnnotation(annotation); final GrCodeReferenceElement ref = annotation.getClassReference(); + GroovyResolveResult resolveResult = ref.advancedResolve(); final PsiElement resolved = ref.resolve(); - assert resolved == null || resolved instanceof PsiClass; - if (resolved != null && !((PsiClass)resolved).isAnnotationType()) { + highlightAnnotation(myHolder, ref, resolveResult); + + if (resolved == null) return; + assert resolved instanceof PsiClass; + + PsiClass anno = (PsiClass) resolved; + if (!anno.isAnnotationType()) { myHolder.createErrorAnnotation(ref, GroovyBundle.message("class.is.not.annotation", ((PsiClass)resolved).getQualifiedName())); + return; + } + PsiElement parent = annotation.getParent(); + PsiElement owner = parent.getParent(); + String[] elementTypeFields = GrAnnotationImpl.getApplicableElementTypeFields(parent instanceof PsiModifierList ? owner : parent); + if (!GrAnnotationImpl.isAnnotationApplicableTo(annotation, false, elementTypeFields)) { + String description = JavaErrorMessages + .message("annotation.not.applicable", ref.getText(), JavaErrorMessages.message("annotation.target." + elementTypeFields[0])); + myHolder.createErrorAnnotation(ref, description); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java index 6f5d0be5057f..ecbecfc4f1f5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java @@ -29,7 +29,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrLabelImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrListOrMapImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrThrowsClauseImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArgumentListImpl; -import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArrrayInitializerImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArrayInitializerImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationNameValuePairImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrModifierListImpl; @@ -106,7 +106,7 @@ public class GroovyPsiCreator implements GroovyElementTypes { if (elem == MODIFIERS) return new GrModifierListImpl(node); if (elem == ANNOTATION) return new GrAnnotationImpl(node); if (elem == ANNOTATION_ARGUMENTS) return new GrAnnotationArgumentListImpl(node); - if (elem == ANNOTATION_ARRAY_INITIALIZER) return new GrAnnotationArrrayInitializerImpl(node); + if (elem == ANNOTATION_ARRAY_INITIALIZER) return new GrAnnotationArrayInitializerImpl(node); if (elem == ANNOTATION_MEMBER_VALUE_PAIR) return new GrAnnotationNameValuePairImpl(node); if (elem == DEFAULT_ANNOTATION_VALUE) return new GrDefaultAnnotationValueImpl(node); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationArrayInitializer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationArrayInitializer.java index 0032f6a560a8..40be5405267c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationArrayInitializer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationArrayInitializer.java @@ -16,15 +16,14 @@ package org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation; +import com.intellij.psi.PsiArrayInitializerMemberValue; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; /** * @author: Dmitry.Krasilschikov * @date: 04.04.2007 */ -public interface GrAnnotationArrayInitializer extends GrAnnotationMemberValue { - +public interface GrAnnotationArrayInitializer extends GrAnnotationMemberValue, PsiArrayInitializerMemberValue { @NotNull GrAnnotationMemberValue[] getInitializers(); } \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationMemberValue.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationMemberValue.java index c56504933964..b6d7d1936850 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationMemberValue.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/auxiliary/modifiers/annotation/GrAnnotationMemberValue.java @@ -16,10 +16,8 @@ package org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition; -import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiAnnotationMemberValue; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; /** * @author: Dmitry.Krasilschikov diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrrayInitializerImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrayInitializerImpl.java similarity index 91% rename from plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrrayInitializerImpl.java rename to plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrayInitializerImpl.java index 97eff3656d34..7216c02f214c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrrayInitializerImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArrayInitializerImpl.java @@ -29,8 +29,8 @@ import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA; * @author: Dmitry.Krasilschikov * @date: 04.04.2007 */ -public class GrAnnotationArrrayInitializerImpl extends GroovyPsiElementImpl implements GrAnnotationArrayInitializer { - public GrAnnotationArrrayInitializerImpl(@NotNull ASTNode node) { +public class GrAnnotationArrayInitializerImpl extends GroovyPsiElementImpl implements GrAnnotationArrayInitializer { + public GrAnnotationArrayInitializerImpl(@NotNull ASTNode node) { super(node); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationImpl.java index 96fcdda8e0b6..268726ca0cf3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationImpl.java @@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.impl.PsiImplUtil; +import com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl; import com.intellij.psi.meta.PsiMetaData; import com.intellij.util.PairFunction; import org.jetbrains.annotations.NonNls; @@ -30,7 +31,14 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.GrStubElementBase; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrAnnotationStub; @@ -118,4 +126,55 @@ public class GrAnnotationImpl extends GrStubElementBase implem return (PsiAnnotationOwner)getParent(); } + @Nullable + public static String[] getApplicableElementTypeFields(PsiElement owner) { + if (owner instanceof PsiClass) { + PsiClass aClass = (PsiClass)owner; + if (aClass.isAnnotationType()) { + return new String[]{"ANNOTATION_TYPE", "TYPE"}; + } + else if (aClass instanceof GrTypeParameter) { + return new String[]{"TYPE_PARAMETER"}; + } + else { + return new String[]{"TYPE"}; + } + } + if (owner instanceof GrMethod) { + if (((PsiMethod)owner).isConstructor()) { + return new String[]{"CONSTRUCTOR"}; + } + else { + return new String[]{"METHOD"}; + } + } + if (owner instanceof GrField) { + return new String[]{"FIELD"}; + } + if (owner instanceof GrParameter) { + return new String[]{"PARAMETER"}; + } + if (owner instanceof GrVariable) { + return new String[]{"LOCAL_VARIABLE"}; + } + if (owner instanceof GrPackageDefinition) { + return new String[]{"PACKAGE"}; + } + if (owner instanceof GrTypeElement) { + return new String[]{"TYPE_USE"}; + } + + return null; + } + + + public static boolean isAnnotationApplicableTo(GrAnnotation annotation, boolean strict, String... elementTypeFields) { + if (elementTypeFields == null) return true; + GrCodeReferenceElement nameRef = annotation.getClassReference(); + PsiElement resolved = nameRef.resolve(); + if (resolved instanceof PsiClass && ((PsiClass)resolved).isAnnotationType()) { + return PsiAnnotationImpl.isAnnotationApplicable(strict, (PsiClass)resolved, elementTypeFields, annotation.getManager(),annotation.getResolveScope()); + } + return !strict; + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index 75de622f3b7d..076050f6afc5 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -738,4 +738,54 @@ private def handleImplicitBind(arg) { }''') myFixture.testHighlighting(true, false, false) } + + public void testTargetAnnotationInsideGroovy1() { + myFixture.addFileToProject('Ann.groovy', ''' +import java.lang.annotation.Target + +import static java.lang.annotation.ElementType.* + +@Target(FIELD) +@interface Ann {} +''') + + myFixture.configureByText('_.groovy', ''' +@Ann +class C { + @Ann + def foo + + def ar() { + @Ann + def x + } +}''') + + myFixture.testHighlighting(true, false, false) + } + + public void testTargetAnnotationInsideGroovy2() { + myFixture.addFileToProject('Ann.groovy', ''' +import java.lang.annotation.Target + +import static java.lang.annotation.ElementType.* + +@Target(value=[FIELD, TYPE]) +@interface Ann {} +''') + + myFixture.configureByText('_.groovy', ''' +@Ann +class C { + @Ann + def foo + + def ar() { + @Ann + def x + } +}''') + myFixture.testHighlighting(true, false, false) + } + } \ No newline at end of file