IDEA-123301 Show a warning if 'List<@Nullable X>' is passed to a place where 'List<@NotNull X'> is expected

This commit is contained in:
peter
2017-05-08 18:30:28 +02:00
parent 0350ddfde9
commit 4c858997f1
3 changed files with 72 additions and 0 deletions
@@ -25,6 +25,7 @@ import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
@@ -158,6 +159,51 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo
}
return false;
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
checkCollectionNullityOnAssignment(expression.getOperationSign(), expression.getLExpression().getType(), expression.getRExpression());
}
@Override
public void visitLocalVariable(PsiLocalVariable variable) {
PsiIdentifier identifier = variable.getNameIdentifier();
if (identifier != null) {
checkCollectionNullityOnAssignment(identifier, variable.getType(), variable.getInitializer());
}
}
@Override
public void visitCallExpression(PsiCallExpression callExpression) {
PsiExpressionList argList = callExpression.getArgumentList();
JavaResolveResult result = callExpression.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
if (method == null || argList == null) return;
PsiSubstitutor substitutor = result.getSubstitutor();
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiExpression[] arguments = argList.getExpressions();
for (int i = 0; i < arguments.length; i++) {
PsiExpression argument = arguments[i];
if (i < parameters.length &&
(i < parameters.length - 1 || !MethodCallInstruction.isVarArgCall(method, substitutor, arguments, parameters))) {
checkCollectionNullityOnAssignment(argument, substitutor.substitute(parameters[i].getType()), argument);
}
}
}
private void checkCollectionNullityOnAssignment(@NotNull PsiElement errorElement, PsiType expectedType, PsiExpression assignedExpression) {
PsiType lItemType = JavaGenericsUtil.getCollectionItemType(expectedType, errorElement.getResolveScope());
PsiType rItemType = assignedExpression == null ? null : JavaGenericsUtil.getCollectionItemType(assignedExpression);
if (DfaPsiUtil.getTypeNullability(lItemType) == Nullness.NOT_NULL &&
DfaPsiUtil.getTypeNullability(rItemType) == Nullness.NULLABLE) {
holder.registerProblem(errorElement,
"Assigning a collection of nullable elements into a collection of non-null elements",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
}
};
}
@@ -0,0 +1,21 @@
import typeUse.*;
import java.util.*;
class JC {
public static void main(String[] args) {
List<@Nullable String> list = new ArrayList<>();
print(<warning descr="Assigning a collection of nullable elements into a collection of non-null elements">list</warning>);
List<@NotNull String> <warning descr="Assigning a collection of nullable elements into a collection of non-null elements">list2</warning> = list;
List<@NotNull String> list3;
list2 <warning descr="Assigning a collection of nullable elements into a collection of non-null elements">=</warning> list;
}
private static void print(List<@NotNull String> list) {
for (String s : list) {
System.out.println(s.length());
}
}
}
@@ -207,4 +207,9 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
doTest();
}
public void testPassingNullableCollectionWhereNotNullIsExpected() {
DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
}