From 3fb458c6513fb1e9ad092e8662033f7d7e6e0059 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jun 2017 15:22:53 +0200 Subject: [PATCH] IDEA-171248 Nullability: missing warning in for-each cycle support for forEach lambda parameters --- .../codeInspection/dataFlow/DfaPsiUtil.java | 27 ++++++++++++++----- .../nullable/NullableStuffInspectionBase.java | 24 +++++++++-------- .../ForeachParameterNullability.java | 6 +++-- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 9b944910847e..8c1777873ec2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -17,12 +17,12 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.codeInspection.dataFlow.instructions.Instruction; import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction; import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.util.Ref; -import com.intellij.patterns.PsiJavaPatterns; import com.intellij.psi.*; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; @@ -93,15 +93,28 @@ public class DfaPsiUtil { Nullness fromType = getTypeNullability(resultType); if (fromType != Nullness.UNKNOWN) return fromType; - if (PsiJavaPatterns.psiParameter().withParents(PsiParameterList.class, PsiLambdaExpression.class).accepts(owner)) { - PsiLambdaExpression lambda = (PsiLambdaExpression)owner.getParent().getParent(); - int index = lambda.getParameterList().getParameterIndex((PsiParameter)owner); - return getFunctionalParameterNullability(lambda, index); + if (owner instanceof PsiParameter) { + return inferParameterNullability((PsiParameter)owner); } return Nullness.UNKNOWN; } + @NotNull + public static Nullness inferParameterNullability(@NotNull PsiParameter parameter) { + PsiElement parent = parameter.getParent(); + if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression) { + return getFunctionalParameterNullability((PsiLambdaExpression)parent.getParent(), ((PsiParameterList)parent).getParameterIndex(parameter)); + } + if (parent instanceof PsiForeachStatement) { + PsiExpression iteratedValue = ((PsiForeachStatement)parent).getIteratedValue(); + if (iteratedValue != null) { + return getTypeNullability(JavaGenericsUtil.getCollectionItemType(iteratedValue)); + } + } + return Nullness.UNKNOWN; + } + @NotNull public static Nullness getTypeNullability(@Nullable PsiType type) { if (type == null) return Nullness.UNKNOWN; @@ -142,7 +155,7 @@ public class DfaPsiUtil { if(nullness != Nullness.UNKNOWN) { return nullness; } - PsiClassType type = ObjectUtils.tryCast(function.getFunctionalInterfaceType(), PsiClassType.class); + PsiClassType type = ObjectUtils.tryCast(LambdaUtil.getFunctionalInterfaceType(function, true), PsiClassType.class); PsiMethod sam = LambdaUtil.getFunctionalInterfaceMethod(type); if (sam != null && index < sam.getParameterList().getParametersCount()) { PsiParameter parameter = sam.getParameterList().getParameters()[index]; @@ -151,7 +164,7 @@ public class DfaPsiUtil { return nullness; } PsiType parameterType = type.resolveGenerics().getSubstitutor().substitute(parameter.getType()); - return getTypeNullability(parameterType); + return getTypeNullability(GenericsUtil.eliminateWildcards(parameterType, false, true)); } return Nullness.UNKNOWN; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java index 7b6b3328ea00..b0cde632df71 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java @@ -523,22 +523,24 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo PsiAnnotation annotation = isDeclaredNotNull == null ? isDeclaredNullable : isDeclaredNotNull; reportPrimitiveType(holder, annotation, annotation, parameter); } - if (parameter.getParent() instanceof PsiForeachStatement) { - PsiExpression iteratedValue = ((PsiForeachStatement)parameter.getParent()).getIteratedValue(); - Nullness itemTypeNullability = DfaPsiUtil.getTypeNullability(iteratedValue == null ? null : JavaGenericsUtil.getCollectionItemType(iteratedValue)); - if (isDeclaredNotNull != null && itemTypeNullability == Nullness.NULLABLE) { - holder.registerProblem(isDeclaredNotNull, "Loop parameter can be null", - new RemoveAnnotationQuickFix(isDeclaredNotNull, null)); - } - else if (isDeclaredNullable != null && itemTypeNullability == Nullness.NOT_NULL) { - holder.registerProblem(isDeclaredNullable, "Loop parameter is always not-null", - new RemoveAnnotationQuickFix(isDeclaredNullable, null)); - } + if (parameter instanceof PsiParameter) { + checkLoopParameterNullability(holder, isDeclaredNotNull, isDeclaredNullable, DfaPsiUtil.inferParameterNullability((PsiParameter)parameter)); } return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null); } + private static void checkLoopParameterNullability(ProblemsHolder holder, @Nullable PsiAnnotation notNull, @Nullable PsiAnnotation nullable, Nullness expectedNullability) { + if (notNull != null && expectedNullability == Nullness.NULLABLE) { + holder.registerProblem(notNull, "Parameter can be null", + new RemoveAnnotationQuickFix(notNull, null)); + } + else if (nullable != null && expectedNullability == Nullness.NOT_NULL) { + holder.registerProblem(nullable, "Parameter is always not-null", + new RemoveAnnotationQuickFix(nullable, null)); + } + } + private static void reportPrimitiveType(final ProblemsHolder holder, final PsiElement psiElement, final PsiAnnotation annotation, final PsiModifierListOwner listOwner) { holder.registerProblem(psiElement.isPhysical() ? psiElement : listOwner.getNavigationElement(), diff --git a/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java b/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java index 5d4268a5c4d3..874b0ab2ac14 100644 --- a/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java +++ b/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java @@ -11,11 +11,13 @@ class JC { } void usage() { - for (@NotNull Object o : getNullableStuff()) { + for (@NotNull Object o : getNullableStuff()) { System.out.println(o.getClass()); } - for (@Nullable Object o : getNotNullStuff()) { + for (@Nullable Object o : getNotNullStuff()) { System.out.println(o.getClass()); } + getNullableStuff().forEach((@NotNull Object s) -> System.out.println(s.hashCode())); + getNotNullStuff().forEach((@Nullable Object s) -> System.out.println(s.hashCode())); } } \ No newline at end of file