diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java index 4c425ed5d9d9..92566655a237 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java @@ -22,6 +22,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.impl.PsiSubstitutorImpl; import com.intellij.psi.impl.source.PsiClassReferenceType; @@ -116,7 +118,7 @@ class PseudoLambdaReplaceTemplate { } else if (!(returnType instanceof PsiArrayType)) { return null; } - return validate(parameterTypes, returnType, method); + return validate(parameterTypes, returnType, null, method); } @Nullable @@ -136,7 +138,9 @@ class PseudoLambdaReplaceTemplate { if (argumentTypes.length != expectedParameters.length) { return null; } - return validate(argumentTypes, methodReturnType, expression); + final JavaResolveResult result = expression.getMethodExpression().advancedResolve(false); + final PsiSubstitutor methodSubstitutor = result.getSubstitutor(); + return validate(argumentTypes, methodReturnType, methodSubstitutor, expression); } public String getStreamApiMethodName() { @@ -151,7 +155,10 @@ class PseudoLambdaReplaceTemplate { return myAcceptDefaultValue; } - private ValidationInfo validate(final PsiType[] arguments, final PsiType methodReturnType, final PsiElement context) { + private ValidationInfo validate(final PsiType[] arguments, + final PsiType methodReturnType, + final @Nullable PsiSubstitutor methodSubstitutor, + final PsiElement context) { int lambdaPosition = -1; int defaultValuePosition = -1; int iterablePosition = -1; @@ -168,7 +175,7 @@ class PseudoLambdaReplaceTemplate { for (int i = 0; i < arguments.length; i++) { PsiType type = arguments[i]; - if (isFunction(type, methodReturnType, context)) { + if (isFunction(type, methodReturnType, methodSubstitutor, context)) { if (lambdaPosition == -1) { lambdaPosition = i; continue; @@ -207,7 +214,7 @@ class PseudoLambdaReplaceTemplate { return new ValidationInfo(lambdaPosition, iterablePosition, defaultValuePosition); } - private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiElement context) { + private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiSubstitutor methodSubstitutor, PsiElement context) { if (type instanceof PsiMethodReferenceType) { final PsiMethodReferenceExpression expression = ((PsiMethodReferenceType)type).getExpression(); final PsiMethod resolvedMethod = (PsiMethod)expression.resolve(); @@ -219,20 +226,21 @@ class PseudoLambdaReplaceTemplate { return false; } final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(expression); - return isSuitableLambdaRole(returnType, baseMethodReturnType, context); + return isSuitableLambdaRole(returnType, baseMethodReturnType, methodSubstitutor, context); } else if (type instanceof PsiLambdaExpressionType) { final PsiLambdaExpression expression = ((PsiLambdaExpressionType)type).getExpression(); final PsiType psiType = LambdaUtil.getFunctionalInterfaceReturnType(expression.getFunctionalInterfaceType()); - return isSuitableLambdaRole(psiType, baseMethodReturnType, context); - } else if (isSuitableFunctionalType(type, baseMethodReturnType, context)) { + return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); + } else if (isSuitableFunctionalType(type, baseMethodReturnType, methodSubstitutor, context)) { return true; } return isJavaLangClassType(type) && myLambdaRole == LambdaRole.PREDICATE; } - private boolean isSuitableLambdaRole(final PsiType lambdaReturnType, - final PsiType baseMethodReturnType, - final PsiElement context) { + private boolean isSuitableLambdaRole(PsiType lambdaReturnType, + PsiType baseMethodReturnType, + PsiSubstitutor methodSubstitutor, + PsiElement context) { if (lambdaReturnType == null) { return false; } @@ -244,6 +252,9 @@ class PseudoLambdaReplaceTemplate { } else { LOG.assertTrue(myLambdaRole == LambdaRole.FUNCTION); + if (methodSubstitutor != null) { + lambdaReturnType = methodSubstitutor.substitute(lambdaReturnType); + } if (baseMethodReturnType instanceof PsiClassType) { final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)baseMethodReturnType).resolveGenerics(); final Map substitutionMap = resolveResult.getSubstitutor().getSubstitutionMap(); @@ -274,19 +285,29 @@ class PseudoLambdaReplaceTemplate { return false; } - private boolean isSuitableFunctionalType(final PsiType type, final PsiType baseMethodReturnType, final PsiElement context) { + private boolean isSuitableFunctionalType(final PsiType type, + final PsiType baseMethodReturnType, + final @Nullable PsiSubstitutor methodSubstitutor, + final PsiElement context) { if (type instanceof PsiClassType) { PsiClass targetClass = ((PsiClassType)type).resolve(); if (targetClass != null) { + //TODO fuu if (targetClass instanceof PsiAnonymousClass) { targetClass = ((PsiAnonymousClass)targetClass).getBaseClassType().resolve(); if (targetClass == null) { return false; } + } else { + if (!LambdaUtil.isFunctionalClass(targetClass)) { + return false; + } + return isSuitableLambdaRole(LambdaUtil.getFunctionalInterfaceReturnType(type), baseMethodReturnType, methodSubstitutor, context); } if (!LambdaUtil.isFunctionalClass(targetClass)) { return false; } + //TODO fuu PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(targetClass); final PsiMethod[] methods = ((PsiClassType)type).resolve().findMethodsByName(method.getName(), false); if (methods.length != 1) { @@ -315,7 +336,7 @@ class PseudoLambdaReplaceTemplate { } else { psiType = method.getReturnType(); } - return isSuitableLambdaRole(psiType, baseMethodReturnType, context); + return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); } return false; } else { @@ -340,17 +361,19 @@ class PseudoLambdaReplaceTemplate { int lambdaIndex = validationInfo.getLambdaPosition(); final PsiExpression[] expressions = expression.getArgumentList().getExpressions(); + final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()]; + final String pipelineHead = createPipelineHeadText(iterableExpression, force); + if (pipelineHead == null) { + return expression; + } + PsiExpression lambdaExpression = expressions[lambdaIndex]; if (!force) { lambdaExpression = convertClassTypeExpression(lambdaExpression); - lambdaExpression = convertToJavaLambda(lambdaExpression, myStreamApiMethodName); + lambdaExpression = convertToJavaLambda(lambdaExpression); } LOG.assertTrue(lambdaExpression != null); - final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()]; - final String pipelineHead = createPipelineHeadText(iterableExpression, lambdaExpression, force); - - final String lambdaExpressionText; final String elementText; if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(myStreamApiMethodName)) { @@ -454,10 +477,7 @@ class PseudoLambdaReplaceTemplate { return null; } - private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) { - if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) { - streamApiMethodName = StreamApiConstants.FILTER; - } + private static PsiExpression convertToJavaLambda(PsiExpression expression) { if (expression instanceof PsiMethodReferenceExpression) { return expression; } @@ -473,72 +493,19 @@ class PseudoLambdaReplaceTemplate { if (!(type instanceof PsiClassType)) { return null; } - final PsiClass lambdaClass = ((PsiClassType)type).resolve(); + final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); + final PsiClass lambdaClass = result.getElement(); if (lambdaClass == null) { return null; } - final String methodName = lambdaClass.getMethods()[0].getName(); - if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) { - return expression; - } - else { - return JavaPsiFacade.getElementFactory(expression.getProject()) - .createExpressionFromText(expression.getText() + "::" + methodName, null); - } + final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(lambdaClass); + LOG.assertTrue(functionalInterfaceMethod != null); + final String methodName = functionalInterfaceMethod.getName(); + return JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::" + methodName, null); } return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true); } - @NotNull - private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) { - final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project) - .findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project))); - LOG.assertTrue(javaUtilStream != null); - final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false); - LOG.assertTrue(methods.length == 1); - final PsiMethod method = methods[0]; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - LOG.assertTrue(parameters.length == 1); - final PsiType type = parameters[0].getType(); - LOG.assertTrue(type instanceof PsiClassType); - final PsiClass resolved = ((PsiClassType)type).resolve(); - LOG.assertTrue(resolved != null); - return resolved; - } - - private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) { - final PsiType currentReturnType = method.getReturnType(); - if (!(currentReturnType instanceof PsiClassType)) { - LOG.error("pseudo-lambda return type must be class " + currentReturnType); - return true; - } - final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve(); - if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) { - return true; - } - final PsiCodeBlock body = method.getBody(); - Collection returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class); - returnStatements = ContainerUtil.filter(returnStatements, new Condition() { - @Override - public boolean value(PsiReturnStatement statement) { - return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method; - } - }); - if (returnStatements.size() != 1) { - return false; - } - final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements); - assert returnStatement != null; - final PsiExpression returnValue = returnStatement.getReturnValue(); - if (returnValue instanceof PsiNewExpression) { - convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass); - return true; - } - else { - return false; - } - } - @NotNull private static PsiExpression convertClassTypeExpression(PsiExpression expression) { final PsiType type = expression.getType(); @@ -552,7 +519,8 @@ class PseudoLambdaReplaceTemplate { return expression; } - private static String createPipelineHeadText(PsiExpression collectionExpression, PsiExpression lambdaExpression, boolean force) { + private static String createPipelineHeadText(@NotNull PsiExpression collectionExpression, + boolean force) { if (collectionExpression instanceof PsiNewExpression) { final PsiDiamondType.DiamondInferenceResult diamondResolveResult = PsiDiamondTypeImpl.resolveInferredTypesNoCheck((PsiNewExpression)collectionExpression, collectionExpression); diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java index 82652edb89d4..46d0c8963f26 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java @@ -6,13 +6,13 @@ import java.util.Collections; class c { void m() { - Collections.emptyList().stream().allMatch(getPredicate(100)); + Collections.emptyList().stream().allMatch(getPredicate(100)::apply); } - public java.util.function.Predicate getPredicate(final int param) { - return new java.util.function.Predicate() { + public Predicate getPredicate(final int param) { + return new Predicate() { @Override - public boolean test(String input) { + public boolean apply(String input) { System.out.println("lambda param " + param); return false; } diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java index 452f94d0f295..a5a23c03188d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { + Collections.emptyList().stream().map(input -> { Collectors c; ArrayList l; System.out.println(input); diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java index cc8ef8ab6ddb..a738f169647d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java @@ -13,7 +13,7 @@ class c { System.out.println(input); //do something int i = 1; - return input; + return input.intern(); } }); } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java index bf6d86ece3a8..56aa7a1c44a9 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java @@ -12,6 +12,6 @@ class c { System.out.println(input); //do something int i = 1; - return input; + return input.intern(); }).collect(Collectors.toList()); } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java index addd4facbcf6..1f114dcb0fc0 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java @@ -1,17 +1,21 @@ import com.google.common.collect.Iterables; import com.google.common.base.Function; +import java.lang.Iterable; import java.lang.String; +import java.util.ArrayList; import java.util.Collections; class c { void m() { - Iterables.transform(new ArrayList<>(), getFunction()) + Iterable trnsfrmd = Iterables.transform(new ArrayList<>(), getFunction()); } Function getFunction() { return new Function() { - + String apply(String s) { + return s.toString().toLowerCase().replace('c', 'h'); + } } } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java index 452f94d0f295..52ae999287e4 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java @@ -1,6 +1,7 @@ import com.google.common.collect.Iterables; import com.google.common.base.Function; +import java.lang.Iterable; import java.lang.String; import java.util.ArrayList; import java.util.Collections; @@ -8,13 +9,14 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { - Collectors c; - ArrayList l; - System.out.println(input); - //do something - int i = 1; - return input; - }).collect(Collectors.toList()) + Iterable trnsfrmd = new ArrayList().stream().map(getFunction()::apply).collect(Collectors.toList()); + } + + Function getFunction() { + return new Function() { + String apply(String s) { + return s.toString().toLowerCase().replace('c', 'h'); + } + } } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java index 452f94d0f295..a5a23c03188d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { + Collections.emptyList().stream().map(input -> { Collectors c; ArrayList l; System.out.println(input); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java index 098f85b1561f..05d97edea504 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java @@ -96,11 +96,11 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur doTest(); } - public void _testTransformLambda() { + public void testTransformLambda() { doTest(); } - public void _testTransformMethodCalling() { + public void testTransformMethodCalling() { doTest(); }