diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index c9b6dd4b0185..cdf6251b819c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -289,13 +289,8 @@ public class JavaCompletionUtil { private static Set processJavaQualifiedReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter, JavaCompletionProcessor.Options options, final PrefixMatcher matcher, CompletionParameters parameters) { - final Set set = new LinkedHashSet(); - final Condition nameCondition = new Condition() { - @Override - public boolean value(String s) { - return matcher.prefixMatches(s); - } - }; + final Set set = new LinkedHashSet<>(); + final Condition nameCondition = matcher::prefixMatches; PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText()); @@ -322,9 +317,9 @@ public class JavaCompletionUtil { PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifierType); final boolean honorExcludes = qualifierClass == null || !isInExcludedPackage(qualifierClass, false); - final Set mentioned = new THashSet(); + final Set mentioned = new THashSet<>(); for (CompletionElement completionElement : processor.getResults()) { - for (LookupElement item : createLookupElements(completionElement, javaReference)) { + for (LookupElement item : createLookupElements(completionElement, javaReference, processor)) { item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType); final Object o = item.getObject(); if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) { @@ -528,7 +523,7 @@ public class JavaCompletionUtil { return false; } - static List createLookupElements(CompletionElement completionElement, PsiJavaReference reference) { + static List createLookupElements(CompletionElement completionElement, PsiJavaReference reference, JavaCompletionProcessor processor) { Object completion = completionElement.getElement(); assert !(completion instanceof LookupElement); @@ -556,7 +551,16 @@ public class JavaCompletionUtil { return Collections.singletonList(JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true).setSubstitutor(substitutor)); } if (completion instanceof PsiMethod) { - return Collections.singletonList(new JavaMethodCallElement((PsiMethod)completion).setQualifierSubstitutor(substitutor)); + PsiMethod method = (PsiMethod)completion; + JavaMethodCallElement item = new JavaMethodCallElement(method).setQualifierSubstitutor(substitutor); + if (processor.shouldQualifyMethodCall(method)) { + PsiClass containingClass = method.getContainingClass(); + String className = containingClass == null ? null : containingClass.getName(); + if (className != null) { + item.setForcedQualifier(className + (method.hasModifierProperty(PsiModifier.STATIC) ? "." : ".this.")); + } + } + return Collections.singletonList(item); } if (completion instanceof PsiVariable) { return Collections.singletonList(new VariableLookupItem((PsiVariable)completion).setSubstitutor(substitutor)); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java index d3ac33f4cab9..30e69895c245 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java @@ -40,6 +40,7 @@ public class JavaMethodCallElement extends LookupItem implements Type private PsiSubstitutor myQualifierSubstitutor = PsiSubstitutor.EMPTY; private PsiSubstitutor myInferenceSubstitutor = PsiSubstitutor.EMPTY; private boolean myMayNeedExplicitTypeParameters; + private String myForcedQualifier = ""; public JavaMethodCallElement(@NotNull PsiMethod method) { this(method, method.getName()); @@ -67,6 +68,11 @@ public class JavaMethodCallElement extends LookupItem implements Type } } + void setForcedQualifier(@NotNull String forcedQualifier) { + myForcedQualifier = forcedQualifier; + setLookupString(forcedQualifier + getLookupString()); + } + @Override public PsiType getType() { return getSubstitutor().substitute(getInferenceSubstitutor().substitute(getObject().getReturnType())); @@ -255,6 +261,9 @@ public class JavaMethodCallElement extends LookupItem implements Type MemberLookupHelper helper = myHelper != null ? myHelper : new MemberLookupHelper(myMethod, myContainingClass, false, false); helper.renderElement(presentation, myHelper != null, myHelper != null && !myHelper.willBeImported(), getSubstitutor()); + if (!myForcedQualifier.isEmpty()) { + presentation.setItemText(myForcedQualifier + presentation.getItemText()); + } if (shouldInsertTypeParameters()) { String typeParamsText = getTypeParamsText(true); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java b/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java index dcd34bfa1f22..c6423a403564 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java @@ -48,7 +48,7 @@ class SuperCalls { fakeSuper.processVariants(superProcessor); for (CompletionElement completionElement : superProcessor.getResults()) { - for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference)) { + for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference, superProcessor)) { set.add(withQualifiedSuper(className, item)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java index 040bc8bba976..c09b1b71d316 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java @@ -56,9 +56,12 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme private final boolean myInJavaDoc; private boolean myStatic = false; private PsiElement myDeclarationHolder = null; - private final Map myResults = new LinkedHashMap(); + private final Map myResults = new LinkedHashMap<>(); private final Set mySecondRateResults = ContainerUtil.newIdentityTroveSet(); private final Set myShadowedNames = ContainerUtil.newHashSet(); + private final Set myCurrentScopeMethodNames = ContainerUtil.newHashSet(); + private final Set myFinishedScopesMethodNames = ContainerUtil.newHashSet(); + private final Set myMethodsToQualify = ContainerUtil.newHashSet(); private final PsiElement myElement; private final PsiElement myScope; private final ElementFilter myFilter; @@ -68,7 +71,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme private PsiClass myQualifierClass = null; private final Condition myMatcher; private final Options myOptions; - private final Set myNonInitializedFields = new HashSet(); + private final Set myNonInitializedFields = new HashSet<>(); private final boolean myAllowStaticWithInstanceQualifier; public JavaCompletionProcessor(@NotNull PsiElement element, ElementFilter filter, Options options, @NotNull Condition nameCondition) { @@ -135,6 +138,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme public static Set getNonInitializedFields(PsiElement element) { final PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); + //noinspection SSBasedInspection final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, true, PsiClass.class); if (statement == null || method == null || !method.isConstructor()) { return Collections.emptySet(); @@ -152,7 +156,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme parent = next; } - final Set fields = new HashSet(); + final Set fields = new HashSet<>(); final PsiClass containingClass = method.getContainingClass(); assert containingClass != null; for (PsiField field : containingClass.getFields()) { @@ -195,6 +199,8 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme } if(event == JavaScopeProcessorEvent.CHANGE_LEVEL){ myMembersFlag = true; + myFinishedScopesMethodNames.addAll(myCurrentScopeMethodNames); + myCurrentScopeMethodNames.clear(); } if (event == JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT) { myDeclarationHolder = (PsiElement)associated; @@ -251,11 +257,23 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (sp == StaticProblem.staticAfterInstance) { mySecondRateResults.add(completion); } + + if (element instanceof PsiMethod) { + String name = ((PsiMethod)element).getName(); + myCurrentScopeMethodNames.add(name); + if (myFinishedScopesMethodNames.contains(name)) { + myMethodsToQualify.add((PsiMethod)element); + } + } } return true; } + public boolean shouldQualifyMethodCall(@NotNull PsiMethod method) { + return myMethodsToQualify.contains(method); + } + private boolean isQualifiedContext() { final PsiElement elementParent = myElement.getParent(); return elementParent instanceof PsiQualifiedReference && ((PsiQualifiedReference)elementParent).getQualifier() != null; @@ -331,12 +349,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (mySecondRateResults.size() == myResults.size()) { return mySecondRateResults; } - return ContainerUtil.filter(myResults.values(), new Condition() { - @Override - public boolean value(CompletionElement element) { - return !mySecondRateResults.contains(element); - } - }); + return ContainerUtil.filter(myResults.values(), element -> !mySecondRateResults.contains(element)); } public void clear() { diff --git a/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass.java b/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass.java new file mode 100644 index 000000000000..895160da216c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass.java @@ -0,0 +1,10 @@ +class A { + void foo(int x){} + class Inner { + void foo(){} + + void test() { + fo + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass_after.java b/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass_after.java new file mode 100644 index 000000000000..432dcab98eeb --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/InstanceMethodFromOuterClass_after.java @@ -0,0 +1,10 @@ +class A { + void foo(int x){} + class Inner { + void foo(){} + + void test() { + A.this.foo(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass.java b/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass.java new file mode 100644 index 000000000000..b7cf284e5fa8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass.java @@ -0,0 +1,10 @@ +class A { + static void foo(int x){} + class Inner { + void foo(){} + + void test() { + fo + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass_after.java b/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass_after.java new file mode 100644 index 000000000000..d70b99097fb9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/StaticMethodFromOuterClass_after.java @@ -0,0 +1,10 @@ +class A { + static void foo(int x){} + class Inner { + void foo(){} + + void test() { + A.foo(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 9bbe5cbd2ca3..cac0f2319347 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -482,6 +482,22 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { public void testMethodCallAfterFinally() { doTest() } public void testPrivateInAnonymous() throws Throwable { doTest() } + public void testStaticMethodFromOuterClass() { + configure() + assertStringItems 'foo', 'A.foo', 'for' + assert LookupElementPresentation.renderElement(myItems[1]).itemText == 'A.foo' + selectItem(myItems[1]) + checkResult() + } + + public void testInstanceMethodFromOuterClass() { + configure() + assertStringItems 'foo', 'A.this.foo', 'for' + assert LookupElementPresentation.renderElement(myItems[1]).itemText == 'A.this.foo' + selectItem(myItems[1]) + checkResult() + } + public void testMethodParenthesesSpaces() throws Throwable { codeStyleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES = true codeStyleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES = true