IDEA-163062 [Java8][Feature] Function.identity() inspection

This commit is contained in:
Tagir Valeev
2016-12-13 16:17:43 +07:00
parent 9f032daf1f
commit 215dabf2ae
13 changed files with 331 additions and 0 deletions
@@ -0,0 +1,17 @@
// "Replace lambda expression with 'Predicate.isEqual()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x -> x).filter(Predicate.isEqual(str))
.filter(str -> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,17 @@
// "Replace lambda expression with 'Function.identity()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(Function.identity()).filter(x -> Objects.equals(x, str))
.filter(str -> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,16 @@
// "Replace lambda expression with 'Pattern.asPredicate()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x -> x).filter(x -> Objects.equals(x, str))
.filter(pattern.asPredicate()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,16 @@
// "Replace lambda expression with 'Predicate.isEqual()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x -> x).filter(x <caret>-> Objects.equals(x, str))
.filter(str -> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,16 @@
// "Replace lambda expression with 'Function.identity()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x <caret>-> x).filter(x -> Objects.equals(x, str))
.filter(str -> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,9 @@
// "Replace lambda expression with 'Function.identity()'" "false"
import java.io.Serializable;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<Object, Object> fn = (Function<Object, Object> & Serializable) x <caret>-> x;
}
}
@@ -0,0 +1,16 @@
// "Replace lambda expression with 'Function.identity()'" "false"
import java.util.Objects;
import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x -> x).filter(x -> Objects.equals(x, str))
.filter(str -> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x <caret>-> x;
}
}
@@ -0,0 +1,16 @@
// "Replace lambda expression with 'Pattern.asPredicate()'" "true"
import java.util.Objects;
import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class Main {
private final Pattern pattern = Pattern.compile("[a-z]");
public <T extends CharSequence> void test() {
Object str = "def";
String xyz = Optional.of("xyz").map(x -> x).filter(x -> Objects.equals(x, str))
.filter(str <caret>-> pattern.matcher(str).find()).orElse("");
UnaryOperator<T> op = x -> x;
}
}
@@ -0,0 +1,36 @@
/*
* 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.LambdaCanBeMethodCallInspection;
import com.intellij.codeInspection.LocalInspectionTool;
import org.jetbrains.annotations.NotNull;
public class LambdaCanBeMethodCallInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new LambdaCanBeMethodCallInspection()};
}
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodCall";
}
}
@@ -745,6 +745,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.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}''
inspection.module.exports.package.to.itself.message=Module exports package to itself
inspection.module.exports.package.to.itself.only.message=Module exports package only to itself
exports.to.itself.delete.module.fix.name=Delete reference to module ''{0}''
@@ -0,0 +1,156 @@
/*
* 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.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/**
* @author Tagir Valeev
*/
public class LambdaCanBeMethodCallInspection extends BaseJavaBatchLocalInspectionTool {
@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) {
super.visitLambdaExpression(lambda);
PsiElement body = lambda.getBody();
if (body == null) return;
PsiType type = lambda.getFunctionalInterfaceType();
if (!(type instanceof PsiClassType)) return;
PsiElement parent = PsiUtil.skipParenthesizedExprUp(lambda.getParent());
if(parent instanceof PsiTypeCastExpression) {
PsiType castType = ((PsiTypeCastExpression)parent).getType();
if( castType instanceof PsiIntersectionType) {
PsiType[] conjuncts = ((PsiIntersectionType)castType).getConjuncts();
if(Arrays.stream(conjuncts).anyMatch(t -> t.equalsToText(CommonClassNames.JAVA_IO_SERIALIZABLE))) return;
}
}
PsiExpression expression = PsiUtil.skipParenthesizedExprDown(LambdaUtil.extractSingleExpressionFromBody(body));
if (expression == null) return;
PsiParameter[] parameters = lambda.getParameterList().getParameters();
if (parameters.length == 1 && ExpressionUtils.isReferenceTo(expression, parameters[0])) {
PsiClass aClass = ((PsiClassType)type).resolve();
if (aClass != null && CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION.equals(aClass.getQualifiedName())) {
PsiType[] typeParameters = ((PsiClassType)type).getParameters();
if (typeParameters.length == 2 && typeParameters[1].isAssignableFrom(typeParameters[0])) {
registerProblem(lambda, "Function.identity()", CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION + ".identity()");
}
}
}
if (parameters.length == 1 && expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
PsiClass aClass = ((PsiClassType)type).resolve();
if (aClass != null && CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE.equals(aClass.getQualifiedName())) {
handlePredicate(lambda, parameters, call);
}
}
}
private void handlePredicate(PsiLambdaExpression lambda, PsiParameter[] parameters, PsiMethodCallExpression call) {
if (MethodCallUtils.isCallToStaticMethod(call, "java.util.Objects", "equals", 2)) {
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length == 2) {
PsiExpression comparedWith;
if (ExpressionUtils.isReferenceTo(args[0], parameters[0])) {
comparedWith = args[1];
}
else if (ExpressionUtils.isReferenceTo(args[1], parameters[0])) {
comparedWith = args[0];
}
else {
return;
}
if (LambdaCanBeMethodReferenceInspection.checkQualifier(comparedWith)) {
registerProblem(lambda, "Predicate.isEqual()",
CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE + ".isEqual(" + comparedWith.getText() + ")");
}
}
}
if (MethodCallUtils.isCallToMethod(call, "java.util.regex.Matcher", PsiType.BOOLEAN, "find")) {
PsiExpression matcher = call.getMethodExpression().getQualifierExpression();
if (matcher instanceof PsiMethodCallExpression) {
PsiMethodCallExpression matcherCall = (PsiMethodCallExpression)matcher;
if (MethodCallUtils.isCallToMethod(matcherCall, "java.util.regex.Pattern", null, "matcher",
new PsiType[]{null})) {
PsiExpression[] matcherArgs = matcherCall.getArgumentList().getExpressions();
if (matcherArgs.length == 1 && ExpressionUtils.isReferenceTo(matcherArgs[0], parameters[0])) {
PsiExpression pattern = matcherCall.getMethodExpression().getQualifierExpression();
if (pattern != null && LambdaCanBeMethodReferenceInspection.checkQualifier(pattern)) {
registerProblem(lambda, "Pattern.asPredicate()",
ParenthesesUtils.getText(pattern, ParenthesesUtils.POSTFIX_PRECEDENCE) + ".asPredicate()");
}
}
}
}
}
}
private void registerProblem(PsiLambdaExpression lambda, String displayReplacement, String replacement) {
holder.registerProblem(lambda, InspectionsBundle.message("inspection.lambda.to.method.call.message", displayReplacement),
new ReplaceWithFunctionCallFix(replacement, displayReplacement));
}
};
}
static final class ReplaceWithFunctionCallFix implements LocalQuickFix {
private final String myDisplayReplacement;
private final String myReplacement;
public ReplaceWithFunctionCallFix(String replacement, String displayReplacement) {
myReplacement = replacement;
myDisplayReplacement = displayReplacement;
}
@Nls
@NotNull
@Override
public String getName() {
return InspectionsBundle.message("inspection.lambda.to.method.call.fix.name", myDisplayReplacement);
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.lambda.to.method.call.fix.family.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
if (!(element instanceof PsiLambdaExpression)) return;
PsiElement result = new CommentTracker().replaceAndRestoreComments(element, myReplacement);
CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result));
}
}
}
@@ -0,0 +1,9 @@
<html>
<body>
Reports lambda expressions which could be replaced with JDK method call.
<!-- tooltip end -->
<p>For example, the expression <code>x -&gt; x</code> of type <code>Function&lt;T, T&gt;</code>
could be replaced with <code>Function.identity()</code> call.</p>
<small>New in 2017.1</small>
</body>
</html>
+3
View File
@@ -780,6 +780,9 @@
<localInspection groupPath="Java" language="JAVA" shortName="Convert2MethodRef" displayName="Lambda can be replaced with method reference"
groupKey="group.names.language.level.specific.issues.and.migration.aids" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="LambdaCanBeMethodCall" displayName="Lambda can be replaced with method call"
groupKey="group.names.language.level.specific.issues.and.migration.aids" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="INFORMATION"
implementationClass="com.intellij.codeInspection.LambdaCanBeMethodCallInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="InvalidComparatorMethodReference" displayName="Invalid method reference used for Comparator"
groupKey="group.names.probable.bugs" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.InvalidComparatorMethodReferenceInspection"/>