From af44ecd7cf68104ed39290b6c387fa92a16124b3 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 13 Oct 2012 17:30:10 +0200 Subject: [PATCH] IDEA-91838 Complete instance members from static inner class --- .../completion/JavaCompletionContributor.java | 12 ++- .../completion/JavaCompletionUtil.java | 5 +- .../JavaDocCompletionContributor.java | 3 +- .../JavaSmartCompletionContributor.java | 8 +- .../scope/JavaCompletionProcessor.java | 77 ++++++++++++++----- .../AccessInstanceFromStaticSecond.java | 11 +++ .../AccessInstanceFromStaticSecond_after.java | 11 +++ .../completion/NormalCompletionTest.groovy | 6 ++ 8 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index 2e4758da8cab..a04d43e27b86 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; import com.intellij.codeInsight.hint.ShowParameterInfoHandler; import com.intellij.codeInsight.lookup.*; @@ -276,7 +277,7 @@ public class JavaCompletionContributor extends CompletionContributor { private static Set addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, final InheritorsHolder inheritors) { final Set usedWords = new HashSet(); final PsiElement position = parameters.getPosition(); - final boolean checkAccess = parameters.getInvocationCount() <= 1; + final boolean first = parameters.getInvocationCount() <= 1; final boolean isSwitchLabel = SWITCH_LABEL.accepts(position); final boolean isAfterNew = JavaClassNameCompletionContributor.AFTER_NEW.accepts(position); final boolean pkgContext = JavaCompletionUtil.inSomePackage(position); @@ -286,10 +287,15 @@ public class JavaCompletionContributor extends CompletionContributor { final ElementFilter filter = getReferenceFilter(position); if (filter != null) { final PsiFile originalFile = parameters.getOriginalFile(); + JavaCompletionProcessor.Options options = + JavaCompletionProcessor.Options.DEFAULT_OPTIONS + .withCheckAccess(first) + .withFilterStaticAfterInstance(first) + .withShowInstanceInStaticContext(!first); for (LookupElement element : JavaCompletionUtil.processJavaReference(position, (PsiJavaReference)reference, new ElementExtractorFilter(filter), - checkAccess, checkAccess, + options, result.getPrefixMatcher(), parameters)) { if (inheritors.alreadyProcessed(element)) { continue; @@ -429,7 +435,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (showClasses && insertedElement.getParent() instanceof PsiReferenceExpression) { final Set set = JavaCompletionUtil.processJavaReference( - insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, false, result.getPrefixMatcher(), parameters); + insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, JavaCompletionProcessor.Options.DEFAULT_OPTIONS, result.getPrefixMatcher(), parameters); for (final LookupElement element : set) { result.addElement(element); } 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 4f59f860d868..81c7ff6a3b9e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -324,7 +324,8 @@ public class JavaCompletionUtil { } public static Set processJavaReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter, - final boolean checkAccess, boolean filterStaticAfterInstance, final PrefixMatcher matcher, CompletionParameters parameters) { + JavaCompletionProcessor.Options options, + final PrefixMatcher matcher, CompletionParameters parameters) { final Set set = new LinkedHashSet(); final Condition nameCondition = new Condition() { public boolean value(String s) { @@ -335,7 +336,7 @@ public class JavaCompletionUtil { PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText()); - final JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, checkAccess, checkInitialized, filterStaticAfterInstance, nameCondition); + final JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, options.withInitialized(checkInitialized), nameCondition); final PsiType plainQualifier = processor.getQualifierType(); PsiType qualifierType = plainQualifier; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java index cffb3f0f1762..e60c97d1fd07 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java @@ -28,6 +28,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.patterns.PsiJavaPatterns; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.*; @@ -75,7 +76,7 @@ public class JavaDocCompletionContributor extends CompletionContributor { if (ref instanceof PsiJavaReference) { result.stopHere(); - final JavaCompletionProcessor processor = new JavaCompletionProcessor(position, TrueFilter.INSTANCE, false, false, false, null); + final JavaCompletionProcessor processor = new JavaCompletionProcessor(position, TrueFilter.INSTANCE, JavaCompletionProcessor.Options.CHECK_NOTHING, Condition.TRUE); ((PsiJavaReference) ref).processVariants(processor); for (final CompletionElement _item : processor.getResults()) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java index 228c630eda8d..90db09e305f8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.*; +import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.util.Key; import com.intellij.patterns.ElementPattern; @@ -396,7 +397,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor { if (reference instanceof PsiJavaReference) { final PsiJavaReference javaReference = (PsiJavaReference)reference; - return JavaCompletionUtil.processJavaReference(element, javaReference, new ElementFilter() { + ElementFilter checkClass = new ElementFilter() { public boolean isAcceptable(Object element, PsiElement context) { return filter.isAcceptable(element, context); } @@ -413,7 +414,10 @@ public class JavaSmartCompletionContributor extends CompletionContributor { } return false; } - }, true, parameters.getInvocationCount() <= 1, matcher, parameters); + }; + JavaCompletionProcessor.Options options = + JavaCompletionProcessor.Options.DEFAULT_OPTIONS.withFilterStaticAfterInstance(parameters.getInvocationCount() <= 1); + return JavaCompletionUtil.processJavaReference(element, javaReference, checkClass, options, matcher, parameters); } return Collections.emptySet(); 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 375af91b5b90..4ce4afdb84c1 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 @@ -51,7 +51,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme private boolean myStatic = false; private PsiElement myDeclarationHolder = null; private final Set myResultNames = new THashSet(); - private final List myResults; + private final List myResults = new ArrayList(); private final PsiElement myElement; private final PsiElement myScope; private final ElementFilter myFilter; @@ -59,13 +59,12 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme private PsiType myQualifierType = null; private PsiClass myQualifierClass = null; private final Condition myMatcher; - private final boolean myCheckAccess; + private final Options myOptions; private final Set myNonInitializedFields = new HashSet(); private boolean myAllowStaticWithInstanceQualifier; - public JavaCompletionProcessor(PsiElement element, ElementFilter filter, final boolean checkAccess, boolean checkInitialized, boolean filterStaticAfterInstance, @Nullable Condition nameCondition) { - myCheckAccess = checkAccess; - myResults = new ArrayList(); + public JavaCompletionProcessor(@NotNull PsiElement element, ElementFilter filter, Options options, @NotNull Condition nameCondition) { + myOptions = options; myElement = element; myMatcher = nameCondition; myFilter = filter; @@ -106,11 +105,11 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme } } - if (checkInitialized) { + if (myOptions.checkInitialized) { myNonInitializedFields.addAll(getNonInitializedFields(element)); } - myAllowStaticWithInstanceQualifier = !filterStaticAfterInstance || CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE || + myAllowStaticWithInstanceQualifier = !options.filterStaticAfterInstance || CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE || SuppressManager.getInstance() .isSuppressedFor(element, AccessStaticViaInstance.ACCESS_STATIC_VIA_INSTANCE); @@ -199,17 +198,16 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme return true; } - final PsiElement elementParent = myElement.getParent(); - if (!(elementParent instanceof PsiMethodReferenceExpression) && checkStaticFlag(element)) return true; + if (!(myElement.getParent() instanceof PsiMethodReferenceExpression) && !isStaticsOk(element)) { + return true; + } - if (element instanceof PsiPackage && myScope instanceof PsiClass) { - if (!(elementParent instanceof PsiQualifiedReference && ((PsiQualifiedReference)elementParent).getQualifier() != null)) { - return true; - } + if (element instanceof PsiPackage && myScope instanceof PsiClass && !isQualifiedContext()) { + return true; } if (satisfies(element, state) && isAccessible(element)) { - CompletionElement element1 = new CompletionElement((PsiNamedElement)element, state.get(PsiSubstitutor.KEY)); + CompletionElement element1 = new CompletionElement(element, state.get(PsiSubstitutor.KEY)); if (myResultNames.add(element1.getUniqueId())) { myResults.add(element1); } @@ -217,13 +215,21 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme return true; } - private boolean checkStaticFlag(PsiElement element) { + private boolean isQualifiedContext() { + final PsiElement elementParent = myElement.getParent(); + return elementParent instanceof PsiQualifiedReference && ((PsiQualifiedReference)elementParent).getQualifier() != null; + } + + private boolean isStaticsOk(PsiElement element) { + if (myOptions.showInstanceInStaticContext && !isQualifiedContext()) { + return true; + } if (!(element instanceof PsiClass) && element instanceof PsiModifierListOwner) { PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)element; if (myStatic) { if (!modifierListOwner.hasModifierProperty(PsiModifier.STATIC)) { // we don't need non static method in static context. - return true; + return false; } } else { @@ -231,16 +237,16 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme && modifierListOwner.hasModifierProperty(PsiModifier.STATIC) && !myMembersFlag) { // according settings we don't need to process such fields/methods - return true; + return false; } } } - return false; + return true; } public boolean satisfies(@NotNull PsiElement element, @NotNull ResolveState state) { final String name = PsiUtilCore.getName(element); - if (StringUtil.isNotEmpty(name) && (myMatcher == null || myMatcher.value(name))) { + if (name != null && StringUtil.isNotEmpty(name) && myMatcher.value(name)) { if (myFilter.isClassAcceptable(element.getClass()) && myFilter.isAcceptable(new CandidateInfo(element, state.get(PsiSubstitutor.KEY)), myElement)) { return true; } @@ -254,7 +260,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme } private boolean isAccessible(final PsiElement element) { - if (!myCheckAccess) return true; + if (!myOptions.checkAccess) return true; if (!(element instanceof PsiMember)) return true; PsiMember member = (PsiMember)element; @@ -302,12 +308,43 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme @Override public T getHint(@NotNull Key hintKey) { if (hintKey == ElementClassHint.KEY) { + //noinspection unchecked return (T)this; } if (hintKey == JavaCompletionHints.NAME_FILTER) { + //noinspection unchecked return (T)myMatcher; } return super.getHint(hintKey); } + + public static class Options { + public static final Options DEFAULT_OPTIONS = new Options(true, false, true, false); + public static final Options CHECK_NOTHING = new Options(false, false, false, false); + final boolean checkAccess; + final boolean checkInitialized; + final boolean filterStaticAfterInstance; + final boolean showInstanceInStaticContext; + + private Options(boolean checkAccess, boolean checkInitialized, boolean filterStaticAfterInstance, boolean showInstanceInStaticContext) { + this.checkAccess = checkAccess; + this.checkInitialized = checkInitialized; + this.filterStaticAfterInstance = filterStaticAfterInstance; + this.showInstanceInStaticContext = showInstanceInStaticContext; + } + + public Options withInitialized(boolean checkInitialized) { + return new Options(checkAccess, checkInitialized, filterStaticAfterInstance, showInstanceInStaticContext); + } + public Options withCheckAccess(boolean checkAccess) { + return new Options(checkAccess, checkInitialized, filterStaticAfterInstance, showInstanceInStaticContext); + } + public Options withFilterStaticAfterInstance(boolean filterStaticAfterInstance) { + return new Options(checkAccess, checkInitialized, filterStaticAfterInstance, showInstanceInStaticContext); + } + public Options withShowInstanceInStaticContext(boolean showInstanceInStaticContext) { + return new Options(checkAccess, checkInitialized, filterStaticAfterInstance, showInstanceInStaticContext); + } + } } diff --git a/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond.java b/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond.java new file mode 100644 index 000000000000..a6fd614b90e4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond.java @@ -0,0 +1,11 @@ +public class Inspection { + + public boolean ignoreEnums = false; + + private static class Visitor { + public void visitClass() { + if (ignoen) { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond_after.java b/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond_after.java new file mode 100644 index 000000000000..1e4633f67723 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AccessInstanceFromStaticSecond_after.java @@ -0,0 +1,11 @@ +public class Inspection { + + public boolean ignoreEnums = false; + + private static class Visitor { + public void visitClass() { + if (ignoreEnums) { + } + } + } +} \ 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 33892f410b62..7b70469cbb30 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -497,6 +497,12 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { checkResult() } + public void testAccessInstanceFromStaticSecond() throws Throwable { + configure() + myFixture.complete(CompletionType.BASIC, 2) + checkResult() + } + public void testContinueLabel() throws Throwable { doTest(); } public void testAnonymousProcess() {