From 7de306e76bf9705ba40e5e97fadcc61483b84cf2 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 13 Sep 2016 11:35:39 +0200 Subject: [PATCH] IDEA-161034 Code completion should be provided for static method references too --- ...unctionalExpressionCompletionProvider.java | 85 +++++++++++++------ .../smartType/StaticMethodReference-out.java | 11 +++ .../smartType/StaticMethodReference.java | 11 +++ .../completion/SmartType18CompletionTest.java | 2 + 4 files changed, 82 insertions(+), 27 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReference-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReference.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java index 26e289ee70f6..c84c6892d833 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java @@ -37,6 +37,7 @@ import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -141,6 +142,10 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider consumer = eachReturnType -> { PsiClass psiClass = PsiUtil.resolveClassInType(eachReturnType); if (psiClass == null || psiClass instanceof PsiTypeParameter) return; @@ -178,6 +183,33 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider context.getDocument().insertString(context.getStartOffset(), "this::")) + .withTypeText(functionalInterfaceType.getPresentableText()) + .withIcon(AllIcons.Nodes.MethodReference) + .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + } + + @NotNull + private static LookupElement createMethodRefOnClass(PsiType functionalInterfaceType, PsiMethod psiMethod, PsiClass qualifierClass) { + String presentableText = qualifierClass.getName() + "::" + psiMethod.getName(); + return LookupElementBuilder + .create(psiMethod) + .withLookupString(presentableText) + .withPresentableText(presentableText) + .withInsertHandler((context, item) -> { + context.getDocument().insertString(context.getStartOffset(), "::"); + JavaCompletionUtil.insertClassReference(qualifierClass, context.getFile(), context.getStartOffset()); + }) + .withTypeText(functionalInterfaceType.getPresentableText()) + .withIcon(AllIcons.Nodes.MethodReference) + .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + } + private static List collectThisVariants(PsiType functionalInterfaceType, PsiParameter[] params, PsiElement originalPosition, @@ -186,17 +218,27 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider context.getDocument().insertString(context.getStartOffset(), "this::")) - .withTypeText(functionalInterfaceType.getPresentableText()) - .withIcon(AllIcons.Nodes.MethodReference) - .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); - result.add(methodRefLookupElement); + result.add(createMethodRefOnThis(functionalInterfaceType, psiMethod)); + } + } + } + return result; + } + + private static List collectStaticVariants(PsiType functionalInterfaceType, + PsiParameter[] params, + PsiElement originalPosition, + PsiSubstitutor substitutor, PsiType expectedReturnType) { + List result = new ArrayList<>(); + for (PsiClass psiClass : JBIterable.generate(PsiTreeUtil.getParentOfType(originalPosition, PsiClass.class), PsiClass::getContainingClass)) { + for (PsiMethod psiMethod : psiClass.getMethods()) { + if (psiMethod.hasModifierProperty(PsiModifier.STATIC) && + hasAppropriateReturnType(expectedReturnType, psiMethod) && + areParameterTypesAppropriate(psiMethod, params, substitutor, 0)) { + result.add(createMethodRefOnClass(functionalInterfaceType, psiMethod, psiClass)); } } } @@ -215,24 +257,14 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider visited = new HashSet<>(); for (PsiMethod psiMethod : paramClass.getAllMethods()) { - final PsiType returnType = psiMethod.getReturnType(); PsiClass containingClass = psiMethod.getContainingClass(); PsiClass qualifierClass = containingClass != null ? containingClass : paramClass; if (visited.add(psiMethod.getName()) && - isInstanceMethodWithAppropriateReturnType(expectedReturnType, psiMethod, returnType) && + !psiMethod.hasModifierProperty(PsiModifier.STATIC) && + hasAppropriateReturnType(expectedReturnType, psiMethod) && areParameterTypesAppropriate(psiMethod, params, substitutor, 1) && JavaResolveUtil.isAccessible(psiMethod, null, psiMethod.getModifierList(), originalPosition, null, null)) { - LookupElement methodRefLookupElement = LookupElementBuilder - .create(psiMethod) - .withPresentableText(qualifierClass.getName() + "::" + psiMethod.getName()) - .withInsertHandler((context, item) -> { - int startOffset = context.getStartOffset(); - context.getDocument().insertString(startOffset, "::"); - JavaCompletionUtil.insertClassReference(qualifierClass, context.getFile(), startOffset); - }) - .withTypeText(functionalInterfaceType.getPresentableText()) - .withIcon(AllIcons.Nodes.MethodReference) - .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + LookupElement methodRefLookupElement = createMethodRefOnClass(functionalInterfaceType, psiMethod, qualifierClass); if (prioritize && containingClass == paramClass) { methodRefLookupElement = PrioritizedLookupElement.withPriority(methodRefLookupElement, 1); } @@ -243,10 +275,9 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider s) { + s.map(Example::method2) + } + + private static String method2(String s) { + return s; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReference.java b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReference.java new file mode 100644 index 000000000000..a74a893eb1c3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReference.java @@ -0,0 +1,11 @@ +import java.util.stream.Stream; + +class Example { + private void example(Stream s) { + s.map(meth) + } + + private static String method2(String s) { + return s; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java index b5c66455f18a..432686032e6c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -144,6 +144,8 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { doTest(true); } + public void testStaticMethodReference() { doTest(false); } + public void testMethodReferenceOnAncestor() { doTest(true); } public void testNoLambdaSuggestionForGenericsFunctionalInterfaceMethod() throws Exception {