mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
StreamChainInliner: special support for toArray; support for toList/toArray expected component type nullability
Fixes IDEA-194697 Nullability analysis for stream chains should check against element-level nullability annotations
This commit is contained in:
+25
-1
@@ -16,6 +16,7 @@
|
||||
package com.intellij.codeInspection.dataFlow.inliner;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaUnknownValue;
|
||||
@@ -27,6 +28,7 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMapper;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
import com.siyeh.ig.psiutils.StreamApiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -49,6 +51,9 @@ public class StreamChainInliner implements CallInliner {
|
||||
private static final CallMatcher MATCH_TERMINAL = instanceCall(JAVA_UTIL_STREAM_BASE_STREAM, "anyMatch", "allMatch",
|
||||
"noneMatch").parameterCount(1);
|
||||
private static final CallMatcher SUM_TERMINAL = instanceCall(JAVA_UTIL_STREAM_BASE_STREAM, "sum", "count").parameterCount(0);
|
||||
private static final CallMatcher TO_ARRAY_TERMINAL = anyOf(
|
||||
instanceCall(JAVA_UTIL_STREAM_STREAM, "toArray").parameterCount(0),
|
||||
instanceCall(JAVA_UTIL_STREAM_STREAM, "toArray").parameterTypes("java.util.function.IntFunction"));
|
||||
private static final CallMatcher OPTIONAL_TERMINAL =
|
||||
anyOf(instanceCall(JAVA_UTIL_STREAM_BASE_STREAM, "min", "max").parameterCount(0),
|
||||
instanceCall(JAVA_UTIL_STREAM_BASE_STREAM, "reduce").parameterCount(1),
|
||||
@@ -117,6 +122,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
.register(SUM_TERMINAL, SumTerminalStep::new)
|
||||
.register(MIN_MAX_TERMINAL, MinMaxTerminalStep::new)
|
||||
.register(OPTIONAL_TERMINAL, OptionalTerminalStep::new)
|
||||
.register(TO_ARRAY_TERMINAL, ToArrayStep::new)
|
||||
.register(COLLECT_TERMINAL, StreamChainInliner::createTerminalFromCollector);
|
||||
|
||||
private static final Step NULL_TERMINAL_STEP = new Step(null, null, null) {
|
||||
@@ -558,7 +564,25 @@ public class StreamChainInliner implements CallInliner {
|
||||
|
||||
@Override
|
||||
boolean expectNotNull() {
|
||||
return myImmutable;
|
||||
if (myImmutable) return true;
|
||||
PsiType collectionType = ExpectedTypeUtils.findExpectedType(myCall, false);
|
||||
PsiType itemType = JavaGenericsUtil.getCollectionItemType(collectionType, myCall.getResolveScope());
|
||||
return DfaPsiUtil.getTypeNullability(itemType) == Nullability.NOT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static class ToArrayStep extends ToCollectionStep {
|
||||
ToArrayStep(@NotNull PsiMethodCallExpression call) {
|
||||
super(call, ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pushInitialValue(CFGBuilder builder) {
|
||||
if (myFunction == null) {
|
||||
super.pushInitialValue(builder);
|
||||
} else {
|
||||
builder.pushUnknown().invokeFunction(1, myFunction, Nullability.NOT_NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -235,4 +235,7 @@ public class StreamInlining {
|
||||
}
|
||||
}
|
||||
|
||||
void testToArray(List<String> list) {
|
||||
list.stream().toArray(size -> <warning descr="Function may return null, but it's not allowed here">null</warning>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import foo.NotNull;
|
||||
import foo.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
public class StreamTypeAnnoInlining {
|
||||
void testToArray() {
|
||||
@NotNull Object @NotNull[] foo0 = Stream.of("a", "b")
|
||||
.map(x-> <warning descr="Function may return null, but it's not allowed here">"a".equals(x) ? null : x.toUpperCase()</warning>)
|
||||
.toArray();
|
||||
|
||||
// IDEA-194697
|
||||
@NotNull String @NotNull[] foo = Stream.of("a", "b")
|
||||
.map(x-> <warning descr="Function may return null, but it's not allowed here">"a".equals(x) ? null : x.toUpperCase()</warning>)
|
||||
.toArray(String[]::new);
|
||||
|
||||
@NotNull String @NotNull[] foo1 = Stream.of("b", "c")
|
||||
.map(x-> <warning descr="Condition '\"a\".equals(x)' is always 'false'">"a".equals(x)</warning> ? null : x.toUpperCase())
|
||||
.toArray(String[]::new);
|
||||
|
||||
@NotNull String @NotNull[] foo2 = Stream.of("a", "b")
|
||||
.map(x-> "a".equals(x) ? null : x.toUpperCase())
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
void testFromList(List<@NotNull String> list) {
|
||||
list.stream().filter(<warning descr="Method reference result is always 'true'">Objects::nonNull</warning>).forEach(System.out::println);
|
||||
}
|
||||
|
||||
List<@NotNull String> testFromNullableToNotNull(List<@Nullable String> list) {
|
||||
return list.stream().map(x -> <warning descr="Function may return null, but it's not allowed here">x</warning>).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
List<@Nullable String> testFromNullableToNullable(List<@Nullable String> list) {
|
||||
return list.stream().map(x -> x).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
List<@NotNull String> testFromUnknownToNotNull(List<String> list) {
|
||||
return list.stream().map(x -> x).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,10 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
|
||||
public void testStreamCollectorInlining() { doTest(); }
|
||||
public void testStreamComparatorInlining() { doTest(); }
|
||||
public void testStreamKnownSource() { doTest(); }
|
||||
public void testStreamTypeAnnoInlining() {
|
||||
setupTypeUseAnnotations("foo", myFixture);
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testMapGetWithNotNullKeys() { doTestWithCustomAnnotations(); }
|
||||
public void testInferNestedForeachNullability() { doTestWithCustomAnnotations(); }
|
||||
|
||||
Reference in New Issue
Block a user