dfa: prefer unknown nullity from super over scoped nullity default (IDEA-167062)

This commit is contained in:
peter
2017-06-03 18:07:11 +02:00
parent 3bc122022e
commit 473d383cbe
6 changed files with 41 additions and 14 deletions
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchHelper;
@@ -35,13 +34,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import static com.intellij.patterns.PsiJavaPatterns.psiMember;
import static com.intellij.patterns.PsiJavaPatterns.psiParameter;
import static com.intellij.patterns.StandardPatterns.or;
public class NullnessUtil {
private static final ElementPattern<? extends PsiModifierListOwner> MEMBER_OR_METHOD_PARAMETER =
or(psiMember(), psiParameter().withSuperParent(2, psiMember()));
static Boolean calcCanBeNull(DfaVariableValue value) {
PsiModifierListOwner var = value.getPsiVariable();
@@ -50,8 +43,7 @@ public class NullnessUtil {
return toBoolean(nullability);
}
Nullness defaultNullability =
value.getFactory().isUnknownMembersAreNullable() && MEMBER_OR_METHOD_PARAMETER.accepts(var) ? Nullness.NULLABLE : Nullness.UNKNOWN;
Nullness defaultNullability = value.getFactory().suggestNullabilityForNonAnnotatedMember(var);
if (var instanceof PsiParameter && var.getParent() instanceof PsiForeachStatement) {
PsiExpression iteratedValue = ((PsiForeachStatement)var.getParent()).getIteratedValue();
@@ -579,8 +579,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (type != null && !(type instanceof PsiPrimitiveType)) {
Nullness nullability = myReturnTypeNullability.get(instruction);
if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) {
nullability = Nullness.NULLABLE;
PsiMethod targetMethod = instruction.getTargetMethod();
if (nullability == Nullness.UNKNOWN && targetMethod != null) {
nullability = factory.suggestNullabilityForNonAnnotatedMember(targetMethod);
}
return factory.createTypeValue(type, nullability);
}
@@ -16,10 +16,12 @@
package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
import com.intellij.openapi.util.Pair;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -31,6 +33,10 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import static com.intellij.patterns.PsiJavaPatterns.psiMember;
import static com.intellij.patterns.PsiJavaPatterns.psiParameter;
import static com.intellij.patterns.StandardPatterns.or;
public class DfaValueFactory {
private final List<DfaValue> myValues = ContainerUtil.newArrayList();
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache = ContainerUtil.newHashMap();
@@ -57,8 +63,17 @@ public class DfaValueFactory {
return myHonorFieldInitializers;
}
public boolean isUnknownMembersAreNullable() {
return myUnknownMembersAreNullable;
private static final ElementPattern<? extends PsiModifierListOwner> MEMBER_OR_METHOD_PARAMETER =
or(psiMember(), psiParameter().withSuperParent(2, psiMember()));
@NotNull
public Nullness suggestNullabilityForNonAnnotatedMember(@NotNull PsiModifierListOwner member) {
if (myUnknownMembersAreNullable && MEMBER_OR_METHOD_PARAMETER.accepts(member) && AnnotationUtil.getSuperAnnotationOwners(member).isEmpty()) {
return Nullness.NULLABLE;
}
return Nullness.UNKNOWN;
}
@NotNull
@@ -156,7 +156,7 @@ public class AnnotationUtil {
return result == null ? PsiAnnotation.EMPTY_ARRAY : result.toArray(new PsiAnnotation[result.size()]);
}
public static <T extends PsiModifierListOwner> List<T> getSuperAnnotationOwners(final T element) {
public static <T extends PsiModifierListOwner> List<T> getSuperAnnotationOwners(@NotNull T element) {
return CachedValuesManager.getCachedValue(element, () -> {
Set<PsiModifierListOwner> result = ContainerUtil.newLinkedHashSet();
if (element instanceof PsiMethod) {
@@ -0,0 +1,10 @@
interface UnknownInterface {
void foo(String s);
}
@javax.annotation.ParametersAreNonnullByDefault
class ImplWithNotNull implements UnknownInterface {
public void foo(String s) {
System.out.println(s.hashCode());
}
}
@@ -352,6 +352,15 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
doTest();
}
public void testNullabilityDefaultVsMethodImplementing() {
addJavaxDefaultNullabilityAnnotations(myFixture);
DataFlowInspection inspection = new DataFlowInspection();
inspection.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true;
myFixture.enableInspections(inspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
public static void addJavaxDefaultNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) {
fixture.addClass("package javax.annotation;" +
"@javax.annotation.meta.TypeQualifierDefault(java.lang.annotation.ElementType.PARAMETER) @javax.annotation.Nonnull " +