mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
IDEA-163909 Inspection to replace computeIfAbsent returning a constant with putIfAbsent
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
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.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class ExcessiveLambdaUsageInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final ExcessiveLambdaInfo[] INFOS = {
|
||||
new ExcessiveLambdaInfo(CommonClassNames.JAVA_UTIL_MAP, "computeIfAbsent", "putIfAbsent", 1, false),
|
||||
new ExcessiveLambdaInfo(CommonClassNames.JAVA_UTIL_OPTIONAL, "orElseGet", "orElse", 0, true),
|
||||
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)
|
||||
};
|
||||
|
||||
@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;
|
||||
PsiElement gParent = parent.getParent();
|
||||
if (!(gParent instanceof PsiMethodCallExpression)) 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 (ExcessiveLambdaInfo info : INFOS) {
|
||||
if(info.isApplicable((PsiMethodCallExpression)gParent, lambda)) {
|
||||
holder.registerProblem(lambda, InspectionsBundle.message("inspection.excessive.lambda.message"),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
new TextRange(0, expr.getTextOffset() - lambda.getTextOffset()), new RemoveExcessiveLambdaFix(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class RemoveExcessiveLambdaFix implements LocalQuickFix {
|
||||
private final ExcessiveLambdaInfo myInfo;
|
||||
|
||||
public RemoveExcessiveLambdaFix(ExcessiveLambdaInfo info) {
|
||||
myInfo = info;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.excessive.lambda.fix.name", myInfo.myConstantMethod);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
call.getMethodExpression().handleElementRename(myInfo.myConstantMethod);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
ct.replaceAndRestoreComments(lambda, factory.createExpressionFromText(ct.text(body), lambda));
|
||||
}
|
||||
}
|
||||
|
||||
static class ExcessiveLambdaInfo {
|
||||
final String myClass;
|
||||
final String myLambdaMethod;
|
||||
final String myConstantMethod;
|
||||
final int myParameterIndex;
|
||||
final boolean myCanUseReturnValue;
|
||||
|
||||
ExcessiveLambdaInfo(String aClass, String lambdaMethod, String constantMethod, int index, boolean canUseReturnValue) {
|
||||
myClass = aClass;
|
||||
myLambdaMethod = lambdaMethod;
|
||||
myConstantMethod = constantMethod;
|
||||
myParameterIndex = index;
|
||||
myCanUseReturnValue = canUseReturnValue;
|
||||
}
|
||||
|
||||
boolean isApplicable(PsiMethodCallExpression call, PsiLambdaExpression lambda) {
|
||||
if(!myLambdaMethod.equals(call.getMethodExpression().getReferenceName())) return false;
|
||||
if(!myCanUseReturnValue && !(call.getParent() instanceof PsiExpressionStatement)) return false;
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if(args.length <= myParameterIndex || args[myParameterIndex] != 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());
|
||||
return fnClass != null && LambdaUtil.getFunction(fnClass) != null &&
|
||||
InheritanceUtil.isInheritor(method.getContainingClass(), false, myClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Use 'putIfAbsent' method without lambda" "true"
|
||||
import java.util.Map;
|
||||
|
||||
class Test {
|
||||
public void test(Map<String, String> map, String key) {
|
||||
/*comment in param*/
|
||||
/*comment in arrow*/
|
||||
map.putIfAbsent(key, (/*comment in parens*/"empty"));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Use 'or' method without lambda" "true"
|
||||
package com.google.common.base;
|
||||
|
||||
interface Supplier<T> {
|
||||
T supply();
|
||||
}
|
||||
|
||||
abstract class Optional<T> {
|
||||
abstract T or(T value);
|
||||
abstract T or(Supplier<? extends T> supplier);
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(Optional<String> opt) {
|
||||
System.out.println(opt.or(""));
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Use 'orElse' method without lambda" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
public String test(List<String> data) {
|
||||
return data.stream().filter(Objects::nonNull).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Use 'putIfAbsent' method without lambda" "true"
|
||||
import java.util.Map;
|
||||
|
||||
class Test {
|
||||
public void test(Map<String, String> map, String key) {
|
||||
map.computeIfAbsent(key, (/*comment in param*/k) /*comment in arrow*/<caret>-> (/*comment in parens*/"empty"));
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Use 'putIfAbsent' method without lambda" "false"
|
||||
import java.util.Map;
|
||||
|
||||
class Test {
|
||||
public void test(Map<String, String> map, String key) {
|
||||
map.computeIfAbsent(key, (/*comment in param*/k) /*comment in arrow*/<caret>-> (/*comment in parens*/"empty"+key));
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Use 'putIfAbsent' method without lambda" "false"
|
||||
import java.util.Map;
|
||||
|
||||
class Test {
|
||||
public String test(Map<String, String> map, String key) {
|
||||
return map.computeIfAbsent(key, k <caret>-> ("empty"));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Use 'or' method without lambda" "true"
|
||||
package com.google.common.base;
|
||||
|
||||
interface Supplier<T> {
|
||||
T supply();
|
||||
}
|
||||
|
||||
abstract class Optional<T> {
|
||||
abstract T or(T value);
|
||||
abstract T or(Supplier<? extends T> supplier);
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(Optional<String> opt) {
|
||||
System.out.println(opt.or(() <caret>-> ""));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Use 'or' method without lambda" "false"
|
||||
package com.google.common.base;
|
||||
|
||||
interface Supplier<T> {
|
||||
T supply();
|
||||
}
|
||||
|
||||
abstract class Optional<T> {
|
||||
abstract T or(T value);
|
||||
abstract T or(Supplier<? extends T> supplier);
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(Optional<Supplier<String>> opt) {
|
||||
System.out.println(opt.or(() <caret>-> ""));
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Use 'orElse' method without lambda" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
public String test(List<String> data) {
|
||||
return data.stream().filter(Objects::nonNull).findFirst().orElseGet((<caret>) -> null);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInspection.ExcessiveLambdaUsageInspection;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class ExcessiveLambdaUsageInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{
|
||||
new ExcessiveLambdaUsageInspection()
|
||||
};
|
||||
}
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage";
|
||||
}
|
||||
}
|
||||
@@ -738,4 +738,8 @@ inspection.tool.window.dialog.title=Inspection Tool Window
|
||||
inspection.tool.window.dialog.no.options=Inspection ''{0}'' has no configurable options
|
||||
inspection.tool.window.inspection.dialog.title=Inspection ''{0}'' options
|
||||
nullable.stuff.inspection.navigate.null.argument.usages.fix.family.name=Navigate to 'null' argument usages
|
||||
nullable.stuff.inspection.navigate.null.argument.usages.view.name=''null'' argument usages for parameter {0}
|
||||
nullable.stuff.inspection.navigate.null.argument.usages.view.name=''null'' argument usages for parameter {0}
|
||||
|
||||
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
|
||||
|
||||
+1
@@ -699,6 +699,7 @@ public class ExpressionUtils {
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean isSimpleExpression(@Nullable PsiExpression expression) {
|
||||
expression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (expression instanceof PsiLiteralExpression ||
|
||||
expression instanceof PsiThisExpression ||
|
||||
expression instanceof PsiClassObjectAccessExpression) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection warns if trivial lambda expression is used where there's an alternative method which behaves the same way but
|
||||
accepts concrete value instead of lambda.
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
For example, <code>Optional.orElseGet(() -> null)</code> can be replaced with <code>Optional.orElse(null)</code>.
|
||||
</p>
|
||||
<small>New in 2017.1</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -840,6 +840,11 @@
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java18api.Java8MapApiInspection"
|
||||
displayName="Replace with single Map method"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="ExcessiveLambdaUsage"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.verbose.or.redundant.code.constructs" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.ExcessiveLambdaUsageInspection"
|
||||
displayName="Excessive lambda usage"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="SimplifyStreamApiCallChains"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"
|
||||
|
||||
Reference in New Issue
Block a user