From c841cb3b44796606f4b1cb24011ac4845303a686 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 19 Jan 2018 15:03:02 +0700 Subject: [PATCH] IDEA-185167 Allow to convert automatically Optional.orElse(...) to Optional.orElseGet(() -> ...) --- java/java-impl/src/META-INF/JavaPlugin.xml | 7 +- .../ExcessiveLambdaUsageInspection.java | 153 ------------------ .../ExcessiveLambdaUsageInspection.java | 91 +++++++++++ ...ExplicitArgumentCanBeLambdaInspection.java | 80 +++++++++ .../LambdaAndExplicitMethodPair.java | 136 ++++++++++++++++ .../ExplicitArgumentCanBeLambda.html | 10 ++ .../afterMapPutIfAbsent.java | 10 ++ .../afterOptionalOrElse.java | 13 ++ .../afterOptionalOrElseCapture.java | 14 ++ .../beforeMapPutIfAbsent.java | 10 ++ .../beforeMapPutIfAbsentUsed.java | 10 ++ .../beforeOptionalOrElse.java | 13 ++ .../beforeOptionalOrElseCapture.java | 14 ++ .../beforeOptionalOrElseCaptureNonFinal.java | 15 ++ .../beforeOptionalOrElseTrivial.java | 9 ++ .../ExcessiveLambdaUsageInspectionTest.java | 2 +- ...icitArgumentCanBeLambdaInspectionTest.java | 33 ++++ .../src/messages/InspectionsBundle.properties | 4 + 18 files changed, 469 insertions(+), 155 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/codeInspection/ExcessiveLambdaUsageInspection.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExplicitArgumentCanBeLambdaInspection.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/LambdaAndExplicitMethodPair.java create mode 100644 java/java-impl/src/inspectionDescriptions/ExplicitArgumentCanBeLambda.html create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterMapPutIfAbsent.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElse.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElseCapture.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsent.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsentUsed.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElse.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCapture.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCaptureNonFinal.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseTrivial.java create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExplicitArgumentCanBeLambdaInspectionTest.java diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index d1449528720b..ba00a4219911 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -580,8 +580,13 @@ + ExpressionUtils.isReferenceTo(expr, param))) return; - - for (ExcessiveLambdaInfo info : INFOS) { - 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.getStartOffsetInParent()), - new RemoveExcessiveLambdaFix(info, info.getTargetName(call))); - } - } - } - }; - } - - static class RemoveExcessiveLambdaFix implements LocalQuickFix { - private final ExcessiveLambdaInfo myInfo; - private final String myName; - - public RemoveExcessiveLambdaFix(ExcessiveLambdaInfo info, String name) { - myInfo = info; - myName = name; - } - - @Nls - @NotNull - @Override - public String getName() { - return InspectionsBundle.message("inspection.excessive.lambda.fix.name", myName); - } - - @Nls - @NotNull - @Override - public String getFamilyName() { - return InspectionsBundle.message("inspection.excessive.lambda.fix.family.name"); - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); - if(!(element instanceof PsiLambdaExpression)) return; - PsiLambdaExpression lambda = (PsiLambdaExpression)element; - PsiElement body = lambda.getBody(); - if(body == null) return; - PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class); - if(call == null) return; - - ExpressionUtils.bindCallTo(call, myInfo.getTargetName(call)); - CommentTracker ct = new CommentTracker(); - ct.replaceAndRestoreComments(lambda, ct.text(body)); - } - } - - static class ExcessiveLambdaInfo { - final String myClass; - final Pattern myLambdaMethod; - final String myConstantMethod; - final int myParameterIndex; - final boolean myCanUseReturnValue; - - /** - * @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 = Pattern.compile(lambdaMethod); - myConstantMethod = constantMethod; - myParameterIndex = index; - myCanUseReturnValue = canUseReturnValue; - } - - boolean isApplicable(PsiMethodCallExpression call, PsiLambdaExpression lambda) { - 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 == 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 <= 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-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java new file mode 100644 index 000000000000..f1b7931b587b --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java @@ -0,0 +1,91 @@ +// Copyright 2000-2018 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.lambdaToExplicit; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ObjectUtils; +import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +/** + * @author Tagir Valeev + */ +public class ExcessiveLambdaUsageInspection extends AbstractBaseJavaLocalInspectionTool { + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitLambdaExpression(PsiLambdaExpression lambda) { + PsiElement parent = lambda.getParent(); + if (!(parent instanceof PsiExpressionList)) return; + PsiMethodCallExpression call = ObjectUtils.tryCast(parent.getParent(), PsiMethodCallExpression.class); + if (call == null) return; + if (!(lambda.getBody() instanceof PsiExpression)) return; + PsiExpression expr = (PsiExpression)lambda.getBody(); + if (!ExpressionUtils.isSimpleExpression(expr)) return; + if (Stream.of(lambda.getParameterList().getParameters()).anyMatch(param -> ExpressionUtils.isReferenceTo(expr, param))) return; + + for (LambdaAndExplicitMethodPair info : LambdaAndExplicitMethodPair.INFOS) { + if(info.isLambdaCall(call, lambda)) { + holder.registerProblem(lambda, InspectionsBundle.message("inspection.excessive.lambda.message"), + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + new TextRange(0, expr.getStartOffsetInParent()), + new RemoveExcessiveLambdaFix(info, info.getExplicitMethodName(call))); + } + } + } + }; + } + + static class RemoveExcessiveLambdaFix implements LocalQuickFix { + private final LambdaAndExplicitMethodPair myInfo; + private final String myName; + + public RemoveExcessiveLambdaFix(LambdaAndExplicitMethodPair info, String name) { + myInfo = info; + myName = name; + } + + @Nls + @NotNull + @Override + public String getName() { + return InspectionsBundle.message("inspection.excessive.lambda.fix.name", myName); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionsBundle.message("inspection.excessive.lambda.fix.family.name"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getStartElement(); + if(!(element instanceof PsiLambdaExpression)) return; + PsiLambdaExpression lambda = (PsiLambdaExpression)element; + PsiElement body = lambda.getBody(); + if(body == null) return; + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class); + if(call == null) return; + + ExpressionUtils.bindCallTo(call, myInfo.getExplicitMethodName(call)); + CommentTracker ct = new CommentTracker(); + ct.replaceAndRestoreComments(lambda, ct.text(body)); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExplicitArgumentCanBeLambdaInspection.java b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExplicitArgumentCanBeLambdaInspection.java new file mode 100644 index 000000000000..3fbddef058ca --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/ExplicitArgumentCanBeLambdaInspection.java @@ -0,0 +1,80 @@ +// Copyright 2000-2018 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.lambdaToExplicit; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ObjectUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.apache.commons.lang.ArrayUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Tagir Valeev + */ +public class ExplicitArgumentCanBeLambdaInspection extends AbstractBaseJavaLocalInspectionTool { + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + for(LambdaAndExplicitMethodPair info : LambdaAndExplicitMethodPair.INFOS) { + PsiExpression arg = info.getLambdaCandidateFromExplicitCall(call); + if(arg != null && !ExpressionUtils.isSimpleExpression(arg)) { + holder.registerProblem(arg, "Explicit argument can be converted to lambda", + new ConvertExplicitCallToLambdaFix(info, info.getLambdaMethodName(call))); + return; + } + } + } + }; + } + + private static class ConvertExplicitCallToLambdaFix implements LocalQuickFix { + private final LambdaAndExplicitMethodPair myInfo; + private final String myName; + + public ConvertExplicitCallToLambdaFix(LambdaAndExplicitMethodPair info, String name) { + myInfo = info; + myName = name; + } + + @Nls + @NotNull + @Override + public String getName() { + return InspectionsBundle.message("inspection.explicit.argument.can.be.lambda.fix.name", myName); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionsBundle.message("inspection.explicit.argument.can.be.lambda.fix.family.name"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiExpression arg = ObjectUtils.tryCast(descriptor.getStartElement(), PsiExpression.class); + if (arg == null) return; + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(arg, PsiMethodCallExpression.class); + if (call == null) return; + PsiExpression[] args = call.getArgumentList().getExpressions(); + int idx = ArrayUtils.indexOf(args, arg); + if(idx < 0) return; + ExpressionUtils.bindCallTo(call, myName); + String lambdaText = myInfo.makeLambda(arg); + PsiLambdaExpression lambda = + (PsiLambdaExpression)arg.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(lambdaText, arg)); + LambdaCanBeMethodReferenceInspection.replaceLambdaWithMethodReference(lambda); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/LambdaAndExplicitMethodPair.java b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/LambdaAndExplicitMethodPair.java new file mode 100644 index 000000000000..bc553a726b1c --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/lambdaToExplicit/LambdaAndExplicitMethodPair.java @@ -0,0 +1,136 @@ +// Copyright 2000-2018 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.lambdaToExplicit; + +import com.intellij.codeInspection.util.LambdaGenerationUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiUtil; +import one.util.streamex.StreamEx; +import org.intellij.lang.annotations.RegExp; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Pattern; + +import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; + +class LambdaAndExplicitMethodPair { + static final LambdaAndExplicitMethodPair[] INFOS = { + new LambdaAndExplicitMethodPair(CommonClassNames.JAVA_UTIL_MAP, "computeIfAbsent", "putIfAbsent", 1, "V", false, "k"), + new LambdaAndExplicitMethodPair(CommonClassNames.JAVA_UTIL_OPTIONAL, "orElseGet", "orElse", 0, "T", true), + new LambdaAndExplicitMethodPair("java.util.OptionalInt", "orElseGet", "orElse", 0, "int", true), + new LambdaAndExplicitMethodPair("java.util.OptionalLong", "orElseGet", "orElse", 0, "long", true), + new LambdaAndExplicitMethodPair("java.util.OptionalDouble", "orElseGet", "orElse", 0, "double", true), + new LambdaAndExplicitMethodPair("com.google.common.base.Optional", "or", "*", 0, "T", true), + new LambdaAndExplicitMethodPair("java.util.Objects", "requireNonNull", "*", 1, JAVA_LANG_STRING, true), + new LambdaAndExplicitMethodPair("java.util.Objects", "requireNonNullElseGet", "requireNonNullElse", 1, "T", true), + new LambdaAndExplicitMethodPair("org.junit.jupiter.api.Assertions", "assert(?!Timeout).*|fail", "*", -1, JAVA_LANG_STRING, true), + new LambdaAndExplicitMethodPair("org.junit.jupiter.api.Assertions", "assert(True|False)", "*", 0, JAVA_LANG_STRING, true), + }; + private final @NotNull String myClass; + private final @NotNull Pattern myLambdaMethod; + private final @NotNull String myExplicitMethod; + private final int myParameterIndex; + private final @NotNull String myExplicitParameterType; + private final boolean myCanUseReturnValue; + private final @NotNull String[] myDefaultLambdaParameters; + + /** + * @param aClass class containing both methods + * @param lambdaMethod regexp to match the name of the method which accepts lambda argument + * @param explicitMethod 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 explicitParameterType type of explicit parameter + * @param canUseReturnValue true if method return value does not depend on whether lambda or constant version is used + */ + LambdaAndExplicitMethodPair(@NotNull String aClass, + @NotNull @RegExp String lambdaMethod, + @NotNull String explicitMethod, + int index, + @NotNull String explicitParameterType, + boolean canUseReturnValue, + @NotNull String... defaultLambdaParameters) { + myClass = aClass; + myLambdaMethod = Pattern.compile(lambdaMethod); + myExplicitMethod = explicitMethod; + myParameterIndex = index; + myExplicitParameterType = explicitParameterType; + myCanUseReturnValue = canUseReturnValue; + myDefaultLambdaParameters = defaultLambdaParameters; + } + + boolean isLambdaCall(PsiMethodCallExpression lambdaCall, PsiLambdaExpression lambda) { + String name = lambdaCall.getMethodExpression().getReferenceName(); + if (name == null || !myLambdaMethod.matcher(name).matches()) return false; + if (!myCanUseReturnValue && !(lambdaCall.getParent() instanceof PsiExpressionStatement)) return false; + PsiExpression[] args = lambdaCall.getArgumentList().getExpressions(); + 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 = lambdaCall.resolveMethod(); + if (method == null) return false; + PsiParameter[] parameters = method.getParameterList().getParameters(); + 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); + } + + PsiExpression getLambdaCandidateFromExplicitCall(PsiMethodCallExpression explicitCall) { + String name = explicitCall.getMethodExpression().getReferenceName(); + if (name == null) return null; + if (myExplicitMethod.equals("*")) { + if (!myLambdaMethod.matcher(name).matches()) return null; + } + else if (!myExplicitMethod.equals(name)) { + return null; + } + if (!myCanUseReturnValue && !(explicitCall.getParent() instanceof PsiExpressionStatement)) return null; + PsiExpression[] args = explicitCall.getArgumentList().getExpressions(); + if (args.length == 0) return null; + int index = myParameterIndex == -1 ? args.length - 1 : myParameterIndex; + if (args.length <= index) return null; + PsiExpression arg = args[index]; + if (arg instanceof PsiFunctionalExpression) return null; + if (!LambdaGenerationUtil.canBeUncheckedLambda(arg)) return null; + PsiMethod method = explicitCall.resolveMethod(); + if (method == null) return null; + PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length <= index) return null; + PsiType type = parameters[index].getType(); + if (!type.equalsToText(myExplicitParameterType)) return null; + if (!InheritanceUtil.isInheritor(method.getContainingClass(), false, myClass)) return null; + return arg; + } + + public String getExplicitMethodName(PsiMethodCallExpression lambdaCall) { + if (myExplicitMethod.equals("*")) { + return lambdaCall.getMethodExpression().getReferenceName(); + } + return myExplicitMethod; + } + + public String getLambdaMethodName(PsiMethodCallExpression explicitCall) { + if (myExplicitMethod.equals("*")) { + return explicitCall.getMethodExpression().getReferenceName(); + } + return myLambdaMethod.pattern(); + } + + @NotNull + public String makeLambda(@NotNull PsiExpression expression) { + if (myDefaultLambdaParameters.length == 0) { + return "()->" + expression.getText(); + } + JavaCodeStyleManager manager = JavaCodeStyleManager.getInstance(expression.getProject()); + String params = StreamEx.of(myDefaultLambdaParameters) + .map(param -> manager.suggestUniqueVariableName(param, expression, true)) + .joining(","); + if (myDefaultLambdaParameters.length != 1) { + params = "(" + params + ")"; + } + return params + "->" + expression.getText(); + } +} diff --git a/java/java-impl/src/inspectionDescriptions/ExplicitArgumentCanBeLambda.html b/java/java-impl/src/inspectionDescriptions/ExplicitArgumentCanBeLambda.html new file mode 100644 index 000000000000..3ba9ba06ec39 --- /dev/null +++ b/java/java-impl/src/inspectionDescriptions/ExplicitArgumentCanBeLambda.html @@ -0,0 +1,10 @@ + + +Reports cases where method call accepts a non-trivial expression, and there's an equivalent method, +which accepts lambda instead. Converting an expression to lambda ensures that expression will not be evaluated +if it happens to be unused inside the method. E.g. optional.orElse(createDefaultValue()) could be converted +to optional.orElseGet(this::createDefaultValue). + +New in 2018.1 + + \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterMapPutIfAbsent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterMapPutIfAbsent.java new file mode 100644 index 000000000000..56a53d9a2c4d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterMapPutIfAbsent.java @@ -0,0 +1,10 @@ +// "Use 'computeIfAbsent' method with functional argument" "true" + +import java.util.Map; + +class Test { + public void test(Map> map, String key) { + map.computeIfAbsent(key, k -> new ArrayList<>()); + map.get(key).add(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElse.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElse.java new file mode 100644 index 000000000000..87217004253a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElse.java @@ -0,0 +1,13 @@ +// "Use 'orElseGet' method with functional argument" "true" + +import java.util.Optional; + +class Test { + String createDefaultString() { + return "foo"; + } + + public void test(Optional opt) { + String result = opt.orElseGet(this::createDefaultString); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElseCapture.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElseCapture.java new file mode 100644 index 000000000000..2a94b0d26dae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/afterOptionalOrElseCapture.java @@ -0,0 +1,14 @@ +// "Use 'orElseGet' method with functional argument" "true" + +import java.util.Optional; + +class Test { + String createDefaultString(int x) { + return "foo"+x; + } + + public void test(Optional opt) { + int x = 5; + String result = opt.orElseGet(() -> createDefaultString(x)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsent.java new file mode 100644 index 000000000000..7cc3aa13873d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsent.java @@ -0,0 +1,10 @@ +// "Use 'computeIfAbsent' method with functional argument" "true" + +import java.util.Map; + +class Test { + public void test(Map> map, String key) { + map.putIfAbsent(key, new ArrayList<>()); + map.get(key).add(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsentUsed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsentUsed.java new file mode 100644 index 000000000000..1623ad11c7ca --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeMapPutIfAbsentUsed.java @@ -0,0 +1,10 @@ +// "Use 'computeIfAbsent' method with functional argument" "false" + +import java.util.Map; + +class Test { + public void test(Map> map, String key) { + List old = map.putIfAbsent(key, new ArrayList<>()); + map.get(key).add(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElse.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElse.java new file mode 100644 index 000000000000..8df623bf857f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElse.java @@ -0,0 +1,13 @@ +// "Use 'orElseGet' method with functional argument" "true" + +import java.util.Optional; + +class Test { + String createDefaultString() { + return "foo"; + } + + public void test(Optional opt) { + String result = opt.orElse(createDefaultString()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCapture.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCapture.java new file mode 100644 index 000000000000..2ab501c92642 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCapture.java @@ -0,0 +1,14 @@ +// "Use 'orElseGet' method with functional argument" "true" + +import java.util.Optional; + +class Test { + String createDefaultString(int x) { + return "foo"+x; + } + + public void test(Optional opt) { + int x = 5; + String result = opt.orElse(createDefaultString(x)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCaptureNonFinal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCaptureNonFinal.java new file mode 100644 index 000000000000..20f281006152 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseCaptureNonFinal.java @@ -0,0 +1,15 @@ +// "Use 'orElseGet' method with functional argument" "false" + +import java.util.Optional; + +class Test { + String createDefaultString(int x) { + return "foo"+x; + } + + public void test(Optional opt) { + int x = 5; + x++; + String result = opt.orElse(createDefaultString(x)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseTrivial.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseTrivial.java new file mode 100644 index 000000000000..95b92f751ae5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda/beforeOptionalOrElseTrivial.java @@ -0,0 +1,9 @@ +// "Use 'orElseGet' method with functional argument" "false" + +import java.util.Optional; + +class Test { + public void test(Optional opt) { + String result = opt.orElse("foo"); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExcessiveLambdaUsageInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExcessiveLambdaUsageInspectionTest.java index b81759f59c94..69c1ea2b61de 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExcessiveLambdaUsageInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExcessiveLambdaUsageInspectionTest.java @@ -16,7 +16,7 @@ package com.intellij.java.codeInsight.daemon.quickFix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; -import com.intellij.codeInspection.ExcessiveLambdaUsageInspection; +import com.intellij.codeInspection.lambdaToExplicit.ExcessiveLambdaUsageInspection; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.testFramework.IdeaTestUtil; diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExplicitArgumentCanBeLambdaInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExplicitArgumentCanBeLambdaInspectionTest.java new file mode 100644 index 000000000000..c39e19e10bc5 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/ExplicitArgumentCanBeLambdaInspectionTest.java @@ -0,0 +1,33 @@ +// Copyright 2000-2018 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.java.codeInsight.daemon.quickFix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.lambdaToExplicit.ExcessiveLambdaUsageInspection; +import com.intellij.codeInspection.lambdaToExplicit.ExplicitArgumentCanBeLambdaInspection; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NotNull; + + +public class ExplicitArgumentCanBeLambdaInspectionTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new ExplicitArgumentCanBeLambdaInspection() + }; + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk9(); + } + + public void test() { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/explicitArgumentCanBeLambda"; + } +} \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 9591113d2ca0..232a7915a0b7 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -779,6 +779,10 @@ inspection.excessive.lambda.message=Excessive lambda usage inspection.excessive.lambda.fix.family.name=Replace lambda with constant inspection.excessive.lambda.fix.name=Use ''{0}'' method without lambda +inspection.explicit.argument.can.be.lambda.message=Explicit argument can be converted to function +inspection.explicit.argument.can.be.lambda.fix.family.name=Replace explicit argument with function +inspection.explicit.argument.can.be.lambda.fix.name=Use ''{0}'' method with functional argument + inspection.lambda.to.method.call.message=Can be replaced with ''{0}'' inspection.lambda.to.method.call.fix.family.name=Replace lambda expression with method call inspection.lambda.to.method.call.fix.name=Replace lambda expression with ''{0}''