resolve member/type annotation nullable/notnull conflict (IDEA-174042)

This commit is contained in:
peter
2017-06-28 16:06:40 +02:00
parent 870e568148
commit 4713328ffe
3 changed files with 63 additions and 5 deletions
@@ -245,11 +245,35 @@ public abstract class NullableNotNullManager {
private PsiAnnotation findPlainNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) {
Set<String> qNames = ContainerUtil.newHashSet(getNullablesWithNickNames());
qNames.addAll(getNotNullsWithNickNames());
return checkBases && owner instanceof PsiMethod
? AnnotationUtil.findAnnotationInHierarchy(owner, qNames)
: AnnotationUtil.findAnnotation(owner, qNames);
PsiAnnotation memberAnno = checkBases && owner instanceof PsiMethod
? AnnotationUtil.findAnnotationInHierarchy(owner, qNames)
: AnnotationUtil.findAnnotation(owner, qNames);
if (memberAnno != null) {
if (owner instanceof PsiMethod) {
return preferTypeAnnotation(memberAnno, ((PsiMethod)owner).getReturnType());
}
if (owner instanceof PsiVariable) {
return preferTypeAnnotation(memberAnno, ((PsiVariable)owner).getType());
}
}
return memberAnno;
}
private static PsiAnnotation preferTypeAnnotation(@NotNull PsiAnnotation memberAnno, @Nullable PsiType type) {
if (type != null) {
for (PsiAnnotation typeAnno : type.getApplicableAnnotations()) {
if (areDifferentNullityAnnotations(memberAnno, typeAnno)) {
return typeAnno;
}
}
}
return memberAnno;
}
private static boolean areDifferentNullityAnnotations(@NotNull PsiAnnotation memberAnno, PsiAnnotation typeAnno) {
return isNullableAnnotation(typeAnno) && isNotNullAnnotation(memberAnno) ||
isNullableAnnotation(memberAnno) && isNotNullAnnotation(typeAnno);
}
@NotNull
protected List<String> getNullablesWithNickNames() {
@@ -0,0 +1,25 @@
import withTypeUse.NotNull;
import withTypeUse.Nullable;
interface Foo {
@Nullable Object @NotNull [] getNotNullArrayOfNullableObjects();
@NotNull Object @Nullable [] getNullableArrayOfNotNullObjects();
}
class FooImpl implements Foo {
@Override
public @Nullable Object @NotNull [] getNotNullArrayOfNullableObjects() {
return <warning descr="'null' is returned by the method declared as @NotNull">null</warning>;
}
@Override
public @NotNull Object @Nullable [] getNullableArrayOfNotNullObjects() {
if (Math.random() > 0.5) {
return null;
}
else {
return new Object[]{null, new Object()};
}
}
}
@@ -133,8 +133,12 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
}
static void setupTypeUseAnnotations(String pkg, JavaCodeInsightTestFixture fixture) {
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface Nullable { }");
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface NotNull { }");
setupCustomAnnotations(pkg, "{ElementType.TYPE_USE}", fixture);
}
private static void setupCustomAnnotations(String pkg, String target, JavaCodeInsightTestFixture fixture) {
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target(" + target + ") public @interface Nullable { }");
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target(" + target + ") public @interface NotNull { }");
setCustomAnnotations(fixture.getProject(), fixture.getTestRootDisposable(), pkg + ".NotNull", pkg + ".Nullable");
}
@@ -151,4 +155,9 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testCapturedWildcardNotNull() { doTest(); }
public void testVarargNotNull() { doTestWithCustomAnnotations(); }
public void testArrayComponentAndMethodAnnotationConflict() {
setupCustomAnnotations("withTypeUse", "{ElementType.METHOD, ElementType.TYPE_USE}", myFixture);
doTest();
}
}