From 110abf16a4da095dca6a39caf612ccff33190717 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 20 Nov 2014 15:47:50 +0100 Subject: [PATCH] ignore @NotNull inferred from @ParametersAreNonnullByDefault when there's hardcoded contract (e.g. Guava Preconditions.checkNotNull) --- .../InferredAnnotationsManagerImpl.java | 7 ++-- .../InferredAnnotationsManager.java | 35 +++++++++++++++++++ .../codeInsight/NullableNotNullManager.java | 5 +++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java index ed34299f4c35..327e00f1fe05 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java @@ -51,7 +51,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { } } - if (!ignoreBytecodeInference(listOwner, annotationFQN)) { + if (!ignoreInference(listOwner, annotationFQN)) { PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotation(listOwner, annotationFQN); if (fromBytecode != null) { return fromBytecode; @@ -71,7 +71,8 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { return contracts.isEmpty() ? null : createContractAnnotation(contracts, HardcodedContracts.isHardcodedPure(method)); } - private static boolean ignoreBytecodeInference(PsiModifierListOwner owner, @Nullable String annotationFQN) { + @Override + public boolean ignoreInference(@NotNull PsiModifierListOwner owner, @Nullable String annotationFQN) { if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && hasHardcodedContracts(owner)) { return true; } @@ -125,7 +126,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { List result = ContainerUtil.newArrayList(); PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotations(listOwner); for (PsiAnnotation annotation : fromBytecode) { - if (!ignoreBytecodeInference(listOwner, annotation.getQualifiedName())) { + if (!ignoreInference(listOwner, annotation.getQualifiedName())) { if (!ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotation.getQualifiedName()) || canHaveContract(listOwner)) { result.add(annotation); } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/InferredAnnotationsManager.java b/java/java-psi-api/src/com/intellij/codeInsight/InferredAnnotationsManager.java index b3fbfe7547cf..9ff366aaa7ce 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/InferredAnnotationsManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/InferredAnnotationsManager.java @@ -23,6 +23,15 @@ import com.intellij.psi.PsiModifierListOwner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * Returns annotations inferred by bytecode our source code, for example contracts and nullity. + * + * @see com.intellij.codeInsight.NullableNotNullManager + * @see org.jetbrains.annotations.Contract + * @see org.jetbrains.annotations.Nullable + * @see org.jetbrains.annotations.NotNull + * @see com.intellij.codeInsight.AnnotationUtil + */ public abstract class InferredAnnotationsManager { private static final NotNullLazyKey INSTANCE_KEY = ServiceManager.createLazyKey(InferredAnnotationsManager.class); @@ -30,11 +39,37 @@ public abstract class InferredAnnotationsManager { return INSTANCE_KEY.getValue(project); } + /** + * @return if exists, an inferred annotation by given qualified name on a given PSI element. Several invocations may return several + * different instances of {@link com.intellij.psi.PsiAnnotation}, which are not guaranteed to be equal. + */ @Nullable public abstract PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN); + /** + * There is a number of well-known methods where automatic inference fails (for example, {@link java.util.Objects#requireNonNull(Object)}. + * For such methods, contracts are hardcoded, and for their parameters inferred @NotNull are suppressed.

+ * + * In addition, package-default annotations like @ParametersAreNonnullByDefault are not honored for parameters where + * {@link org.jetbrains.annotations.NotNull} inference is ignored. + * + * @return whether inference is to be suppressed the given annotation on the given method or parameter + */ + public abstract boolean ignoreInference(@NotNull PsiModifierListOwner owner, @Nullable String annotationFQN); + + /** + * When annotation name is known, prefer {@link #findInferredAnnotation(com.intellij.psi.PsiModifierListOwner, String)} as + * potentially faster. + * + * @return all inferred annotations for the given element + */ @NotNull public abstract PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner); + /** + * @return whether the given annotation was inferred by this service. + * + * @see com.intellij.codeInsight.AnnotationUtil#isInferredAnnotation(com.intellij.psi.PsiAnnotation) + */ public abstract boolean isInferredAnnotation(@NotNull PsiAnnotation annotation); } 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 00d76638cc97..403043d38952 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -184,6 +184,11 @@ public class NullableNotNullManager implements PersistentStateComponent if (AnnotationUtil.isAnnotated(owner, nullable ? Arrays.asList(DEFAULT_NOT_NULLS) : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) { return null; } + + if (!nullable && InferredAnnotationsManager.getInstance(owner.getProject()).ignoreInference(owner, AnnotationUtil.NOT_NULL)) { + return null; + } + return findNullabilityDefaultInHierarchy(owner, nullable); }