From 95fac58f03ad2939785b5b71418ae2369ac4fe92 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 7 Dec 2016 12:13:02 +0700 Subject: [PATCH] ExcessiveLambdaUsageInspection: support JUnit5 assertions; regex as lambdaMethod, minor improvements (IDEA-CR-16398) --- .../ExcessiveLambdaUsageInspection.java | 55 ++++++++++++++----- .../afterJUnit5Assert.java | 17 ++++++ .../beforeJUnit5Assert.java | 17 ++++++ 3 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterJUnit5Assert.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeJUnit5Assert.java diff --git a/java/java-impl/src/com/intellij/codeInspection/ExcessiveLambdaUsageInspection.java b/java/java-impl/src/com/intellij/codeInspection/ExcessiveLambdaUsageInspection.java index f2199bbf8ebb..881169fb9fe8 100644 --- a/java/java-impl/src/com/intellij/codeInspection/ExcessiveLambdaUsageInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/ExcessiveLambdaUsageInspection.java @@ -23,9 +23,11 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ExpressionUtils; +import org.intellij.lang.annotations.RegExp; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import java.util.regex.Pattern; import java.util.stream.Stream; /** @@ -38,9 +40,10 @@ public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspection new ExcessiveLambdaInfo("java.util.OptionalInt", "orElseGet", "orElse", 0, true), new ExcessiveLambdaInfo("java.util.OptionalLong", "orElseGet", "orElse", 0, true), new ExcessiveLambdaInfo("java.util.OptionalDouble", "orElseGet", "orElse", 0, true), - new ExcessiveLambdaInfo("java.util.OptionalDouble", "orElseGet", "orElse", 0, true), - new ExcessiveLambdaInfo("com.google.common.base.Optional", "or", "or", 0, true), - new ExcessiveLambdaInfo("java.util.Objects", "requireNonNull", "requireNonNull", 1, true) + new ExcessiveLambdaInfo("com.google.common.base.Optional", "or", "*", 0, true), + new ExcessiveLambdaInfo("java.util.Objects", "requireNonNull", "*", 1, true), + new ExcessiveLambdaInfo("org.junit.jupiter.api.Assertions", "assert.*|fail", "*", -1, true), + new ExcessiveLambdaInfo("org.junit.jupiter.api.Assertions", "assert(True|False)", "*", 0, true), }; @NotNull @@ -62,10 +65,12 @@ public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspection if (Stream.of(lambda.getParameterList().getParameters()).anyMatch(param -> ExpressionUtils.isReferenceTo(expr, param))) return; for (ExcessiveLambdaInfo info : INFOS) { - if(info.isApplicable((PsiMethodCallExpression)gParent, lambda)) { + PsiMethodCallExpression call = (PsiMethodCallExpression)gParent; + if(info.isApplicable(call, lambda)) { holder.registerProblem(lambda, InspectionsBundle.message("inspection.excessive.lambda.message"), ProblemHighlightType.LIKE_UNUSED_SYMBOL, - new TextRange(0, expr.getTextOffset() - lambda.getTextOffset()), new RemoveExcessiveLambdaFix(info)); + new TextRange(0, expr.getStartOffsetInParent()), + new RemoveExcessiveLambdaFix(info, info.getTargetName(call))); } } } @@ -74,16 +79,18 @@ public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspection static class RemoveExcessiveLambdaFix implements LocalQuickFix { private final ExcessiveLambdaInfo myInfo; + private final String myName; - public RemoveExcessiveLambdaFix(ExcessiveLambdaInfo info) { + public RemoveExcessiveLambdaFix(ExcessiveLambdaInfo info, String name) { myInfo = info; + myName = name; } @Nls @NotNull @Override public String getName() { - return InspectionsBundle.message("inspection.excessive.lambda.fix.name", myInfo.myConstantMethod); + return InspectionsBundle.message("inspection.excessive.lambda.fix.name", myName); } @Nls @@ -103,7 +110,7 @@ public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspection PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class); if(call == null) return; - call.getMethodExpression().handleElementRename(myInfo.myConstantMethod); + call.getMethodExpression().handleElementRename(myInfo.getTargetName(call)); CommentTracker ct = new CommentTracker(); PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); ct.replaceAndRestoreComments(lambda, factory.createExpressionFromText(ct.text(body), lambda)); @@ -112,31 +119,49 @@ public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspection static class ExcessiveLambdaInfo { final String myClass; - final String myLambdaMethod; + final Pattern myLambdaMethod; final String myConstantMethod; final int myParameterIndex; final boolean myCanUseReturnValue; - ExcessiveLambdaInfo(String aClass, String lambdaMethod, String constantMethod, int index, boolean canUseReturnValue) { + /** + * @param aClass class containing both methods + * @param lambdaMethod regexp to match the name of the method which accepts lambda argument + * @param constantMethod name of the equivalent method ("*" if name is the same as lambdaMethod) + * accepting constant instead of lambda argument (all other args must be the same) + * @param index index of lambda argument, zero-based, or -1 to denote the last argument + * @param canUseReturnValue true if method return value does not depend on whether lambda or constant version is used + */ + ExcessiveLambdaInfo(String aClass, @RegExp String lambdaMethod, String constantMethod, int index, boolean canUseReturnValue) { myClass = aClass; - myLambdaMethod = lambdaMethod; + myLambdaMethod = Pattern.compile(lambdaMethod); myConstantMethod = constantMethod; myParameterIndex = index; myCanUseReturnValue = canUseReturnValue; } boolean isApplicable(PsiMethodCallExpression call, PsiLambdaExpression lambda) { - if(!myLambdaMethod.equals(call.getMethodExpression().getReferenceName())) return false; + String name = call.getMethodExpression().getReferenceName(); + if(name == null || !myLambdaMethod.matcher(name).matches()) return false; if(!myCanUseReturnValue && !(call.getParent() instanceof PsiExpressionStatement)) return false; PsiExpression[] args = call.getArgumentList().getExpressions(); - if(args.length <= myParameterIndex || args[myParameterIndex] != lambda) return false; + if(args.length == 0) return false; + int index = myParameterIndex == -1 ? args.length - 1 : myParameterIndex; + if(args.length <= index || args[index] != lambda) return false; PsiMethod method = call.resolveMethod(); if(method == null) return false; PsiParameter[] parameters = method.getParameterList().getParameters(); - if(parameters.length <= myParameterIndex) return false; - PsiClass fnClass = PsiUtil.resolveClassInClassTypeOnly(parameters[myParameterIndex].getType()); + if(parameters.length <= index) return false; + PsiClass fnClass = PsiUtil.resolveClassInClassTypeOnly(parameters[index].getType()); return fnClass != null && LambdaUtil.getFunction(fnClass) != null && InheritanceUtil.isInheritor(method.getContainingClass(), false, myClass); } + + public String getTargetName(PsiMethodCallExpression call) { + if(myConstantMethod.equals("*")) { + return call.getMethodExpression().getReferenceName(); + } + return myConstantMethod; + } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterJUnit5Assert.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterJUnit5Assert.java new file mode 100644 index 000000000000..16c205e6f526 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterJUnit5Assert.java @@ -0,0 +1,17 @@ +// "Use 'assertEquals' method without lambda" "true" +package org.junit.jupiter.api; + +interface Supplier { + T supply(); +} + +class Assertions { + static void assertEquals(int expected, int actual, String message) {}; + static void assertEquals(int expected, int actual, Supplier message) {}; +} + +class Test { + public void test() { + Assertions.assertEquals(4, 2+2, "Math works!"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeJUnit5Assert.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeJUnit5Assert.java new file mode 100644 index 000000000000..b16804df7658 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeJUnit5Assert.java @@ -0,0 +1,17 @@ +// "Use 'assertEquals' method without lambda" "true" +package org.junit.jupiter.api; + +interface Supplier { + T supply(); +} + +class Assertions { + static void assertEquals(int expected, int actual, String message) {}; + static void assertEquals(int expected, int actual, Supplier message) {}; +} + +class Test { + public void test() { + Assertions.assertEquals(4, 2+2, () -> "Math works!"); + } +} \ No newline at end of file