From 3b8ed7796d431a9e1af92d23f1ffbf94cf790de0 Mon Sep 17 00:00:00 2001 From: Peter Gromov Date: Wed, 18 Dec 2019 21:05:35 +0100 Subject: [PATCH] java: when there's a matching named import, don't go into on-demand imports (part of IDEA-228814, maybe IDEA-226939) to prevent recursion prevention and suboptimal caching when there are star imports in the file but their superclass references are imported explicitly GitOrigin-RevId: eadc92df1c754153f5b6f97c9bf2734c30243181 --- .../psi/impl/source/PsiJavaFileBaseImpl.java | 281 ++++++++---------- .../openapi/util/RecursionManager.java | 2 +- 2 files changed, 122 insertions(+), 161 deletions(-) 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 c9d086d82d74..227a57427324 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 @@ -8,6 +8,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.VolatileNotNullLazyValue; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; @@ -20,6 +21,7 @@ import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; import com.intellij.psi.impl.source.resolve.ClassResolverProcessor; import com.intellij.psi.impl.source.resolve.SymbolCollectingProcessor; +import com.intellij.psi.impl.source.resolve.SymbolCollectingProcessor.ResultWithContext; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.scope.*; import com.intellij.psi.stubs.StubElement; @@ -27,21 +29,21 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.JBIterable; import com.intellij.util.containers.MostlySingularMultiMap; +import com.intellij.util.containers.MultiMap; import com.intellij.util.indexing.IndexingDataKeys; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; +import java.util.*; public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJavaFile { private static final Logger LOG = Logger.getInstance(PsiJavaFileBaseImpl.class); private static final String[] IMPLICIT_IMPORTS = { CommonClassNames.DEFAULT_PACKAGE }; - private final CachedValue> myResolveCache; + private final CachedValue> myResolveCache; private volatile String myPackageName; protected PsiJavaFileBaseImpl(IElementType elementType, IElementType contentElementType, FileViewProvider viewProvider) { @@ -230,72 +232,33 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava return PsiImplUtil.namesToPackageReferences(myManager, IMPLICIT_IMPORTS); } - private static class StaticImportFilteringProcessor implements PsiScopeProcessor { - private final PsiScopeProcessor myDelegate; - private boolean myIsProcessingOnDemand; - private final Collection myHiddenFieldNames = new HashSet<>(); - private final Collection myHiddenMethodNames = new HashSet<>(); - private final Collection myHiddenTypeNames = new HashSet<>(); + private static class StaticImportFilteringProcessor extends DelegatingScopeProcessor { + private final Map> myExplicitlyEnumerated; private final Collection myCollectedElements = new HashSet<>(); - StaticImportFilteringProcessor(final PsiScopeProcessor delegate) { - myDelegate = delegate; - } - - @Override - public T getHint(@NotNull final Key hintKey) { - return myDelegate.getHint(hintKey); - } - - @Override - public void handleEvent(@NotNull final Event event, final Object associated) { - if (JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT.equals(event) && associated instanceof PsiImportStaticStatement) { - final PsiImportStaticStatement importStaticStatement = (PsiImportStaticStatement)associated; - myIsProcessingOnDemand = importStaticStatement.isOnDemand(); - } - myDelegate.handleEvent(event, associated); - } - - /** - * JLS 6.4 Shadowing and Obscuring - * A single-static-import declaration d in a compilation unit c of package p that imports a field named n shadows the declaration of any - * static field named n imported by a static-import-on-demand declaration in c, throughout c. - * - * A single-static-import declaration d in a compilation unit c of package p that imports a method named n with signature s shadows the - * declaration of any static method named n with signature s imported by a static-import-on-demand declaration in c, throughout c. - * - * A single-static-import declaration d in a compilation unit c of package p that imports a type named n shadows, throughout c, the declarations of: - * - any static type named n imported by a static-import-on-demand declaration in c; - * - any top level type (p7.6) named n declared in another compilation unit (p7.3) of p; - * - any type named n imported by a type-import-on-demand declaration (p7.5.2) in c. - */ - private void registerSingleStaticImportHiding(JavaResolveResult result, String referenceName) { - getHiddenMembers(result.getElement()).add(referenceName); - } - - private Collection getHiddenMembers(PsiElement element) { - if (element instanceof PsiField) { - return myHiddenFieldNames; - } - else { - return element instanceof PsiClass ? myHiddenTypeNames - : myHiddenMethodNames; - } + StaticImportFilteringProcessor(PsiScopeProcessor delegate, Map> explicitlyEnumerated) { + super(delegate); + myExplicitlyEnumerated = explicitlyEnumerated; } @Override public boolean execute(@NotNull final PsiElement element, @NotNull final ResolveState state) { if (element instanceof PsiModifierListOwner && ((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) { - if (element instanceof PsiNamedElement && myIsProcessingOnDemand) { + if (element instanceof PsiNamedElement) { final String name = ((PsiNamedElement)element).getName(); - if (getHiddenMembers(element).contains(name)) return true; + Iterable shadowing = myExplicitlyEnumerated.get(name); + if (shadowing != null && ContainerUtil.exists(shadowing, rwc -> hasSameDeclarationKind(element, rwc.getElement()))) return true; } if (myCollectedElements.add(element)) { - return myDelegate.execute(element, state); + return getDelegate().execute(element, state); } } return true; } + + private static boolean hasSameDeclarationKind(PsiElement e1, PsiElement e2) { + return e1 instanceof PsiClass ? e2 instanceof PsiClass : e1 instanceof PsiMethod ? e2 instanceof PsiMethod : e2 instanceof PsiField; + } } @Override @@ -303,49 +266,113 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava @NotNull final ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { - assert isValid(); + NameHint nameHint = processor.getHint(NameHint.KEY); + String name = nameHint != null ? nameHint.getName(state) : null; + + Map> explicitlyEnumerated = getExplicitlyEnumeratedDeclarations(); + //noinspection unchecked + Iterable iterable = name != null ? explicitlyEnumerated.get(name) + : ContainerUtil.concat(explicitlyEnumerated.values().toArray(new Iterable[0])); + if (iterable != null && !ContainerUtil.process(iterable, new MyResolveCacheProcessor(processor, state))) return false; if (processor instanceof ClassResolverProcessor && isPhysical() && (getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue()) && !PsiUtil.isInsideJavadocComment(place)) { - final ClassResolverProcessor hint = (ClassResolverProcessor)processor; - String name = hint.getName(state); - MostlySingularMultiMap cache = myResolveCache.getValue(); + MostlySingularMultiMap cache = myResolveCache.getValue(); MyResolveCacheProcessor cacheProcessor = new MyResolveCacheProcessor(processor, state); return name != null ? cache.processForKey(name, cacheProcessor) : cache.processAllValues(cacheProcessor); } - return processDeclarationsNoGuess(processor, state, lastParent, place); + return processOnDemandPackages(processor, state, lastParent, place); } - 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); + private Map> getExplicitlyEnumeratedDeclarations() { + return CachedValuesManager.getCachedValue(this, () -> { + MultiMap ownClasses = MultiMap.create(); + MultiMap typeImports = MultiMap.create(); + MultiMap staticImports = MultiMap.create(); + for (PsiClass psiClass : getClasses()) { + String name = psiClass.getName(); + if (name != null) { + ownClasses.putValue(name, psiClass); + } + } + for (PsiImportStatement anImport : getImportStatements()) { + if (!anImport.isOnDemand()) { + String qName = anImport.getQualifiedName(); + if (qName != null) { + typeImports.putValue(StringUtil.getShortName(qName), anImport); + } + } + } + for (PsiImportStaticStatement staticImport : getImportStaticStatements()) { + String name = staticImport.getReferenceName(); + if (name != null) { + staticImports.putValue(name, staticImport); + } + } + + Map> result = new LinkedHashMap<>(); + for (String name : ContainerUtil.newLinkedHashSet(ContainerUtil.concat(ownClasses.keySet(), typeImports.keySet(), staticImports.keySet()))) { + VolatileNotNullLazyValue> lazy = VolatileNotNullLazyValue.createValue( + () -> findExplicitDeclarations(name, ownClasses, typeImports, staticImports)); + result.put(name, () -> lazy.getValue().iterator()); + } + return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); + }); + } + + private static Iterable findExplicitDeclarations(String name, + MultiMap ownClasses, + MultiMap typeImports, + MultiMap staticImports) { + List result = new ArrayList<>(); + for (PsiClass psiClass : ownClasses.get(name)) { + result.add(new ResultWithContext(psiClass, null)); + } + for (PsiImportStatement statement : typeImports.get(name)) { + PsiElement target = statement.resolve(); + if (target instanceof PsiClass) { + result.add(new ResultWithContext((PsiNamedElement)target, statement)); + } + } + for (PsiImportStaticStatement statement : staticImports.get(name)) { + PsiJavaCodeReferenceElement reference = statement.getImportReference(); + if (reference != null) { + for (JavaResolveResult result1 : reference.multiResolve(false)) { + PsiElement element = result1.getElement(); + if (element instanceof PsiNamedElement) { + result.add(new ResultWithContext((PsiNamedElement)element, statement)); + } + } + } + } + return JBIterable.from(result).unique(ResultWithContext::getElement); + } + + private boolean processOnDemandPackages(PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, PsiElement place) { 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 (!processOnDemandTypeImports(processor, state, place)) return false; } - if (!processOnDemandStaticImports(state, lastParent, place, importStaticStatements, staticImportProcessor)) return false; + if (!processOnDemandStaticImports(state, lastParent, place, + new StaticImportFilteringProcessor(processor, getExplicitlyEnumeratedDeclarations()))) { + return false; + } return !shouldProcessClasses || processImplicitImports(processor, state, place); } - 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 PsiImportStaticStatement[] getImportStaticStatements() { + return getImportList() != null ? getImportList().getImportStaticStatements() : PsiImportStaticStatement.EMPTY_ARRAY; + } + + private PsiImportStatement[] getImportStatements() { + return getImportList() != null ? getImportList().getImportStatements() : PsiImportStatement.EMPTY_ARRAY; } private static boolean shouldProcessClasses(PsiScopeProcessor processor) { @@ -353,53 +380,14 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava return classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS); } - private static boolean processNamedImports(ResolveState state, - PsiImportStatement[] importStatements, - PsiImportStaticStatement[] importStaticStatements, - StaticImportFilteringProcessor staticImportProcessor) { - PsiScopeProcessor processor = staticImportProcessor.myDelegate; - - NameHint nameHint = processor.getHint(NameHint.KEY); - String name = nameHint != null ? nameHint.getName(state) : null; - - if (shouldProcessClasses(processor) && !processNamedTypeImports(processor, state, name, importStatements)) { - 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; - } - - 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; - - if (!processor.execute(resolved, classState)) return false; - } - } - } - return true; - } - 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()); return aPackage == null || processPackageDeclarations(processor, state, place, aPackage); } - private static boolean processOnDemandTypeImports(PsiScopeProcessor processor, ResolveState state, PsiElement place, PsiImportStatement[] imports) { - for (PsiImportStatement statement : imports) { + private boolean processOnDemandTypeImports(PsiScopeProcessor processor, ResolveState state, PsiElement place) { + for (PsiImportStatement statement : getImportStatements()) { if (statement.isOnDemand()) { final PsiElement resolved = statement.resolve(); if (resolved != null) { @@ -411,43 +399,16 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava 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) { + private boolean processOnDemandStaticImports(@NotNull ResolveState state, + PsiElement lastParent, + PsiElement place, + StaticImportFilteringProcessor processor) { + for (PsiImportStaticStatement importStaticStatement : getImportStaticStatements()) { 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; + processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); + if (!targetElement.processDeclarations(processor, state, lastParent, place)) return false; } } return true; @@ -582,7 +543,7 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava return JavaPsiImplementationHelper.getInstance(project).getEffectiveLanguageLevel(virtualFile); } - private static class MyCacheBuilder implements CachedValueProvider> { + private static class MyCacheBuilder implements CachedValueProvider> { private final PsiJavaFileBaseImpl myFile; MyCacheBuilder(PsiJavaFileBaseImpl file) { @@ -590,15 +551,15 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava } @Override - public Result> compute() { + public Result> compute() { SymbolCollectingProcessor p = new SymbolCollectingProcessor(); - myFile.processDeclarationsNoGuess(p, ResolveState.initial(), myFile, myFile); - MostlySingularMultiMap results = p.getResults(); + myFile.processOnDemandPackages(p, ResolveState.initial(), myFile, myFile); + MostlySingularMultiMap results = p.getResults(); return Result.create(results, PsiModificationTracker.MODIFICATION_COUNT, myFile); } } - private static class MyResolveCacheProcessor implements Processor { + private static class MyResolveCacheProcessor implements Processor { private final PsiScopeProcessor myProcessor; private final ResolveState myState; @@ -608,7 +569,7 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava } @Override - public boolean process(SymbolCollectingProcessor.ResultWithContext result) { + public boolean process(ResultWithContext result) { final PsiElement context = result.getFileContext(); myProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, context); final PsiNamedElement element = result.getElement(); diff --git a/platform/util/src/com/intellij/openapi/util/RecursionManager.java b/platform/util/src/com/intellij/openapi/util/RecursionManager.java index 3b5835c2f6fd..8384f80f2c92 100644 --- a/platform/util/src/com/intellij/openapi/util/RecursionManager.java +++ b/platform/util/src/com/intellij/openapi/util/RecursionManager.java @@ -388,7 +388,7 @@ public class RecursionManager { "com.intellij.psi.impl.source.tree.java.MethodReferenceResolver.resolve(", "com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil.isPolyExpression(", // inner class imports - "com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processDeclarations(", + "com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processOnDemandStaticImports(", // IDEA-212671 "com.intellij.xml.impl.schema.XmlNSDescriptorImpl.getRedefinedElementDescriptor(",