[java-inspections] FunctionalExpressionCanBeFolded: disable if qualifier subtype overrides default methods of function

Fixes IDEA-310524 "Method reference can be replaced with qualifier changes" suggestion changes behavior in unwanted ways. (Spring Security)

GitOrigin-RevId: f927ab534894188fa418a499c98af3a7c169b0cd
This commit is contained in:
Tagir Valeev
2024-07-11 10:58:42 +02:00
committed by intellij-monorepo-bot
parent 78a77d064c
commit 5778a45943
3 changed files with 89 additions and 2 deletions

View File

@@ -1,6 +1,8 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.types.DfReferenceType;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
@@ -8,7 +10,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -48,20 +52,47 @@ public final class FunctionalExpressionCanBeFoldedInspection extends AbstractBas
if (qualifierExpression != null && referenceNameElement != null && !(qualifierExpression instanceof PsiSuperExpression)) {
final PsiType qualifierType = qualifierExpression.getType();
if (qualifierType != null) {
//don't get ground type as check is required over expected type instead
//don't get the ground type as check is required over the expected type instead
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, true);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod != null) {
final PsiElement resolve = resolver.get();
if (resolve instanceof PsiMethod &&
(interfaceMethod == resolve || MethodSignatureUtil.isSuperMethod(interfaceMethod, (PsiMethod)resolve)) &&
TypeConversionUtil.isAssignable(functionalInterfaceType, qualifierType)) {
TypeConversionUtil.isAssignable(functionalInterfaceType, qualifierType) &&
!mayOverrideDefaultMethods(qualifierExpression, functionalInterfaceType)) {
holder.registerProblem(referenceNameElement, errorMessage, new ReplaceMethodRefWithQualifierFix());
}
}
}
}
}
private static boolean mayOverrideDefaultMethods(PsiExpression expression, PsiType functionalInterfaceType) {
PsiClass functionalInterface = PsiUtil.resolveClassInClassTypeOnly(functionalInterfaceType);
if (functionalInterface == null || !functionalInterface.isInterface()) return true;
if (!ContainerUtil.exists(functionalInterface.getAllMethods(), m -> m.hasModifierProperty(PsiModifier.DEFAULT))) {
return false;
}
PsiType qualifierType = null;
if (CommonDataflow.getDfType(expression) instanceof DfReferenceType refType) {
qualifierType = refType.getConstraint().getPsiType(functionalInterface.getProject());
}
if (qualifierType == null) {
qualifierType = expression.getType();
}
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifierType);
if (qualifierClass == null) return true;
if (qualifierClass.isEquivalentTo(functionalInterface)) return false;
for (PsiMethod method : qualifierClass.getAllMethods()) {
for (PsiMethod superMethod : method.findSuperMethods(functionalInterface)) {
if (superMethod.hasModifierProperty(PsiModifier.DEFAULT)) {
return true;
}
}
}
return false;
}
};
}

View File

@@ -0,0 +1,28 @@
// "Fix all 'Functional expression can be folded' problems in file" "true"
import java.util.function.Predicate;
class Test {
interface LoggingPredicate extends Predicate<String> {
@Override
default Predicate<String> negate() {
System.out.println("negating!");
return Predicate.super.negate();
}
}
interface SubPredicate extends Predicate<String> {
}
public static void main(String[] args) {
LoggingPredicate predicate = s -> !s.isEmpty();
test(predicate::test);
Predicate<String> copy = predicate;
test(copy::test);
SubPredicate subPredicate = s -> !s.isEmpty();
test(subPredicate);
}
private static void test(Predicate<String> test) {
System.out.println(test.negate().test("hello"));
}
}

View File

@@ -0,0 +1,28 @@
// "Fix all 'Functional expression can be folded' problems in file" "true"
import java.util.function.Predicate;
class Test {
interface LoggingPredicate extends Predicate<String> {
@Override
default Predicate<String> negate() {
System.out.println("negating!");
return Predicate.super.negate();
}
}
interface SubPredicate extends Predicate<String> {
}
public static void main(String[] args) {
LoggingPredicate predicate = s -> !s.isEmpty();
test(predicate::test);
Predicate<String> copy = predicate;
test(copy::test);
SubPredicate subPredicate = s -> !s.isEmpty();
test(subPredicate::<caret>test);
}
private static void test(Predicate<String> test) {
System.out.println(test.negate().test("hello"));
}
}