NullableNotNullManager: findEffectiveNullabilityAnnotationInfo, findEffectiveNullability

Removed isContainer* methods; more nullability refactoring
This commit is contained in:
Tagir Valeev
2018-06-09 14:29:26 +07:00
parent 49b710611c
commit ee554a876c
9 changed files with 90 additions and 93 deletions
@@ -2,9 +2,7 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ExpressionUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.*;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
import com.intellij.codeInspection.*;
@@ -763,15 +761,15 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
final PsiMethod method = getScopeMethod(block);
if (method == null) return;
NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
PsiAnnotation anno = manager.findEffectiveNullabilityAnnotation(method);
Nullness annoNullness = Nullness.fromAnnotation(anno);
if (annoNullness == Nullness.NULLABLE) {
assert anno != null;
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityAnnotationInfo(method);
PsiAnnotation anno = info == null ? null : info.getAnnotation();
Nullability annoNullness = info == null ? Nullability.UNKNOWN : info.getNullability();
if (annoNullness == Nullability.NULLABLE) {
if (!AnnotationUtil.isInferredAnnotation(anno)) return;
if (DfaPsiUtil.getTypeNullability(method.getReturnType()) == Nullness.NULLABLE) return;
}
if (annoNullness != Nullness.NOT_NULL && (!SUGGEST_NULLABLE_ANNOTATIONS || block.getParent() instanceof PsiLambdaExpression)) return;
if (annoNullness != Nullability.NOT_NULL && (!SUGGEST_NULLABLE_ANNOTATIONS || block.getParent() instanceof PsiLambdaExpression)) return;
PsiType returnType = method.getReturnType();
// no warnings in void lambdas, where the expression is not returned anyway
@@ -784,8 +782,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
final PsiExpression expr = problem.getAnchor();
if (!reportedAnchors.add(expr)) continue;
if (annoNullness == Nullness.NOT_NULL) {
assert anno != null;
if (annoNullness == Nullability.NOT_NULL) {
String presentable = NullableStuffInspectionBase.getPresentableAnnoName(anno);
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.return.null.from.notnull", presentable)
@@ -2,6 +2,8 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
@@ -84,9 +86,9 @@ public class DfaPsiUtil {
return Nullness.NOT_NULL;
}
Nullness fromAnnotation = getNullabilityFromAnnotation(owner, ignoreParameterNullabilityInference);
if (fromAnnotation != Nullness.UNKNOWN) {
return fromAnnotation;
Nullability fromAnnotation = getNullabilityFromAnnotation(owner, ignoreParameterNullabilityInference);
if (fromAnnotation != Nullability.UNKNOWN) {
return Nullness.fromNullability(fromAnnotation);
}
if (owner instanceof PsiMethod && isMapMethodWithUnknownNullity((PsiMethod)owner)) {
@@ -111,14 +113,14 @@ public class DfaPsiUtil {
}
@NotNull
private static Nullness getNullabilityFromAnnotation(PsiModifierListOwner owner, boolean ignoreParameterNullabilityInference) {
private static Nullability getNullabilityFromAnnotation(PsiModifierListOwner owner, boolean ignoreParameterNullabilityInference) {
NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject());
PsiAnnotation annotation = manager.findEffectiveNullabilityAnnotation(owner);
if (annotation == null ||
(ignoreParameterNullabilityInference && owner instanceof PsiParameter && AnnotationUtil.isInferredAnnotation(annotation))) {
return Nullness.UNKNOWN;
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityAnnotationInfo(owner);
if (info == null ||
ignoreParameterNullabilityInference && owner instanceof PsiParameter && AnnotationUtil.isInferredAnnotation(info.getAnnotation())) {
return Nullability.UNKNOWN;
}
return Nullness.fromAnnotation(annotation);
return info.getNullability();
}
private static boolean isMapMethodWithUnknownNullity(@NotNull PsiMethod method) {
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
@@ -53,7 +54,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
for (int index = 0; index < parameters.length; index++) {
PsiParameter parameter = parameters[index];
if (!(parameter.getType() instanceof PsiPrimitiveType) &&
NullableNotNullManager.getInstance(method.getProject()).findEffectiveNullabilityAnnotation(parameter) == null &&
NullableNotNullManager.getInstance(method.getProject()).findEffectiveNullability(parameter) == Nullability.UNKNOWN &&
JavaNullMethodArgumentUtil.hasNullArgument(method, index)) {
nullableParameters.add(parameter);
}
@@ -16,12 +16,7 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiModifierListOwner;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @deprecated use {@link Nullability}
@@ -30,25 +25,6 @@ import org.jetbrains.annotations.Nullable;
public enum Nullness {
NOT_NULL, NULLABLE, UNKNOWN;
/**
* Convert nullability annotation returned by {@link NullableNotNullManager#findEffectiveNullabilityAnnotation(PsiModifierListOwner)}
* to {@code Nullness} value
*
* @param annotation annotation to convert
* @return Nullness value
*/
@NotNull
public static Nullness fromAnnotation(@Nullable PsiAnnotation annotation) {
if (annotation == null) return UNKNOWN;
if (NullableNotNullManager.isNullableAnnotation(annotation) || NullableNotNullManager.isContainerNullableAnnotation(annotation)) {
return NULLABLE;
}
if (NullableNotNullManager.isNotNullAnnotation(annotation) || NullableNotNullManager.isContainerNotNullAnnotation(annotation)) {
return NOT_NULL;
}
return UNKNOWN;
}
@Contract(pure = true)
public Nullability toNullability() {
switch (this) {
@@ -3,6 +3,7 @@ package com.intellij.codeInspection.nullable;
import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
@@ -513,10 +514,11 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
private static void checkNotNullFieldsInitialized(PsiField field, NullableNotNullManager manager, @NotNull ProblemsHolder holder) {
PsiAnnotation annotation = manager.getNotNullAnnotation(field, false);
if (annotation == null || HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) return;
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityAnnotationInfo(field);
if (info == null || HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) return;
boolean byDefault = manager.isContainerAnnotation(annotation);
boolean byDefault = info.isContainer();
PsiAnnotation annotation = info.getAnnotation();
PsiJavaCodeReferenceElement name = annotation.getNameReferenceElement();
holder.registerProblem(annotation.isPhysical() && !byDefault ? annotation : field.getNameIdentifier(),
(byDefault && name != null ? "@" + name.getReferenceName() : "Not-null") + " fields must be initialized");
@@ -578,8 +580,8 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
@NotNull
private static String getPresentableAnnoName(@NotNull PsiModifierListOwner owner) {
NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject());
PsiAnnotation anno = manager.findEffectiveNullabilityAnnotation(owner);
String name = anno == null ? null : anno.getQualifiedName();
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityAnnotationInfo(owner);
String name = info == null ? null : info.getAnnotation().getQualifiedName();
if (name == null) {
return "???";
}
@@ -15,6 +15,7 @@
*/
package com.intellij.slicer;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.DfaUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
@@ -45,9 +46,8 @@ public class JavaSliceNullnessAnalyzer extends SliceNullnessAnalyzerBase {
if (value instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)value).resolveMethod();
if (method != null) {
PsiAnnotation annotation = NullableNotNullManager.getInstance(method.getProject()).findEffectiveNullabilityAnnotation(method);
Nullness nullness = Nullness.fromAnnotation(annotation);
if (nullness != Nullness.UNKNOWN) return nullness;
Nullability nullability = NullableNotNullManager.getInstance(method.getProject()).findEffectiveNullability(method);
return Nullness.fromNullability(nullability);
}
}
if (value instanceof PsiPolyadicExpression && ((PsiPolyadicExpression)value).getOperationTokenType() == JavaTokenType.PLUS) {
@@ -75,12 +75,13 @@ public class JavaSliceNullnessAnalyzer extends SliceNullnessAnalyzerBase {
}
}
if (value instanceof PsiEnumConstant) return Nullness.NOT_NULL;
if (value instanceof PsiModifierListOwner) {
if (NullableNotNullManager.isNotNull((PsiModifierListOwner)value)) return Nullness.NOT_NULL;
if (NullableNotNullManager.isNullable((PsiModifierListOwner)value)) return Nullness.NULLABLE;
return Nullness
.fromNullability(NullableNotNullManager.getInstance(value.getProject()).findEffectiveNullability(((PsiModifierListOwner)value)));
}
if (value instanceof PsiEnumConstant) return Nullness.NOT_NULL;
return Nullness.UNKNOWN;
}
}
@@ -40,4 +40,18 @@ public class NullabilityAnnotationInfo {
public boolean isContainer() {
return myContainer;
}
/**
* @return true if this annotation is an external annotation
*/
public boolean isExternal() {
return AnnotationUtil.isExternalAnnotation(myAnnotation);
}
/**
* @return true if this annotation is an inferred annotation
*/
public boolean isInferred() {
return AnnotationUtil.isInferredAnnotation(myAnnotation);
}
}
@@ -83,7 +83,8 @@ public abstract class NullableNotNullManager {
if (annotation == null) {
return null;
}
if (isContainerAnnotation(annotation)) {
PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values();
if (checkNullityDefault(annotation, acceptAnyTarget, false) != null) {
return null;
}
return annotation.getQualifiedName();
@@ -94,11 +95,6 @@ public abstract class NullableNotNullManager {
return findNullityAnnotationWithDefault(owner, checkBases, true);
}
public boolean isContainerAnnotation(@NotNull PsiAnnotation anno) {
PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values();
return checkNullityDefault(anno, acceptAnyTarget, false) != null;
}
public abstract void setDefaultNullable(@NotNull String defaultNullable);
@NotNull
@@ -199,17 +195,25 @@ public abstract class NullableNotNullManager {
}
/**
* Returns nullability annotation (either nullable, or non-nullable) which has effect for given element.
* To figure out its exact effect can be subsequently checked using {@link #isNullableAnnotation(PsiAnnotation)},
* {@link #isNotNullAnnotation(PsiAnnotation)}, {@link #isContainerNullableAnnotation(PsiAnnotation)} or
* {@link #isContainerNotNullAnnotation(PsiAnnotation)}. May return explicit, inferred, external or
* default nullability annotation for class/package.
* Returns effective nullability derived from annotations for given element.
*
* @param owner
* @return effective nullability annotation, or null if not found.
* @param owner element to find a nullability for
* @return effective nullability
*/
@NotNull
public Nullability findEffectiveNullability(@NotNull PsiModifierListOwner owner) {
NullabilityAnnotationInfo info = findEffectiveNullabilityAnnotationInfo(owner);
return info == null ? Nullability.UNKNOWN : info.getNullability();
}
/**
* Returns nullability annotation info which has effect for given element.
*
* @param owner element to find an annotation for
* @return effective nullability annotation info, or null if not found.
*/
@Nullable
public PsiAnnotation findEffectiveNullabilityAnnotation(@NotNull PsiModifierListOwner owner) {
public NullabilityAnnotationInfo findEffectiveNullabilityAnnotationInfo(@NotNull PsiModifierListOwner owner) {
PsiType type = getOwnerType(owner);
if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null;
@@ -218,8 +222,9 @@ public abstract class NullableNotNullManager {
}
@Nullable
private PsiAnnotation doFindEffectiveNullabilityAnnotation(@NotNull PsiModifierListOwner owner) {
Set<String> annotationNames = ContainerUtil.newHashSet(getNullablesWithNickNames());
private NullabilityAnnotationInfo doFindEffectiveNullabilityAnnotation(@NotNull PsiModifierListOwner owner) {
List<String> nullables = getNullablesWithNickNames();
Set<String> annotationNames = ContainerUtil.newHashSet(nullables);
annotationNames.addAll(getNotNullsWithNickNames());
Set<String> extraAnnotations = DEFAULT_ALL.stream().filter(anno -> !annotationNames.contains(anno)).collect(Collectors.toSet());
annotationNames.addAll(extraAnnotations);
@@ -231,20 +236,30 @@ public abstract class NullableNotNullManager {
// return null in this case
return null;
}
return annotation;
return new NullabilityAnnotationInfo(annotation,
nullables.contains(annotation.getQualifiedName()) ? Nullability.NULLABLE : Nullability.NOT_NULL,
false);
}
if (owner instanceof PsiParameter) {
List<PsiParameter> superParameters = getSuperAnnotationOwners((PsiParameter)owner);
if (!superParameters.isEmpty()) {
PsiAnnotation superParameterAnnotation = takeAnnotationFromSuperParameters((PsiParameter)owner, superParameters);
return superParameterAnnotation != null && isContainerNotNullAnnotation(superParameterAnnotation) ? superParameterAnnotation : null;
for (PsiParameter parameter: superParameters) {
PsiAnnotation plain = findPlainAnnotation(parameter, false, annotationNames);
// Plain not null annotation is not inherited
if (plain != null) return null;
NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(parameter);
if (defaultInfo != null) {
return defaultInfo.getNullability() == Nullability.NOT_NULL ? defaultInfo : null;
}
}
return null;
}
}
NullabilityAnnotationInfo nullityDefault = findNullityDefaultInHierarchy(owner);
if (nullityDefault != null && (nullityDefault.getNullability() == Nullability.NULLABLE || !hasHardcodedContracts(owner))) {
return nullityDefault.getAnnotation();
NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(owner);
if (defaultInfo != null && (defaultInfo.getNullability() == Nullability.NULLABLE || !hasHardcodedContracts(owner))) {
return defaultInfo;
}
return null;
}
@@ -411,16 +426,4 @@ public abstract class NullableNotNullManager {
public static boolean isNotNullAnnotation(@NotNull PsiAnnotation annotation) {
return getInstance(annotation.getProject()).getNotNullsWithNickNames().contains(annotation.getQualifiedName());
}
public static boolean isContainerNullableAnnotation(@NotNull PsiAnnotation annotation) {
PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values();
NullabilityAnnotationInfo nullityDefault = getInstance(annotation.getProject()).checkNullityDefault(annotation, acceptAnyTarget, false);
return nullityDefault != null && nullityDefault.getNullability() == Nullability.NULLABLE;
}
public static boolean isContainerNotNullAnnotation(@NotNull PsiAnnotation annotation) {
PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values();
NullabilityAnnotationInfo nullityDefault = getInstance(annotation.getProject()).checkNullityDefault(annotation, acceptAnyTarget, false);
return nullityDefault != null && nullityDefault.getNullability() == Nullability.NOT_NULL;
}
}
@@ -16,8 +16,9 @@
package com.siyeh.ipp.asserttoif;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -110,9 +111,9 @@ public class ObjectsRequireNonNullIntention extends Intention {
if (ClassUtils.findClass("java.util.Objects", element) == null) {
return false;
}
final PsiAnnotation annotation = NullableNotNullManager.getInstance(variable.getProject()).findEffectiveNullabilityAnnotation(variable);
if (annotation != null && Nullness.fromAnnotation(annotation) == Nullness.NOT_NULL &&
!AnnotationUtil.isExternalAnnotation(annotation) && !AnnotationUtil.isInferredAnnotation(annotation)) {
final NullabilityAnnotationInfo
info = NullableNotNullManager.getInstance(variable.getProject()).findEffectiveNullabilityAnnotationInfo(variable);
if (info != null && info.getNullability() == Nullability.NOT_NULL && !info.isExternal() && !info.isInferred()) {
return true;
}
final PsiStatement referenceStatement = PsiTreeUtil.getParentOfType(referenceExpression, PsiStatement.class);