diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java index fcc92c9b775d..f676bc4b4dcc 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java @@ -22,6 +22,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyKey; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -313,106 +314,150 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava } private boolean processDeclarationsNoGuess(PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, PsiElement place) { + PsiImportList importList = getImportList(); + PsiImportStatement[] importStatements = importList != null ? importList.getImportStatements() : PsiImportStatement.EMPTY_ARRAY; + PsiImportStaticStatement[] importStaticStatements = importList != null ? importList.getImportStaticStatements() : PsiImportStaticStatement.EMPTY_ARRAY; + StaticImportFilteringProcessor staticImportProcessor = new StaticImportFilteringProcessor(processor); + + boolean shouldProcessClasses = shouldProcessClasses(processor); + if (shouldProcessClasses && !processOwnClasses(processor, state)) return false; + + if (!processNamedImports(state, importStatements, importStaticStatements, staticImportProcessor)) return false; + + if (shouldProcessClasses) { + if (!processCurrentPackage(processor, state, place)) return false; + if (!processOnDemandTypeImports(processor, state, place, importStatements)) return false; + } + + if (!processOnDemandStaticImports(state, lastParent, place, importStaticStatements, staticImportProcessor)) return false; + + if (shouldProcessClasses && !processImplicitImports(processor, state, place)) return false; + + return true; + } + + private boolean processOwnClasses(PsiScopeProcessor processor, @NotNull ResolveState state) { processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this); + for (PsiClass aClass : getClasses()) { + if (!processor.execute(aClass, state)) return false; + } + return true; + } + + private static boolean shouldProcessClasses(PsiScopeProcessor processor) { final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); - final NameHint nameHint = processor.getHint(NameHint.KEY); - final String name = nameHint != null ? nameHint.getName(state) : null; - final PsiImportList importList = getImportList(); + return classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS); + } - if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { - final PsiClass[] classes = getClasses(); - for (PsiClass aClass : classes) { - if (!processor.execute(aClass, state)) return false; - } + private static boolean processNamedImports(ResolveState state, + PsiImportStatement[] importStatements, + PsiImportStaticStatement[] importStaticStatements, + StaticImportFilteringProcessor staticImportProcessor) { + PsiScopeProcessor processor = staticImportProcessor.myDelegate; - final PsiImportStatement[] importStatements = importList != null ? importList.getImportStatements() : PsiImportStatement.EMPTY_ARRAY; + NameHint nameHint = processor.getHint(NameHint.KEY); + String name = nameHint != null ? nameHint.getName(state) : null; - // single-type processing - for (PsiImportStatement statement : importStatements) { - if (!statement.isOnDemand()) { - if (name != null) { - final String refText = statement.getQualifiedName(); - if (refText == null || !refText.endsWith(name)) continue; - } + if (shouldProcessClasses(processor) && !processNamedTypeImports(processor, state, name, importStatements)) { + return false; + } - final PsiElement resolved = statement.resolve(); - if (resolved instanceof PsiClass) { - processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement); - final PsiClass containingClass = ((PsiClass)resolved).getContainingClass(); - if (containingClass != null && containingClass.hasTypeParameters()) { - if (!processor.execute(resolved, state.put(PsiSubstitutor.KEY, - createRawSubstitutor(containingClass)))) return false; - } - else if (!processor.execute(resolved, state)) return false; - } + return processNamedStaticImports(state, name, importStaticStatements, staticImportProcessor); + } + + private static boolean processNamedTypeImports(PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable String nameHint, PsiImportStatement[] imports) { + for (PsiImportStatement statement : imports) { + if (!statement.isOnDemand()) { + if (nameHint != null) { + final String refText = statement.getQualifiedName(); + if (refText == null || !nameHint.equals(StringUtil.getShortName(refText))) continue; } - } - processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null); - // check in current package - final PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName()); - if (aPackage != null && !processPackageDeclarations(processor, state, place, aPackage)) return false; + final PsiElement resolved = statement.resolve(); + if (resolved instanceof PsiClass) { + processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement); + PsiClass containingClass = ((PsiClass)resolved).getContainingClass(); + ResolveState classState = containingClass != null && containingClass.hasTypeParameters() + ? state.put(PsiSubstitutor.KEY, createRawSubstitutor(containingClass)) + : state; - // on-demand processing - for (PsiImportStatement statement : importStatements) { - if (statement.isOnDemand()) { - final PsiElement resolved = statement.resolve(); - if (resolved != null) { - processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement); - processOnDemandTarget(resolved, processor, state, place); - } + if (!processor.execute(resolved, classState)) return false; } } } + return true; + } - final PsiImportStaticStatement[] importStaticStatements = importList != null ? importList.getImportStaticStatements() : PsiImportStaticStatement.EMPTY_ARRAY; - if (importStaticStatements.length > 0) { - final StaticImportFilteringProcessor staticImportProcessor = new StaticImportFilteringProcessor(processor); + private boolean processCurrentPackage(PsiScopeProcessor processor, ResolveState state, PsiElement place) { + processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null); + PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName()); + if (aPackage != null && !processPackageDeclarations(processor, state, place, aPackage)) return false; + return true; + } - // single member processing - for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { - if (importStaticStatement.isOnDemand()) continue; - - String referenceName = importStaticStatement.getReferenceName(); - if (name != null && !name.equals(referenceName)) continue; - - final PsiJavaCodeReferenceElement reference = importStaticStatement.getImportReference(); - if (reference != null) { - final JavaResolveResult[] results = reference.multiResolve(false); - if (results.length > 0) { - staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); - for (JavaResolveResult result : results) { - staticImportProcessor.registerSingleStaticImportHiding(result, referenceName); - PsiElement element = result.getElement(); - if (element != null && !staticImportProcessor.execute(element, state)) return false; - } - } - } - } - - // on-demand processing - for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { - if (!importStaticStatement.isOnDemand()) continue; - final PsiClass targetElement = importStaticStatement.resolveTargetClass(); - if (targetElement != null) { - staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); - if (!targetElement.processDeclarations(staticImportProcessor, state, lastParent, place)) return false; - } - } - } - - if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { - processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null); - - final PsiJavaCodeReferenceElement[] implicitlyImported = getImplicitlyImportedPackageReferences(); - for (PsiJavaCodeReferenceElement aImplicitlyImported : implicitlyImported) { - final PsiElement resolved = aImplicitlyImported.resolve(); + private static boolean processOnDemandTypeImports(PsiScopeProcessor processor, ResolveState state, PsiElement place, PsiImportStatement[] imports) { + for (PsiImportStatement statement : imports) { + if (statement.isOnDemand()) { + final PsiElement resolved = statement.resolve(); if (resolved != null) { + processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement); if (!processOnDemandTarget(resolved, processor, state, place)) return false; } } } + return true; + } + private static boolean processNamedStaticImports(@NotNull ResolveState state, + @Nullable String nameHint, + @NotNull PsiImportStaticStatement[] importStaticStatements, + StaticImportFilteringProcessor staticImportProcessor) { + for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { + if (importStaticStatement.isOnDemand()) continue; + + String referenceName = importStaticStatement.getReferenceName(); + if (nameHint != null && !nameHint.equals(referenceName)) continue; + + final PsiJavaCodeReferenceElement reference = importStaticStatement.getImportReference(); + if (reference != null) { + final JavaResolveResult[] results = reference.multiResolve(false); + if (results.length > 0) { + staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); + for (JavaResolveResult result : results) { + staticImportProcessor.registerSingleStaticImportHiding(result, referenceName); + PsiElement element = result.getElement(); + if (element != null && !staticImportProcessor.execute(element, state)) return false; + } + } + } + } + return true; + } + + private static boolean processOnDemandStaticImports(@NotNull ResolveState state, + PsiElement lastParent, + PsiElement place, + PsiImportStaticStatement[] importStaticStatements, + StaticImportFilteringProcessor staticImportProcessor) { + for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { + if (!importStaticStatement.isOnDemand()) continue; + final PsiClass targetElement = importStaticStatement.resolveTargetClass(); + if (targetElement != null) { + staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); + if (!targetElement.processDeclarations(staticImportProcessor, state, lastParent, place)) return false; + } + } + return true; + } + + private boolean processImplicitImports(PsiScopeProcessor processor, ResolveState state, PsiElement place) { + processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null); + for (PsiJavaCodeReferenceElement aImplicitlyImported : getImplicitlyImportedPackageReferences()) { + final PsiElement resolved = aImplicitlyImported.resolve(); + if (resolved != null) { + if (!processOnDemandTarget(resolved, processor, state, place)) return false; + } + } return true; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java index 06ff256e3379..583892772e60 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java @@ -239,7 +239,10 @@ public class ClassResolverProcessor implements PsiScopeProcessor, NameHint, Elem return true; } } - return myCurrentFileContext instanceof PsiImportStatementBase; + if (myCurrentFileContext instanceof PsiImportStatementBase) { + return ((PsiImportStatementBase)myCurrentFileContext).isOnDemand(); + } + return false; } private boolean checkAccessibility(final PsiClass aClass) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/staticImportConflict/UsageMixed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/staticImportConflict/UsageMixed.java index 9b5acd3cb554..4149a3c197e2 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/staticImportConflict/UsageMixed.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/staticImportConflict/UsageMixed.java @@ -18,6 +18,6 @@ import static x.Ba class UsageMixed { void use() { - D.class.getName(); + D.class.getName(); } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/psi/resolve/LightResolveClassTest.groovy b/java/java-tests/testSrc/com/intellij/java/psi/resolve/LightResolveClassTest.groovy new file mode 100644 index 000000000000..7022329c5445 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/psi/resolve/LightResolveClassTest.groovy @@ -0,0 +1,30 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.psi.resolve + + +import com.intellij.psi.impl.source.PsiFileImpl +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +/** + * @author peter + */ +class LightResolveClassTest extends LightCodeInsightFixtureTestCase { + + void "test no loading for star imported class when named import matches"() { + def unnamedFile = myFixture.addFileToProject('unnamed/Bar.java', 'package unnamed; public class Bar {}') as PsiFileImpl + def named = myFixture.addClass 'package named; public class Bar {}' + def foo = myFixture.addClass ''' +import unnamed.*; +import named.Bar; +class Foo extends Bar {} +''' + + assertContentsNotLoaded(unnamedFile) + assert named == foo.superClass + assertContentsNotLoaded(unnamedFile) + } + + private static void assertContentsNotLoaded(PsiFileImpl unnamedFile) { + assert unnamedFile.derefStub() == null + assert unnamedFile.treeElement == null + } +} diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index eedee4bc13f0..3ff3420d7b2a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -2,6 +2,7 @@ package com.intellij.psi.impl.source; +import com.google.common.annotations.VisibleForTesting; import com.intellij.ide.util.PsiNavigationSupport; import com.intellij.lang.*; import com.intellij.navigation.ItemPresentation; @@ -617,7 +618,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF } @Nullable - private StubTree derefStub() { + @VisibleForTesting + public StubTree derefStub() { return myTrees.derefStub(); }