[java-inspections] IDEA-358770 Stream/Optional desugaring: support Predicate.not

GitOrigin-RevId: af9cdb5e674406bbbe94a746e85a662a590a4d3d
This commit is contained in:
Tagir Valeev
2024-09-04 15:45:16 +02:00
committed by intellij-monorepo-bot
parent 870a7cd709
commit cf460af83e
5 changed files with 151 additions and 3 deletions

View File

@@ -14,6 +14,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.LambdaRefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.*;
import one.util.streamex.EntryStream;
import one.util.streamex.StreamEx;
@@ -35,6 +36,9 @@ import java.util.function.Consumer;
*/
public abstract class FunctionHelper {
private static final Logger LOG = Logger.getInstance(FunctionHelper.class);
private static final CallMatcher PREDICATE_NOT = CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE, "not")
.parameterCount(1);
private final PsiType myResultType;
@@ -42,7 +46,10 @@ public abstract class FunctionHelper {
myResultType = resultType;
}
public PsiType getResultType() {
/**
* @return result type of this function
*/
public final PsiType getResultType() {
return myResultType;
}
@@ -194,6 +201,13 @@ public abstract class FunctionHelper {
MethodCallUtils.isCallToStaticMethod(call, CommonClassNames.JAVA_UTIL_COLLECTIONS, "reverseOrder", 0)) {
return paramCount == 2 ? new InlinedFunctionHelper(returnType, 2, "{1}.compareTo({0})") : null;
}
if (PREDICATE_NOT.test(call)) {
PsiExpression arg = call.getArgumentList().getExpressions()[0];
FunctionHelper delegate = create(arg, paramCount, false);
if (delegate != null) {
return new PredicateNotFunctionHelper(delegate);
}
}
}
return new ComplexExpressionFunctionHelper(returnType, type, interfaceMethod.getName(), expression);
}
@@ -538,6 +552,48 @@ public abstract class FunctionHelper {
myExpression = context.createExpression(MessageFormat.format(myTemplate, (Object[])argumentValues));
}
}
private static class PredicateNotFunctionHelper extends FunctionHelper {
private final @NotNull FunctionHelper myDelegate;
private PsiExpression myExpression;
private PredicateNotFunctionHelper(@NotNull FunctionHelper delegate) {
super(delegate.getResultType());
myDelegate = delegate;
}
@Override
public PsiExpression getExpression() {
LOG.assertTrue(myExpression != null);
return myExpression;
}
@Override
public void rename(String oldName, String newName, ChainContext context) {
myDelegate.rename(oldName, newName, context);
}
@Override
public void registerReusedElements(Consumer<? super PsiElement> consumer) {
myDelegate.registerReusedElements(consumer);
}
@Override
public void suggestOutputNames(ChainContext context, ChainVariable var) {
myDelegate.suggestOutputNames(context, var);
}
@Override
public void preprocessVariable(ChainContext context, ChainVariable var, int index) {
myDelegate.preprocessVariable(context, var, index);
}
@Override
public void transform(ChainContext context, String... argumentValues) {
myDelegate.transform(context, argumentValues);
myExpression = context.createExpression(BoolUtils.getNegatedExpressionText(myDelegate.getExpression()));
}
}
private static class LambdaFunctionHelper extends FunctionHelper {
@NotNull String @NotNull [] myParameters;
@@ -605,8 +661,8 @@ public abstract class FunctionHelper {
PsiParameter parameter = lambda.getParameterList().getParameters()[0];
PsiElement body = lambda.getBody();
LOG.assertTrue(body != null);
boolean mayBeNotFinal = ReferencesSearch.search(parameter, new LocalSearchScope(body))
.allMatch(e -> PsiTreeUtil.getParentOfType(e.getElement(), PsiLambdaExpression.class, PsiClass.class) == lambda);
boolean mayBeNotFinal = VariableAccessUtils.getVariableReferences(parameter)
.stream().allMatch(e -> PsiTreeUtil.getParentOfType(e, PsiLambdaExpression.class, PsiClass.class) == lambda);
if (!mayBeNotFinal) {
var.markFinal();
}

View File

@@ -0,0 +1,29 @@
// "Fix all ''Optional' can be replaced with sequence of 'if' statements' problems in file" "true"
import java.util.Optional;
import java.util.function.Predicate;
class Test {
void test(String s) {
Predicate<String> external = String::isEmpty;
if (s != null) {
String string = s.trim();
if (!external.test(string)) use(string);
}
if (s != null) {
String trimmed = s.trim();
if (!trimmed.isEmpty()) use(trimmed);
}
if (s != null) {
String x = s.trim();
String trim = x.trim();
if (trim.length() <= 5) use(trim);
}
}
void use(String s) {
}
}

View File

@@ -0,0 +1,29 @@
// "Fix all ''Optional' can be replaced with sequence of 'if' statements' problems in file" "true"
import java.util.Optional;
import java.util.function.Predicate;
class Test {
void test(String s) {
Predicate<String> external = String::isEmpty;
<caret>Optional.ofNullable(s)
.map(String::trim)
.filter(Predicate.not(external))
.ifPresent(this::use);
Optional.ofNullable(s)
.map(String::trim)
.filter(Predicate.not(String::isEmpty))
.ifPresent(this::use);
Optional.ofNullable(s)
.map(x -> x.trim())
.map(x -> x.trim())
.filter(Predicate.not(x -> x.length() > 5))
.ifPresent(this::use);
}
void use(String s) {
}
}

View File

@@ -1,6 +1,8 @@
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
import java.util.*;
import java.util.stream.Stream;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
@@ -28,6 +30,26 @@ public class Main {
}
return null;
}
void predicateNot() {
boolean res = false;
for (Optional<?> o : asList(Optional.of(1), Optional.of(2), Optional.empty())) {
if (!o.isEmpty()) {
res = true;
break;
}
}
}
void predicateNotLambda() {
boolean res = false;
for (Optional<?> o : asList(Optional.of(1), Optional.of(2), Optional.empty())) {
if (o.isPresent()) {
res = true;
break;
}
}
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));

View File

@@ -1,6 +1,8 @@
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
import java.util.*;
import java.util.stream.Stream;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
@@ -12,6 +14,16 @@ public class Main {
String testTernary(String[] strings) {
return Arrays.stream(strings).filter(Objects::nonNull).anyMatch(s -> !s.startsWith("xyz")) ? "s" : null;
}
void predicateNot() {
boolean res = Stream.of(Optional.of(1), Optional.of(2), Optional.empty())
.anyMatch(Predicate.not(Optional::isEmpty));
}
void predicateNotLambda() {
boolean res = Stream.of(Optional.of(1), Optional.of(2), Optional.empty())
.anyMatch(Predicate.not(o -> o.isEmpty()));
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));