diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java index 1f7c36f5a010..4d8668a09268 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java @@ -146,6 +146,7 @@ public class AllClassesGetter { final Set qnames = new THashSet(); final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(context.getProject()); + final boolean pkgContext = JavaCompletionUtil.inSomePackage(context); AllClassesSearch.search(scope, context.getProject(), new Condition() { public boolean value(String s) { @@ -154,7 +155,8 @@ public class AllClassesGetter { }).forEach(new Processor() { public boolean process(PsiClass psiClass) { assert psiClass != null; - if (isSuitable(context, packagePrefix, qnames, psiClass, filterByScope)) { + if (isSuitable(context, packagePrefix, qnames, psiClass, filterByScope, pkgContext)) { + qnames.add(psiClass.getQualifiedName()); consumer.consume(psiClass); } return true; @@ -177,8 +179,8 @@ public class AllClassesGetter { } private static boolean isSuitable(@NotNull final PsiElement context, final String packagePrefix, final Set qnames, - @NotNull final PsiClass psiClass, - final boolean filterByScope) { + @NotNull final PsiClass psiClass, + final boolean filterByScope, final boolean pkgContext) { ProgressManager.checkCanceled(); if (!context.isValid() || !psiClass.isValid()) return false; @@ -188,11 +190,11 @@ public class AllClassesGetter { final String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName == null || !qualifiedName.startsWith(packagePrefix)) return false; - if (!(psiClass instanceof PsiCompiledElement) || !filterByScope || - JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().isAccessible(psiClass, context, psiClass)) { - return qnames.add(qualifiedName); - } - return false; + if (qnames.contains(qualifiedName)) return false; + + if (!filterByScope && !(psiClass instanceof PsiCompiledElement)) return true; + + return JavaCompletionUtil.isSourceLevelAccessible(context, psiClass, pkgContext); } public static JavaPsiClassReferenceElement createLookupItem(@NotNull final PsiClass psiClass, 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 a0dbe85c7561..60aafaf0d85c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -587,11 +587,16 @@ public class JavaCompletionUtil { boolean mayHighlight = qualifierType != null && (castedQualifierType == null || !qualifierType.isAssignableFrom(castedQualifierType)); + final boolean pkgContext = inSomePackage(element); + final Set mentioned = new THashSet(); for (CompletionElement completionElement : plainResults) { LookupElement item = createLookupElement(completionElement, qualifierType); if (item != null) { final Object o = item.getObject(); + if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) { + continue; + } if (o instanceof PsiMember) { mentioned.add((PsiMember)o); } @@ -1002,4 +1007,24 @@ public class JavaCompletionUtil { } return false; } + + public static boolean inSomePackage(PsiElement context) { + PsiFile contextFile = context.getContainingFile(); + return contextFile instanceof PsiClassOwner && StringUtil.isNotEmpty(((PsiClassOwner)contextFile).getPackageName()); + } + + public static boolean isSourceLevelAccessible(PsiElement context, PsiClass psiClass, final boolean pkgContext) { + if (!JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().isAccessible(psiClass, context, psiClass)) { + return false; + } + + if (pkgContext) { + PsiFile classFile = psiClass.getContainingFile(); + if (classFile instanceof PsiClassOwner && StringUtil.isEmpty(((PsiClassOwner)classFile).getPackageName())) { + return false; + } + } + + return true; + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java index 74c6384d32d0..e238e33b056f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java @@ -32,11 +32,13 @@ public abstract class StaticMemberProcessor { private final Project myProject; private final PsiResolveHelper myResolveHelper; private boolean myHintShown = false; + private final boolean myPackagedContext; public StaticMemberProcessor(final PsiElement position) { myPosition = position; myProject = myPosition.getProject(); myResolveHelper = JavaPsiFacade.getInstance(myProject).getResolveHelper(); + myPackagedContext = JavaCompletionUtil.inSomePackage(position); } public void importMembersOf(@Nullable PsiClass psiClass) { @@ -64,7 +66,7 @@ public abstract class StaticMemberProcessor { final PsiClass containingClass = method.getContainingClass(); assert containingClass != null; - if (classes.add(containingClass)) { + if (classes.add(containingClass) && JavaCompletionUtil.isSourceLevelAccessible(myPosition, containingClass, myPackagedContext)) { final boolean shouldImport = myStaticImportedClasses.contains(containingClass); if (!myHintShown && !shouldImport && CompletionService.getCompletionService().getAdvertisementText() == null) { final String shortcut = CompletionContributor.getActionShortcut("EditorRight"); 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 5d1d5bcc4981..f2c1b2f641fb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -828,6 +828,15 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { public void testOnlyAnnotationsAfterAt() throws Throwable { doTest() } public void testOnlyExceptionsInCatch() throws Throwable { doTest() } + public void testTopLevelClassesFromPackaged() throws Throwable { + myFixture.addClass "public class Fooooo {}" + final text = "package foo; class Bar { Fooo }" + def file = myFixture.addFileToProject("foo/Bar.java", text) + myFixture.configureFromExistingVirtualFile file.virtualFile + assertEmpty myFixture.completeBasic() + myFixture.checkResult text + } + public void testRightShift() throws Throwable { configure() assertStringItems("myField1", "myField2"); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy index 917ca3e0fad5..eafd4427cf54 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy @@ -437,5 +437,15 @@ try {} catch (AbcdException""" myFixture.checkResult "try {} catch (AbcdException e) {}" } + public void testTopLevelClassesFromPackaged() throws Throwable { + myFixture.addClass "public class Fooooo {}" + final text = "package foo; class Bar { Fooo }" + def file = myFixture.addFileToProject("foo/Bar.groovy", text) + myFixture.configureFromExistingVirtualFile file.virtualFile + assertEmpty myFixture.completeBasic() + myFixture.checkResult text + } + + } \ No newline at end of file