From 0c5937c8b7de347e405143e7100e261cfe1ffce4 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 25 Apr 2018 09:40:33 +0200 Subject: [PATCH] support @TypeQualifierNickname usages without its declaration (IDEA-182842) --- .../NullableNotNullManagerImpl.java | 37 ++++++++++++++++--- .../fixture/TypeQualifierNickname.java | 6 +-- .../DataFlowInspectionTest.java | 24 ++++++++---- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index e71160dbf3fa..cf6248e23c8e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -9,7 +9,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -21,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -87,17 +90,39 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> { List result = new ArrayList<>(); GlobalSearchScope scope = GlobalSearchScope.allScope(myProject); - for (PsiClass tqNick : JavaPsiFacade.getInstance(myProject).findClasses(TYPE_QUALIFIER_NICKNAME, scope)) { - result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), candidate -> { - String qname = candidate.getQualifiedName(); - if (qname == null || qname.startsWith("javax.annotation.")) return false; - return getNickNamedNullability(candidate) != Nullness.UNKNOWN; - })); + PsiClass[] nickDeclarations = JavaPsiFacade.getInstance(myProject).findClasses(TYPE_QUALIFIER_NICKNAME, scope); + for (PsiClass tqNick : nickDeclarations) { + result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), NullableNotNullManagerImpl::isNullabilityNickName)); + } + if (nickDeclarations.length == 0) { + result.addAll(getUnresolvedNicknameUsages()); } return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); }); } + // some frameworks use jsr305 annotations but don't have them in classpath + private List getUnresolvedNicknameUsages() { + List result = new ArrayList<>(); + Collection annotations = JavaAnnotationIndex.getInstance().get(StringUtil.getShortName(TYPE_QUALIFIER_NICKNAME), myProject, GlobalSearchScope.allScope(myProject)); + for (PsiAnnotation annotation : annotations) { + PsiElement context = annotation.getContext(); + if (context instanceof PsiModifierList && context.getContext() instanceof PsiClass) { + PsiClass ownerClass = (PsiClass)context.getContext(); + if (ownerClass.isAnnotationType() && isNullabilityNickName(ownerClass)) { + result.add(ownerClass); + } + } + } + return result; + } + + private static boolean isNullabilityNickName(@NotNull PsiClass candidate) { + String qname = candidate.getQualifiedName(); + if (qname == null || qname.startsWith("javax.annotation.")) return false; + return getNickNamedNullability(candidate) != Nullness.UNKNOWN; + } + private static Nullness getNickNamedNullability(@NotNull PsiClass psiClass) { if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return Nullness.UNKNOWN; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java index c2f20a40f1a3..d5c2dbee593b 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java @@ -1,15 +1,11 @@ import javax.annotation.meta.*; -@TypeQualifierNickname() -@javax.annotation.Nonnull(when = When.MAYBE) -@interface NullableNick {} - interface UnknownInterface { void foo(String s); } class ImplWithNotNull implements UnknownInterface { - public void foo(@NullableNick String s) { + public void foo(@bar.NullableNick String s) { System.out.println(s.hashCode()); } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index 2a7911d7abff..b8dea85f11a5 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -369,16 +369,28 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { } public void testTypeQualifierNickname() { + myFixture.addClass("package javax.annotation.meta; public @interface TypeQualifierNickname {}"); addJavaxNullabilityAnnotations(myFixture); + addNullableNick(); - myFixture.addClass("package bar;" + - "import javax.annotation.meta.*;" + - "@TypeQualifierNickname() @javax.annotation.NonNull(when = Maybe.MAYBE) " + - "public @interface NullableNick {}"); - doTest(); } + public void testTypeQualifierNicknameWithoutDeclarations() { + addJavaxNullabilityAnnotations(myFixture); + addNullableNick(); + + myFixture.enableInspections(new DataFlowInspection()); + myFixture.testHighlighting(true, false, true, "TypeQualifierNickname.java"); + } + + private void addNullableNick() { + myFixture.addClass("package bar;" + + "@javax.annotation.meta.TypeQualifierNickname() " + + "@javax.annotation.Nonnull(when = javax.annotation.meta.When.MAYBE) " + + "public @interface NullableNick {}"); + } + public static void addJavaxDefaultNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) { fixture.addClass("package javax.annotation;" + "@javax.annotation.meta.TypeQualifierDefault(java.lang.annotation.ElementType.PARAMETER) @javax.annotation.Nonnull " + @@ -393,8 +405,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { "public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}"); fixture.addClass("package javax.annotation.meta;" + "public enum When { ALWAYS, UNKNOWN, MAYBE, NEVER }"); - fixture.addClass("package javax.annotation.meta;" + - "public @interface TypeQualifierNickname {}"); fixture.addClass("package javax.annotation;" + "import javax.annotation.meta.*;" +