mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] First class support for Collection.removeIf()
Fixes IDEA-286737 Side-effects from removeIf predicate are not taken into account GitOrigin-RevId: 6efc71f98d10824a288e4510a209da34ad381bbb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7d070fec8f
commit
d2e2a474ad
+1
-1
@@ -2394,7 +2394,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
private static final CallInliner[] INLINERS = {
|
||||
new OptionalChainInliner(), new LambdaInliner(),
|
||||
new OptionalChainInliner(), new LambdaInliner(), new CollectionUpdateInliner(),
|
||||
new StreamChainInliner(), new MapUpdateInliner(), new AssumeInliner(), new ClassMethodsInliner(),
|
||||
new AssertAllInliner(), new BoxingInliner(), new SimpleMethodInliner(),
|
||||
new TransformInliner(), new EnumCompareInliner(), new IndexOfInliner()
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow.java.inliner;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.Mutability;
|
||||
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
|
||||
import com.intellij.codeInspection.dataFlow.java.CFGBuilder;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.problems.MutabilityProblem;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CollectionUpdateInliner implements CallInliner {
|
||||
private static final CallMatcher COLLECTION_REMOVEIF = CallMatcher.instanceCall(
|
||||
CommonClassNames.JAVA_UTIL_COLLECTION, "removeIf").parameterTypes(CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE);
|
||||
|
||||
@Override
|
||||
public boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call) {
|
||||
if (COLLECTION_REMOVEIF.test(call)) {
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return false;
|
||||
PsiType elementType = PsiUtil.substituteTypeParameter(qualifier.getType(), CommonClassNames.JAVA_UTIL_COLLECTION, 0, true);
|
||||
DfType elementDfType = DfTypes.typedObject(elementType, DfaPsiUtil.getTypeNullability(elementType));
|
||||
PsiExpression predicate = call.getArgumentList().getExpressions()[0];
|
||||
DfaVariableValue result = builder.createTempVariable(PsiType.BOOLEAN);
|
||||
builder
|
||||
.assignAndPop(result, DfTypes.FALSE)
|
||||
.pushExpression(qualifier) // stack: qualifier
|
||||
.ensure(RelationType.IS, Mutability.MUTABLE.asDfType(), new MutabilityProblem(call, true), null)
|
||||
.evaluateFunction(predicate)
|
||||
.unwrap(SpecialField.COLLECTION_SIZE) // stack: qualifier.size
|
||||
.dup() // stack: qualifier.size qualifier.size
|
||||
.push(DfTypes.intValue(0)) // stack: qualifier.size qualifier.size 0
|
||||
.ifCondition(RelationType.GT)
|
||||
.doWhileUnknown()
|
||||
.push(elementDfType)
|
||||
.invokeFunction(1, predicate)
|
||||
.ifConditionIs(true)
|
||||
.pushUnknown()
|
||||
.assign()
|
||||
.assignAndPop(result, DfTypes.TRUE)
|
||||
.end()
|
||||
.end()
|
||||
.end()
|
||||
.pop()
|
||||
.push(result);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
class RemoveIf
|
||||
{
|
||||
void unmodifiable(Collection<String> c) {
|
||||
c = Collections.unmodifiableCollection(c);
|
||||
c.<warning descr="Immutable object is modified">removeIf</warning>(x -> true);
|
||||
}
|
||||
|
||||
void falsePredicate(Collection<String> c) {
|
||||
int size = c.size();
|
||||
c.removeIf(x -> false);
|
||||
if (<warning descr="Condition 'size == c.size()' is always 'true'">size == c.size()</warning>) {}
|
||||
c.removeIf(String::isEmpty);
|
||||
if (size == c.size()) {}
|
||||
}
|
||||
|
||||
void empty(Collection<String> c) {
|
||||
if (!c.isEmpty()) return;
|
||||
c.removeIf(x -> c.add(x));
|
||||
if (<warning descr="Condition 'c.isEmpty()' is always 'true'">c.isEmpty()</warning>) {}
|
||||
}
|
||||
|
||||
int x;
|
||||
|
||||
void returnValueAndSideEffect(Collection<String> c) {
|
||||
x = 0;
|
||||
boolean ret = c.removeIf(v -> {
|
||||
x = 1;
|
||||
return v.isEmpty();
|
||||
});
|
||||
if (<warning descr="Condition 'x == 0 && ret' is always 'false'">x == 0 && <warning descr="Condition 'ret' is always 'false' when reached">ret</warning></warning>) {}
|
||||
if (x == 1 && ret) {}
|
||||
}
|
||||
}
|
||||
+3
@@ -78,6 +78,9 @@ public class DataFlowRangeAnalysisTest extends DataFlowInspectionTestCase {
|
||||
public void testWidenMismatch() { doTest(); }
|
||||
public void testDontWidenPlusInLoop() { doTest(); }
|
||||
public void testCollectionAddRemove() { doTest(); }
|
||||
|
||||
public void testCollectionRemoveIf() { doTest(); }
|
||||
|
||||
public void testRelationsOnAddition() { doTest(); }
|
||||
public void testModSpecialCase() { doTest(); }
|
||||
public void testArrayAccessWithCastInCountedLoop() { doTest(); }
|
||||
|
||||
@@ -1899,11 +1899,6 @@
|
||||
<item name="java.util.Collection boolean removeAll(java.util.Collection<?>) 0">
|
||||
<annotation name="org.jetbrains.annotations.NotNull" />
|
||||
</item>
|
||||
<item name='java.util.Collection boolean removeIf(java.util.function.Predicate<? super E>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name="java.util.Collection boolean retainAll(java.util.Collection<?>)">
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
|
||||
Reference in New Issue
Block a user