From 6f8c9cd291e8ddf6569dfa8a98532bf7dc456baf Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jul 2014 10:13:19 +0200 Subject: [PATCH] IDEA-125281 Support @TypeQualifierDefault for nullable/notnull warnings --- .../impl/FieldFromParameterUtils.java | 4 +- .../codeInsight/AnnotationTargetUtil.java | 136 ++++++++++++++++++ .../intellij/codeInsight/AnnotationUtil.java | 4 +- .../codeInsight/NullableNotNullManager.java | 75 +++++++--- .../com/intellij/psi/impl/PsiImplUtil.java | 103 +------------ .../fixture/CustomTypeQualifierDefault.java | 25 ++++ .../DataFlowInspectionTest.java | 32 ++++- 7 files changed, 256 insertions(+), 123 deletions(-) create mode 100644 java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/CustomTypeQualifierDefault.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java index 52adc200eb0c..c98b002ae664 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java @@ -187,12 +187,12 @@ public final class FieldFromParameterUtils { modifierList.setModifierProperty(PsiModifier.FINAL, isFinal); final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - final String nullable = manager.getNullable(parameter); + final PsiAnnotation nullable = manager.getNullableAnnotation(parameter, false); if (nullable != null && !manager.isContainerAnnotation(nullable)) { modifierList.addAfter(factory.createAnnotationFromText("@" + nullable, field), null); } else if (isFinal) { - final String notNull = manager.getNotNull(parameter); + final PsiAnnotation notNull = manager.getNotNullAnnotation(parameter, false); if (notNull != null && !manager.isContainerAnnotation(notNull)) { modifierList.addAfter(factory.createAnnotationFromText("@" + notNull, field), null); } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java new file mode 100644 index 000000000000..c7d9e303aae3 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java @@ -0,0 +1,136 @@ +/* + * Copyright 2000-2014 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.codeInsight; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Set; + +/** + * @author peter + */ +public class AnnotationTargetUtil { + public static final Set DEFAULT_TARGETS = Collections.unmodifiableSet(ContainerUtil.newHashSet( + PsiAnnotation.TargetType.PACKAGE, PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.ANNOTATION_TYPE, + PsiAnnotation.TargetType.FIELD, PsiAnnotation.TargetType.METHOD, PsiAnnotation.TargetType.CONSTRUCTOR, + PsiAnnotation.TargetType.PARAMETER, PsiAnnotation.TargetType.LOCAL_VARIABLE)); + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.AnnotationUtil"); + private static final PsiAnnotation.TargetType[] PACKAGE_TARGETS = {PsiAnnotation.TargetType.PACKAGE}; + private static final PsiAnnotation.TargetType[] TYPE_USE_TARGETS = {PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] ANNOTATION_TARGETS = {PsiAnnotation.TargetType.ANNOTATION_TYPE, PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] TYPE_TARGETS = {PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] TYPE_PARAMETER_TARGETS = { + PsiAnnotation.TargetType.TYPE_PARAMETER, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] CONSTRUCTOR_TARGETS = {PsiAnnotation.TargetType.CONSTRUCTOR, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] METHOD_TARGETS = {PsiAnnotation.TargetType.METHOD, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] FIELD_TARGETS = {PsiAnnotation.TargetType.FIELD, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] PARAMETER_TARGETS = {PsiAnnotation.TargetType.PARAMETER, PsiAnnotation.TargetType.TYPE_USE}; + private static final PsiAnnotation.TargetType[] LOCAL_VARIABLE_TARGETS ={ + PsiAnnotation.TargetType.LOCAL_VARIABLE, PsiAnnotation.TargetType.TYPE_USE}; + + @NotNull + public static PsiAnnotation.TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { + if (owner == null) { + return PsiAnnotation.TargetType.EMPTY_ARRAY; + } + + if (owner instanceof PsiType || owner instanceof PsiTypeElement) { + return TYPE_USE_TARGETS; + } + + if (owner instanceof PsiTypeParameter) { + return TYPE_PARAMETER_TARGETS; + } + + if (owner instanceof PsiModifierList) { + PsiElement element = ((PsiModifierList)owner).getParent(); + if (element instanceof PsiPackageStatement) { + return PACKAGE_TARGETS; + } + if (element instanceof PsiClass) { + if (((PsiClass)element).isAnnotationType()) { + return ANNOTATION_TARGETS; + } + else { + return TYPE_TARGETS; + } + } + if (element instanceof PsiMethod) { + if (((PsiMethod)element).isConstructor()) { + return CONSTRUCTOR_TARGETS; + } + else { + return METHOD_TARGETS; + } + } + if (element instanceof PsiField) { + return FIELD_TARGETS; + } + if (element instanceof PsiParameter) { + return PARAMETER_TARGETS; + } + if (element instanceof PsiLocalVariable) { + return LOCAL_VARIABLE_TARGETS; + } + } + + return PsiAnnotation.TargetType.EMPTY_ARRAY; + } + + @Nullable + public static Set extractRequiredAnnotationTargets(@Nullable PsiAnnotationMemberValue value) { + if (value instanceof PsiReference) { + PsiAnnotation.TargetType targetType = translateTargetRef((PsiReference)value); + if (targetType != null) { + return Collections.singleton(targetType); + } + } + else if (value instanceof PsiArrayInitializerMemberValue) { + Set targets = ContainerUtil.newHashSet(); + for (PsiAnnotationMemberValue initializer : ((PsiArrayInitializerMemberValue)value).getInitializers()) { + if (initializer instanceof PsiReference) { + PsiAnnotation.TargetType targetType = translateTargetRef((PsiReference)initializer); + if (targetType != null) { + targets.add(targetType); + } + } + } + return targets; + } + + return null; + } + + @Nullable + private static PsiAnnotation.TargetType translateTargetRef(@NotNull PsiReference reference) { + PsiElement field = reference.resolve(); + if (field instanceof PsiEnumConstant) { + String name = ((PsiEnumConstant)field).getName(); + try { + return PsiAnnotation.TargetType.valueOf(name); + } + catch (IllegalArgumentException e) { + LOG.warn("Unknown target: " + name); + } + } + return null; + } +} diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java index c2c79a9d9d8a..25034e9ab7ba 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java @@ -379,14 +379,14 @@ public class AnnotationUtil { @NotNull public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner, boolean inHierarchy, - Set visited) { + @Nullable Set visited) { return getAllAnnotations(owner, inHierarchy, visited, true); } @NotNull public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner, boolean inHierarchy, - Set visited, boolean withInferred) { + @Nullable Set visited, boolean withInferred) { final PsiModifierList list = owner.getModifierList(); PsiAnnotation[] annotations = PsiAnnotation.EMPTY_ARRAY; if (list != null) { diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 9435fc872845..2210e02a080a 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -27,7 +27,6 @@ import com.intellij.psi.*; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,18 +38,19 @@ import java.util.*; */ public class NullableNotNullManager implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#" + NullableNotNullManager.class.getName()); - @NonNls private static final String NULLABLE_CONTAINER_ANNOTATION = "javax.annotation.ParametersAreNullableByDefault"; - @NonNls private static final String NONNULL_CONTAINER_ANNOTATION = "javax.annotation.ParametersAreNonnullByDefault"; public String myDefaultNullable = AnnotationUtil.NULLABLE; public String myDefaultNotNull = AnnotationUtil.NOT_NULL; public final JDOMExternalizableStringList myNullables = new JDOMExternalizableStringList(); public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList(); - public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, "javax.annotation.Nullable", + private static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable"; + private static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; + + public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, JAVAX_ANNOTATION_NULLABLE, "edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable" }; - public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, "javax.annotation.Nonnull", + public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, JAVAX_ANNOTATION_NONNULL, "edu.umd.cs.findbugs.annotations.NonNull", "android.support.annotation.NonNull" }; @@ -106,8 +106,9 @@ public class NullableNotNullManager implements PersistentStateComponent return findNullabilityAnnotation(owner, checkBases, true); } - public boolean isContainerAnnotation(String anno) { - return NULLABLE_CONTAINER_ANNOTATION.equals(anno) || NONNULL_CONTAINER_ANNOTATION.equals(anno); + public boolean isContainerAnnotation(@NotNull PsiAnnotation anno) { + PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values(); + return isNullabilityDefault(anno, true, acceptAnyTarget) || isNullabilityDefault(anno, false, acceptAnyTarget); } public void setDefaultNullable(@NotNull String defaultNullable) { @@ -145,15 +146,20 @@ public class NullableNotNullManager implements PersistentStateComponent return annotation; } - if (owner instanceof PsiParameter && !TypeConversionUtil.isPrimitiveAndNotNull(((PsiParameter)owner).getType())) { - // even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations - if (AnnotationUtil.isAnnotated(owner, nullable ? Arrays.asList(DEFAULT_NOT_NULLS) : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) { - return null; - } - return findContainerAnnotation(owner, nullable - ? NULLABLE_CONTAINER_ANNOTATION - : NONNULL_CONTAINER_ANNOTATION); + PsiType type = getOwnerType(owner); + if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null; + + // even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations + if (AnnotationUtil.isAnnotated(owner, nullable ? Arrays.asList(DEFAULT_NOT_NULLS) : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) { + return null; } + return findNullabilityDefaultInHierarchy(owner, nullable); + } + + @Nullable + private static PsiType getOwnerType(PsiModifierListOwner owner) { + if (owner instanceof PsiVariable) return ((PsiVariable)owner).getType(); + if (owner instanceof PsiMethod) return ((PsiMethod)owner).getReturnType(); return null; } @@ -166,11 +172,13 @@ public class NullableNotNullManager implements PersistentStateComponent } @Nullable - private static PsiAnnotation findContainerAnnotation(PsiModifierListOwner owner, String annotationFQN) { + private static PsiAnnotation findNullabilityDefaultInHierarchy(PsiModifierListOwner owner, boolean nullable) { + PsiAnnotation.TargetType[] placeTargetTypes = AnnotationTargetUtil.getTargetsForLocation(owner.getModifierList()); + PsiElement element = owner.getParent(); while (element != null) { if (element instanceof PsiModifierListOwner) { - PsiAnnotation annotation = AnnotationUtil.findAnnotation((PsiModifierListOwner)element, annotationFQN); + PsiAnnotation annotation = getNullabilityDefault((PsiModifierListOwner)element, nullable, placeTargetTypes); if (annotation != null) { return annotation; } @@ -179,7 +187,7 @@ public class NullableNotNullManager implements PersistentStateComponent if (element instanceof PsiClassOwner) { String packageName = ((PsiClassOwner)element).getPackageName(); PsiPackage psiPackage = JavaPsiFacade.getInstance(element.getProject()).findPackage(packageName); - return AnnotationUtil.findAnnotation(psiPackage, annotationFQN); + return psiPackage == null ? null : getNullabilityDefault(psiPackage, nullable, placeTargetTypes); } element = element.getContext(); @@ -187,6 +195,37 @@ public class NullableNotNullManager implements PersistentStateComponent return null; } + private static PsiAnnotation getNullabilityDefault(@NotNull PsiModifierListOwner container, boolean nullable, PsiAnnotation.TargetType[] placeTargetTypes) { + PsiModifierList modifierList = container.getModifierList(); + if (modifierList == null) return null; + for (PsiAnnotation annotation : modifierList.getAnnotations()) { + if (isNullabilityDefault(annotation, nullable, placeTargetTypes)) { + return annotation; + } + } + return null; + } + + private static boolean isNullabilityDefault(@NotNull PsiAnnotation annotation, boolean nullable, PsiAnnotation.TargetType[] placeTargetTypes) { + PsiJavaCodeReferenceElement element = annotation.getNameReferenceElement(); + PsiElement declaration = element == null ? null : element.resolve(); + if (!(declaration instanceof PsiClass)) return false; + + if (!AnnotationUtil.isAnnotated((PsiClass)declaration, + nullable ? JAVAX_ANNOTATION_NULLABLE : JAVAX_ANNOTATION_NONNULL, + false, + true)) { + return false; + } + + PsiAnnotation tqDefault = AnnotationUtil.findAnnotation((PsiClass)declaration, true, "javax.annotation.meta.TypeQualifierDefault"); + if (tqDefault == null) return false; + + Set required = AnnotationTargetUtil.extractRequiredAnnotationTargets(tqDefault.findAttributeValue(null)); + if (required == null) return false; + return required.isEmpty() || ContainerUtil.intersects(required, Arrays.asList(placeTargetTypes)); + } + public List getNullables() { return myNullables; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 514087492300..bcf0decb69b7 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl; +import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.lang.ASTNode; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -51,7 +52,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -60,22 +60,6 @@ import static com.intellij.psi.PsiAnnotation.TargetType; public class PsiImplUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiImplUtil"); - private static final Set DEFAULT_TARGETS = Collections.unmodifiableSet(ContainerUtil.newHashSet( - TargetType.PACKAGE, TargetType.TYPE, TargetType.ANNOTATION_TYPE, - TargetType.FIELD, TargetType.METHOD, TargetType.CONSTRUCTOR, - TargetType.PARAMETER, TargetType.LOCAL_VARIABLE)); - - private static final TargetType[] PACKAGE_TARGETS = {TargetType.PACKAGE}; - private static final TargetType[] TYPE_USE_TARGETS = {TargetType.TYPE_USE}; - private static final TargetType[] ANNOTATION_TARGETS = {TargetType.ANNOTATION_TYPE, TargetType.TYPE, TargetType.TYPE_USE}; - private static final TargetType[] TYPE_TARGETS = {TargetType.TYPE, TargetType.TYPE_USE}; - private static final TargetType[] TYPE_PARAMETER_TARGETS = {TargetType.TYPE_PARAMETER, TargetType.TYPE_USE}; - private static final TargetType[] CONSTRUCTOR_TARGETS = {TargetType.CONSTRUCTOR, TargetType.TYPE_USE}; - private static final TargetType[] METHOD_TARGETS = {TargetType.METHOD, TargetType.TYPE_USE}; - private static final TargetType[] FIELD_TARGETS = {TargetType.FIELD, TargetType.TYPE_USE}; - private static final TargetType[] PARAMETER_TARGETS = {TargetType.PARAMETER, TargetType.TYPE_USE}; - private static final TargetType[] LOCAL_VARIABLE_TARGETS ={TargetType.LOCAL_VARIABLE, TargetType.TYPE_USE}; - private PsiImplUtil() { } @NotNull @@ -376,93 +360,14 @@ public class PsiImplUtil { PsiModifierList modifierList = annotationType.getModifierList(); if (modifierList == null) return null; PsiAnnotation target = modifierList.findAnnotation(CommonClassNames.JAVA_LANG_ANNOTATION_TARGET); - if (target == null) return DEFAULT_TARGETS; // if omitted it is applicable to all but Java 8 TYPE_USE/TYPE_PARAMETERS targets + if (target == null) return AnnotationTargetUtil.DEFAULT_TARGETS; // if omitted it is applicable to all but Java 8 TYPE_USE/TYPE_PARAMETERS targets - PsiAnnotationMemberValue value = target.findAttributeValue(null); - if (value instanceof PsiReference) { - TargetType targetType = translateTargetRef((PsiReference)value); - if (targetType != null) { - return Collections.singleton(targetType); - } - } - else if (value instanceof PsiArrayInitializerMemberValue) { - Set targets = ContainerUtil.newHashSet(); - for (PsiAnnotationMemberValue initializer : ((PsiArrayInitializerMemberValue)value).getInitializers()) { - if (initializer instanceof PsiReference) { - TargetType targetType = translateTargetRef((PsiReference)initializer); - if (targetType != null) { - targets.add(targetType); - } - } - } - return targets; - } - - return null; - } - - @Nullable - private static TargetType translateTargetRef(PsiReference reference) { - PsiElement field = reference.resolve(); - if (field instanceof PsiEnumConstant) { - String name = ((PsiEnumConstant)field).getName(); - try { - return TargetType.valueOf(name); - } - catch (IllegalArgumentException e) { - LOG.warn("Unknown target: " + name); - } - } - return null; + return AnnotationTargetUtil.extractRequiredAnnotationTargets(target.findAttributeValue(null)); } @NotNull public static TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { - if (owner == null) { - return TargetType.EMPTY_ARRAY; - } - - if (owner instanceof PsiType || owner instanceof PsiTypeElement) { - return TYPE_USE_TARGETS; - } - - if (owner instanceof PsiTypeParameter) { - return TYPE_PARAMETER_TARGETS; - } - - if (owner instanceof PsiModifierList) { - PsiElement element = ((PsiModifierList)owner).getParent(); - if (element instanceof PsiPackageStatement) { - return PACKAGE_TARGETS; - } - if (element instanceof PsiClass) { - if (((PsiClass)element).isAnnotationType()) { - return ANNOTATION_TARGETS; - } - else { - return TYPE_TARGETS; - } - } - if (element instanceof PsiMethod) { - if (((PsiMethod)element).isConstructor()) { - return CONSTRUCTOR_TARGETS; - } - else { - return METHOD_TARGETS; - } - } - if (element instanceof PsiField) { - return FIELD_TARGETS; - } - if (element instanceof PsiParameter) { - return PARAMETER_TARGETS; - } - if (element instanceof PsiLocalVariable) { - return LOCAL_VARIABLE_TARGETS; - } - } - - return TargetType.EMPTY_ARRAY; + return AnnotationTargetUtil.getTargetsForLocation(owner); } @Nullable diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/CustomTypeQualifierDefault.java b/java/java-tests/testData/inspection/dataFlow/fixture/CustomTypeQualifierDefault.java new file mode 100644 index 000000000000..e9ec6b1dbe11 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/CustomTypeQualifierDefault.java @@ -0,0 +1,25 @@ +import foo.*; +import org.jetbrains.annotations.NotNull; + +class Some { + void foo(@NotNull String s) { + NotNullClass.foo(null); + if (NotNullClass.foo(s) == null) {} + + NullableClass.foo(null); + if (NullableClass.foo("a") == null) {} + + AnotherPackageNotNull.foo(null); + if (AnotherPackageNotNull.foo(s) == null) {} + } + +} + +@bar.MethodsAreNotNullByDefault +class NotNullClass { + static native Object foo(String s); + +} +class NullableClass { + static native Object foo(String s); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 4cba2ab918a7..3bdcae2b9093 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -258,8 +258,14 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testAccessingSameArrayElements() { doTest(); } public void testParametersAreNonnullByDefault() { - myFixture.addClass("package javax.annotation; public @interface ParametersAreNonnullByDefault {}"); - myFixture.addClass("package javax.annotation; public @interface ParametersAreNullableByDefault {}"); + addJavaxNullabilityAnnotations(); + + myFixture.addClass("package javax.annotation;" + + "@javax.annotation.meta.TypeQualifierDefault(java.lang.annotation.ElementType.PARAMETER) @javax.annotation.Nonnull " + + "public @interface ParametersAreNonnullByDefault {}"); + myFixture.addClass("package javax.annotation;" + + "@javax.annotation.meta.TypeQualifierDefault(java.lang.annotation.ElementType.PARAMETER) @javax.annotation.Nullable " + + "public @interface ParametersAreNullableByDefault {}"); myFixture.addClass("package foo; public class AnotherPackageNotNull { public static void foo(String s) {}}"); myFixture.addFileToProject("foo/package-info.java", "@javax.annotation.ParametersAreNonnullByDefault package foo;"); @@ -267,6 +273,28 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { doTest(); } + private void addJavaxNullabilityAnnotations() { + myFixture.addClass("package javax.annotation;" + + "public @interface Nonnull {}"); + myFixture.addClass("package javax.annotation.meta;" + + "public @interface TypeQualifier {}"); + myFixture.addClass("package javax.annotation.meta;" + + "public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}"); + } + + public void testCustomTypeQualifierDefault() { + addJavaxNullabilityAnnotations(); + myFixture.addClass("package bar;" + + "@javax.annotation.meta.TypeQualifierDefault(java.lang.annotation.ElementType.METHOD) @javax.annotation.Nonnull " + + "public @interface MethodsAreNotNullByDefault {}"); + + myFixture.addClass("package foo; public class AnotherPackageNotNull { public static native Object foo(String s); }"); + myFixture.addFileToProject("foo/package-info.java", "@bar.MethodsAreNotNullByDefault package foo;"); + + myFixture.enableInspections(new DataFlowInspection()); + myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); + } + public void testTrueOrEqualsSomething() { doTest(); myFixture.launchAction(myFixture.findSingleIntention("Remove redundant assignment"));