From 76fe4475b11df6f26e16215925abedfb16accfd1 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 30 Aug 2012 18:44:46 +0400 Subject: [PATCH 1/4] lambda smart completion: choose functional interface method instead of containing method (IDEA-90809) --- .../codeInsight/ExpectedTypesProvider.java | 4 +- .../smartType/ExpectedReturnType-out.java | 26 +++++++++ .../smartType/ExpectedReturnType.java | 26 +++++++++ .../completion/SmartType18CompletionTest.java | 56 +++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index 18e8fd2feb13..f2b88359c673 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -331,7 +331,9 @@ public class ExpectedTypesProvider { } @Override public void visitReturnStatement(PsiReturnStatement statement) { - final PsiMethod scopeMethod = PsiTreeUtil.getParentOfType(statement, PsiMethod.class); + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class); + final PsiMethod interfaceMethod = lambdaExpression != null ? LambdaUtil.getFunctionalInterfaceMethod(lambdaExpression.getFunctionalInterfaceType()) : null; + final PsiMethod scopeMethod = interfaceMethod != null ? interfaceMethod : PsiTreeUtil.getParentOfType(statement, PsiMethod.class); if (scopeMethod != null) { PsiType type = scopeMethod.getReturnType(); if (type != null) { diff --git a/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType-out.java b/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType-out.java new file mode 100644 index 000000000000..c96328d6c192 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType-out.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2012 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. + */ +interface I { + boolean foo(String s); +} +public class Test { + public int m() { + I i = s -> { + return s.contains(); + }; + return 0; + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType.java b/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType.java new file mode 100644 index 000000000000..81f759d67340 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/ExpectedReturnType.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2012 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. + */ +interface I { + boolean foo(String s); +} +public class Test { + public int m() { + I i = s -> { + return s. + }; + return 0; + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java new file mode 100644 index 000000000000..9687216e4a15 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2012 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.completion; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.lookup.Lookup; +import com.intellij.codeInsight.lookup.LookupElementPresentation; +import com.intellij.testFramework.LightProjectDescriptor; +import org.jetbrains.annotations.NotNull; + +public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { + @Override + protected String getBasePath() { + return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/smartType/"; + } + + @Override + protected void complete() { + myItems = myFixture.complete(CompletionType.SMART); + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_LATEST; + } + + + public void testExpectedReturnType() { + doTest(); + } + + private void doTest() { + configureByFile("/" + getTestName(false) + ".java"); + assertNotNull(myItems); + assertTrue(myItems.length > 0); + final Lookup lookup = getLookup(); + if (lookup != null) { + selectItem(lookup.getCurrentItem(), Lookup.NORMAL_SELECT_CHAR); + } + checkResultByFile("/" + getTestName(false) + "-out.java"); + } +} From 626122ba82e5ff27ef06a5f1d536d5b05275e448 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 30 Aug 2012 19:39:19 +0400 Subject: [PATCH 2/4] lambda -> anonymous: qualify this --- .../types/ReplaceLambdaWithAnonymousIntention.java | 11 ++++++++++- .../siyeh/ipp/types/lambda2anonymous/QualifyThis.java | 6 ++++++ .../ipp/types/lambda2anonymous/QualifyThis_after.java | 11 +++++++++++ .../ReplaceLambdaWithAnonymousIntentionTest.java | 4 ++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis_after.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntention.java index 5a5dc8258841..161f4d8e6e6c 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntention.java @@ -15,6 +15,7 @@ */ package com.siyeh.ipp.types; +import com.intellij.codeInsight.ChangeContextUtil; import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.generation.GenerateMembersUtil; import com.intellij.codeInsight.generation.OverrideImplementUtil; @@ -25,6 +26,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; @@ -62,7 +64,12 @@ public class ReplaceLambdaWithAnonymousIntention extends Intention { if (blockText == null) return; final PsiElementFactory psiElementFactory = JavaPsiFacade.getElementFactory(element.getProject()); + PsiCodeBlock blockFromText = psiElementFactory.createCodeBlockFromText(blockText, lambdaExpression); + ChangeContextUtil.encodeContextInfo(blockFromText, true); PsiNewExpression newExpression = (PsiNewExpression)psiElementFactory.createExpressionFromText("new " + functionalInterfaceType.getCanonicalText() + "(){}", lambdaExpression); + PsiClass thisClass = PsiTreeUtil.getParentOfType(lambdaExpression, PsiClass.class, true); + ChangeContextUtil.decodeContextInfo(blockFromText, thisClass, RefactoringUtil.createThisExpression(lambdaExpression.getManager(), thisClass)); + blockFromText = psiElementFactory.createCodeBlockFromText(blockFromText.getText(), null); newExpression = (PsiNewExpression)lambdaExpression.replace(newExpression); final PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass(); @@ -80,7 +87,8 @@ public class ReplaceLambdaWithAnonymousIntention extends Intention { } PsiCodeBlock codeBlock = member.getBody(); LOG.assertTrue(codeBlock != null); - codeBlock = (PsiCodeBlock)codeBlock.replace(psiElementFactory.createCodeBlockFromText(blockText, null)); + + codeBlock = (PsiCodeBlock)codeBlock.replace(blockFromText); final Set vars2BeFinal = new HashSet(); codeBlock.accept(new JavaRecursiveElementWalkingVisitor() { @Override @@ -99,6 +107,7 @@ public class ReplaceLambdaWithAnonymousIntention extends Intention { for (PsiVariable var : vars2BeFinal) { PsiUtil.setModifierProperty(var, PsiModifier.FINAL, true); } + GenerateMembersUtil.positionCaret(editor, member, true); } } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis.java new file mode 100644 index 000000000000..0e8d12f3f03b --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis.java @@ -0,0 +1,6 @@ +class Test { + public void m() { + Runnable r = () -> {System.out.println(this);}; + r.run(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis_after.java new file mode 100644 index 000000000000..edc3db64e3c3 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/QualifyThis_after.java @@ -0,0 +1,11 @@ +class Test { + public void m() { + Runnable r = new Runnable() { + @Override + public void run() { + System.out.println(Test.this); + } + }; + r.run(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntentionTest.java index cbb85e819988..1196dec5ec95 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/ReplaceLambdaWithAnonymousIntentionTest.java @@ -55,6 +55,10 @@ public class ReplaceLambdaWithAnonymousIntentionTest extends IPPTestCase { doTest(); } + public void testQualifyThis() { + doTest(); + } + @Override protected String getIntentionName() { return IntentionPowerPackBundle.message("replace.lambda.with.anonymous.intention.name"); From 4672022e91fbce9c0b226f2048b9a99f2727177a Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 30 Aug 2012 19:43:50 +0400 Subject: [PATCH 3/4] lambda: hide "infer param types" when no params available (IDEA-90821) --- .../siyeh/ipp/types/InferLambdaParameterTypeIntention.java | 4 +++- .../siyeh/ipp/types/inferLambdaParameterType/NoParams.java | 7 +++++++ .../ipp/types/InferLambdaParameterTypeIntentionTest.java | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/types/inferLambdaParameterType/NoParams.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/InferLambdaParameterTypeIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/InferLambdaParameterTypeIntention.java index 217dc0442adf..38362a9798dc 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/InferLambdaParameterTypeIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/InferLambdaParameterTypeIntention.java @@ -84,7 +84,9 @@ public class InferLambdaParameterTypeIntention extends Intention { public boolean satisfiedBy(PsiElement element) { final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class); if (lambdaExpression != null) { - for (PsiParameter parameter : lambdaExpression.getParameterList().getParameters()) { + PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters(); + if (parameters.length == 0) return false; + for (PsiParameter parameter : parameters) { if (parameter.getTypeElement() != null) { return false; } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/inferLambdaParameterType/NoParams.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/inferLambdaParameterType/NoParams.java new file mode 100644 index 000000000000..4639ef575274 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/inferLambdaParameterType/NoParams.java @@ -0,0 +1,7 @@ +class Test2 { + static void bar(Runnable r){} + + { + bar(() -> {}); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/InferLambdaParameterTypeIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/InferLambdaParameterTypeIntentionTest.java index f2921a41b4ab..94bf41b59cb7 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/InferLambdaParameterTypeIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/InferLambdaParameterTypeIntentionTest.java @@ -39,6 +39,10 @@ public class InferLambdaParameterTypeIntentionTest extends IPPTestCase { assertIntentionNotAvailable("Expand lambda to (Object x) -> {...}"); } + public void testNoParams() throws Exception { + assertIntentionNotAvailable("Expand lambda to () -> {...}"); + } + @Override protected String getIntentionName() { return ""; From bb0b0c008a432f1c44d464916b28a3e4323ab23a Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 30 Aug 2012 20:06:36 +0400 Subject: [PATCH 4/4] convert anonym class to onle-line lambda if possible (IDEA-90820) --- .../AnonymousCanBeLambdaInspection.java | 8 ++++++++ .../quickFix/anonymous2lambda/afterComparable.java | 4 +--- .../quickFix/anonymous2lambda/afterFormalTypes.java | 4 +--- .../anonymous2lambda/afterMultilineComparable.java | 9 +++++++++ .../anonymous2lambda/beforeMultilineComparable.java | 12 ++++++++++++ 5 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterMultilineComparable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/beforeMultilineComparable.java diff --git a/java/java-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index fe2884b6d912..9b40f1f37fa9 100644 --- a/java/java-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -145,6 +145,14 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaLocalInspectionTool buf.append("->"); final PsiCodeBlock body = method.getBody(); LOG.assertTrue(body != null); + final PsiStatement[] statements = body.getStatements(); + if (statements.length == 1 && statements[0] instanceof PsiReturnStatement) { + PsiExpression value = ((PsiReturnStatement)statements[0]).getReturnValue(); + if (value != null) { + buf.append(value.getText()); + return buf.toString(); + } + } buf.append(body.getText()); return buf.toString(); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterComparable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterComparable.java index c4186c9efb5e..f787f50af2f3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterComparable.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterComparable.java @@ -1,8 +1,6 @@ // "Replace with lambda" "true" class Test { { - Comparable c = o -> { - return 0; - }; + Comparable c = o -> 0; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterFormalTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterFormalTypes.java index d6372aa4b089..b39fca9cb111 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterFormalTypes.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterFormalTypes.java @@ -9,8 +9,6 @@ class Test2 { static I bar(I i){return i;} { - bar((List list) -> { - return null; //To change body of implemented methods use File | Settings | File Templates. - }); + bar((List list) -> null); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterMultilineComparable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterMultilineComparable.java new file mode 100644 index 000000000000..4feda0438041 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/afterMultilineComparable.java @@ -0,0 +1,9 @@ +// "Replace with lambda" "true" +class Test { + { + Comparable c = o -> { + System.out.println(); + return 0; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/beforeMultilineComparable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/beforeMultilineComparable.java new file mode 100644 index 000000000000..8a2ed032f70a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambda/beforeMultilineComparable.java @@ -0,0 +1,12 @@ +// "Replace with lambda" "true" +class Test { + { + Comparable c = new Comparable() { + @Override + public int compareTo(String o) { + System.out.println(); + return 0; + } + }; + } +} \ No newline at end of file