diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/GlobalJavaInspectionContext.java b/java/java-analysis-api/src/com/intellij/codeInspection/GlobalJavaInspectionContext.java index 09ca346081aa..eb8af30228fd 100644 --- a/java/java-analysis-api/src/com/intellij/codeInspection/GlobalJavaInspectionContext.java +++ b/java/java-analysis-api/src/com/intellij/codeInspection/GlobalJavaInspectionContext.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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.codeInspection; @@ -86,6 +72,9 @@ public abstract class GlobalJavaInspectionContext implements GlobalInspectionCon */ public abstract void enqueueMethodUsagesProcessor(RefMethod refMethod, UsagesProcessor p); + + public abstract void enqueueQualifiedNameOccurrencesProcessor(RefClass refClass, Runnable p); + public abstract EntryPointsManager getEntryPointsManager(RefManager manager); @NotNull diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnreferencedFilter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnreferencedFilter.java index 7775b0b83d78..222e28029d80 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnreferencedFilter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnreferencedFilter.java @@ -35,7 +35,7 @@ public class UnreferencedFilter extends RefUnreachableFilter { if (!(refElement instanceof RefMethod || refElement instanceof RefClass || refElement instanceof RefField)) return 0; if (!((GlobalInspectionContextBase)myContext).isToCheckMember(refElement, myTool)) return 0; - if (refElement instanceof RefField) { + if (refElement instanceof RefField && !isExternallyReferenced(refElement)) { RefField refField = (RefField) refElement; if (refField.isUsedForReading() && !refField.isUsedForWriting()) return 1; if (refField.isUsedForWriting() && !refField.isUsedForReading()) return 1; @@ -44,4 +44,8 @@ public class UnreferencedFilter extends RefUnreachableFilter { if (refElement instanceof RefClass && ((RefClass)refElement).isAnonymous()) return 0; return -1; } + + protected static boolean isExternallyReferenced(RefElement element) { + return element.getInReferences().stream().anyMatch(reference -> reference instanceof RefFile); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java index b8e3a6043c2b..e9c7eba08da3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java @@ -13,23 +13,17 @@ import com.intellij.codeInspection.ex.JobDescriptor; import com.intellij.codeInspection.reference.*; import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspectionBase; import com.intellij.codeInspection.util.RefFilter; -import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.*; import com.intellij.psi.impl.PsiClassImplUtil; -import com.intellij.psi.search.DelegatingGlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor; -import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.PsiMethodUtil; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; @@ -283,60 +277,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { } }); - if (isAddNonJavaUsedEnabled()) { - checkForReachableRefs(globalContext); - final StrictUnreferencedFilter strictUnreferencedFilter = new StrictUnreferencedFilter(this, globalContext); - ProgressManager.getInstance().runProcess(new Runnable() { - @Override - public void run() { - final RefManager refManager = globalContext.getRefManager(); - final PsiSearchHelper helper = PsiSearchHelper.getInstance(refManager.getProject()); - refManager.iterate(new RefJavaVisitor() { - @Override - public void visitElement(@NotNull final RefEntity refEntity) { - if (refEntity instanceof RefClass && strictUnreferencedFilter.accepts((RefClass)refEntity)) { - findExternalClassReferences((RefClass)refEntity); - } - else if (refEntity instanceof RefMethod) { - RefMethod refMethod = (RefMethod)refEntity; - if (refMethod.isConstructor() && strictUnreferencedFilter.accepts(refMethod)) { - findExternalClassReferences(refMethod.getOwnerClass()); - } - } - } - - private void findExternalClassReferences(final RefClass refElement) { - final UClass psiClass = refElement.getUastElement(); - String qualifiedName = psiClass != null ? psiClass.getQualifiedName() : null; - if (qualifiedName != null) { - final GlobalSearchScope projectScope = GlobalSearchScope.projectScope(globalContext.getProject()); - final PsiNonJavaFileReferenceProcessor processor = (file, startOffset, endOffset) -> { - getEntryPointsManager(globalContext).addEntryPoint(refElement, false); - return false; - }; - final DelegatingGlobalSearchScope globalSearchScope = new DelegatingGlobalSearchScope(projectScope) { - @Override - public boolean contains(@NotNull VirtualFile file) { - return file.getFileType() != JavaFileType.INSTANCE && super.contains(file); - } - }; - - helper.processUsagesInNonJavaFiles(qualifiedName, processor, globalSearchScope); - - //references from java-like are already in graph or - //they would be checked during GlobalJavaInspectionContextImpl.performPostRunActivities - for (RefElement element : refElement.getInReferences()) { - if (!(element instanceof RefJavaElement)) { - getEntryPointsManager(globalContext).addEntryPoint(refElement, false); - } - } - } - } - }); - } - }, null); - } - myProcessedSuspicious = new HashSet<>(); myPhase = 1; } @@ -453,7 +393,8 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { @NotNull GlobalInspectionContext globalContext, @NotNull ProblemDescriptionsProcessor problemDescriptionsProcessor) { checkForReachableRefs(globalContext); - final RefFilter filter = myPhase == 1 ? new StrictUnreferencedFilter(this, globalContext) : + final boolean firstPhase = myPhase == 1; + final RefFilter filter = firstPhase ? new StrictUnreferencedFilter(this, globalContext) : new RefUnreachableFilter(this, globalContext); LOG.assertTrue(myProcessedSuspicious != null, "phase: " + myPhase); @@ -486,7 +427,14 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { public void visitMethod(@NotNull final RefMethod refMethod) { myProcessedSuspicious.add(refMethod); if (refMethod instanceof RefImplicitConstructor) { - visitClass(refMethod.getOwnerClass()); + RefClass ownerClass = refMethod.getOwnerClass(); + LOG.assertTrue(ownerClass != null); + visitClass(ownerClass); + } + else if (refMethod.isConstructor()) { + RefClass ownerClass = refMethod.getOwnerClass(); + LOG.assertTrue(ownerClass != null); + queryQualifiedNameUsages(ownerClass); } else { UMethod uMethod = (UMethod)refMethod.getUastElement(); @@ -515,9 +463,31 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { getEntryPointsManager(globalContext).addEntryPoint(refClass, false); return false; }); + + queryQualifiedNameUsages(refClass); requestAdded[0] = true; } } + + public void queryQualifiedNameUsages(@NotNull RefClass refClass) { + if (firstPhase && isAddNonJavaUsedEnabled()) { + globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueQualifiedNameOccurrencesProcessor(refClass, () -> { + EntryPointsManager entryPointsManager = getEntryPointsManager(globalContext); + entryPointsManager.addEntryPoint(refClass, false); + for (RefMethod constructor : refClass.getConstructors()) { + entryPointsManager.addEntryPoint(constructor, false); + } + }); + + //references from java-like are already in graph or + //they would be checked during GlobalJavaInspectionContextImpl.performPostRunActivities + for (RefElement element : refClass.getInReferences()) { + if (!(element instanceof RefJavaElement)) { + getEntryPointsManager(globalContext).addEntryPoint(refElement, false); + } + } + } + } }); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java index 40e67d405bce..aeee6f796018 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java @@ -5,7 +5,6 @@ package com.intellij.codeInspection.reference; import com.intellij.codeInsight.TestFrameworks; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; -import com.intellij.lang.jvm.JvmClass; import com.intellij.lang.jvm.JvmField; import com.intellij.lang.jvm.JvmMetaLanguage; import com.intellij.lang.jvm.JvmModifier; @@ -274,9 +273,9 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass { @NotNull @Override public String getQualifiedName() { - final JvmClass jvmClass = getUastElement(); - if (jvmClass == null) return super.getQualifiedName(); - final String qName = jvmClass.getQualifiedName(); + final UClass uClass = getUastElement(); + if (uClass == null) return super.getQualifiedName(); + final String qName = uClass.getQualifiedName(); if (qName == null) return super.getQualifiedName(); return qName; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java index 8ecf176b3e95..1822ec9ce04a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java @@ -34,7 +34,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.uast.*; import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor; -import javax.swing.*; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -444,6 +443,11 @@ public class RefJavaManagerImpl extends RefJavaManager { .filter(Objects::nonNull); } + @Override + public void markExternalReferencesProcessed(@NotNull RefElement file) { + getEntryPointsManager().addEntryPoint(file, false); + } + @Override public EntryPointsManager getEntryPointsManager() { EntryPointsManager entryPointsManager = myEntryPointsManager; diff --git a/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java b/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java index 3ce122e4022d..32cbb861e098 100644 --- a/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 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. +// 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.codeInspection.ex; @@ -15,6 +15,7 @@ import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; @@ -43,10 +44,8 @@ import org.jetbrains.uast.UClass; import org.jetbrains.uast.UMethod; import org.jetbrains.uast.UastContextKt; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext { private static final Logger LOG = Logger.getInstance(GlobalJavaInspectionContextImpl.class); @@ -56,6 +55,7 @@ public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext private Map> myMethodUsagesRequests; private Map> myFieldUsagesRequests; private Map> myClassUsagesRequests; + private Map> myQNameUsagesRequests; @Override @@ -89,6 +89,12 @@ public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext enqueueRequestImpl(refMethod, myMethodUsagesRequests, p); } + @Override + public void enqueueQualifiedNameOccurrencesProcessor(RefClass refClass, Runnable c) { + if (myQNameUsagesRequests == null) myQNameUsagesRequests = new THashMap<>(); + enqueueRequestImpl(refClass, myQNameUsagesRequests, c); + } + @Override public EntryPointsManager getEntryPointsManager(final RefManager manager) { return manager.getExtension(RefJavaManager.MANAGER).getEntryPointsManager(); @@ -162,7 +168,7 @@ public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext return anyModuleUsesProjectSdk && !anyModuleAcceptsSdk; } - private static void enqueueRequestImpl(RefElement refElement, Map> requestMap, T processor) { + private static void enqueueRequestImpl(RefElement refElement, Map> requestMap, T processor) { List requests = requestMap.computeIfAbsent(refElement.getPointer(), __ -> new ArrayList<>()); requests.add(processor); } @@ -289,6 +295,41 @@ public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext myMethodUsagesRequests = null; } + + if (myQNameUsagesRequests != null) { + PsiSearchHelper helper = PsiSearchHelper.getInstance(refManager.getProject()); + RefJavaManager javaManager = refManager.getExtension(RefJavaManager.MANAGER); + List sortedIDs = getSortedIDs(myQNameUsagesRequests); + for (SmartPsiElementPointer id : sortedIDs) { + final UClass uClass = ReadAction.compute(() -> UastContextKt.toUElement(dereferenceInReadAction(id), UClass.class)); + String qualifiedName = uClass != null ? uClass.getQualifiedName() : null; + if (qualifiedName != null) { + List callbacks = myQNameUsagesRequests.get(id); + final GlobalSearchScope projectScope = GlobalSearchScope.projectScope(context.getProject()); + final PsiNonJavaFileReferenceProcessor processor = (file, startOffset, endOffset) -> { + for (Runnable callback : callbacks) { + callback.run(); + } + return false; + }; + + final DelegatingGlobalSearchScope globalSearchScope = new DelegatingGlobalSearchScope(projectScope) { + Set fileTypes = javaManager.getLanguages().stream().map(l -> l.getAssociatedFileType()).collect(Collectors.toSet()); + boolean ignoreExpectedXml = ApplicationManager.getApplication().isUnitTestMode(); + + @Override + public boolean contains(@NotNull VirtualFile file) { + return !fileTypes.contains(file.getFileType()) && (!ignoreExpectedXml || !file.getName().equals("expected.xml")) && super.contains(file); + } + }; + + helper.processUsagesInNonJavaFiles(qualifiedName, processor, globalSearchScope); + } + } + + + myQNameUsagesRequests = null; + } } private static String getClassPresentableName(@NotNull UClass uClass) { diff --git a/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/expected.xml b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/expected.xml new file mode 100644 index 000000000000..79e1a0fd0a57 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/expected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/GroovyUsage.groovy b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/GroovyUsage.groovy new file mode 100644 index 000000000000..e10ff97c8d97 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/GroovyUsage.groovy @@ -0,0 +1,5 @@ +class MyGroovyUsage { + static void "to use java"(Util util) { + util.foo + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/Test.java b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/Test.java new file mode 100644 index 000000000000..a969804b3930 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/fieldReferenceFromExternalGroovy/src/Test.java @@ -0,0 +1,3 @@ +class Util { + String foo; +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/expected.xml b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/expected.xml new file mode 100644 index 000000000000..79e1a0fd0a57 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/expected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/GroovyUsage.groovy b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/GroovyUsage.groovy new file mode 100644 index 000000000000..e135b0bfc9d1 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/GroovyUsage.groovy @@ -0,0 +1,5 @@ +class MyGroovyUsage { + static void "to use java"() { + Util.FOO + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/Test.java b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/Test.java new file mode 100644 index 000000000000..060b235c6810 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticFieldReferenceFromExternalGroovy/src/Test.java @@ -0,0 +1,3 @@ +class Util { + public final static String FOO = "foo"; +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/expected.xml b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/expected.xml new file mode 100644 index 000000000000..79e1a0fd0a57 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/expected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/GroovyUsage.groovy b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/GroovyUsage.groovy new file mode 100644 index 000000000000..fc885bb02232 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/GroovyUsage.groovy @@ -0,0 +1,5 @@ +class MyGroovyUsage { + static void "to use java"() { + Util.foo() + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/Test.java b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/Test.java new file mode 100644 index 000000000000..a583553a1f0e --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/staticMethodReferenceFromGroovy/src/Test.java @@ -0,0 +1,3 @@ +class Util { + static void foo() { } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/UnusedDeclarationTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/UnusedDeclarationTest.java index b1c414df1b33..4c4667e44167 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/UnusedDeclarationTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/UnusedDeclarationTest.java @@ -207,6 +207,18 @@ public class UnusedDeclarationTest extends AbstractUnusedDeclarationTest { doTest(); } + public void testStaticMethodReferenceFromGroovy() { + doTest(); + } + + public void testStaticFieldReferenceFromExternalGroovy() { + doTest(); + } + + public void testFieldReferenceFromExternalGroovy() { + doTest(); + } + public void testStaticImport() { doTest(); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/lang/RefManagerExtension.java b/platform/analysis-api/src/com/intellij/codeInspection/lang/RefManagerExtension.java index 68f203697b83..31e9e349948e 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/lang/RefManagerExtension.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/lang/RefManagerExtension.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 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. +// 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.codeInspection.lang; @@ -79,4 +79,8 @@ public interface RefManagerExtension { default Stream extractExternalFileImplicitReferences(@NotNull PsiFile psiFile) { return Stream.empty(); } + + default void markExternalReferencesProcessed(@NotNull RefElement file) { + + } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index 6e45f2268cd9..505403cd69c7 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 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. +// 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.codeInspection.reference; @@ -467,50 +467,49 @@ public class RefManagerImpl extends RefManager { else if (processExternalElements) { PsiFile file = element.getContainingFile(); if (file != null) { - boolean referencesProcessed = false; - for (RefManagerExtension managerExtension : myExtensions.values()) { - if (managerExtension.shouldProcessExternalFile(file)) { - RefElement refFile = getReference(file); - LOG.assertTrue(refFile != null, file); - if (!referencesProcessed) { - referencesProcessed = true; - for (PsiReference reference : element.getReferences()) { - PsiElement resolve = reference.resolve(); - if (resolve != null) { - fireNodeMarkedReferenced(resolve, file); - RefElement refWhat = getReference(resolve); - if (refWhat == null) { - PsiFile targetContainingFile = resolve.getContainingFile(); - //no logic to distinguish different elements in the file anyway - if (file == targetContainingFile) continue; - refWhat = getReference(targetContainingFile); - } + RefManagerExtension externalFileManagerExtension = myExtensions.values().stream().filter(ex -> ex.shouldProcessExternalFile(file)).findFirst().orElse(null); + if (externalFileManagerExtension == null) { + if (element instanceof PsiFile) { + VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element); + if (virtualFile instanceof VirtualFileWithId) { + registerUnprocessed((VirtualFileWithId)virtualFile); + } + } + } else { + RefElement refFile = getReference(file); + LOG.assertTrue(refFile != null, file); + for (PsiReference reference : element.getReferences()) { + PsiElement resolve = reference.resolve(); + if (resolve != null) { + fireNodeMarkedReferenced(resolve, file); + RefElement refWhat = getReference(resolve); + if (refWhat == null) { + PsiFile targetContainingFile = resolve.getContainingFile(); + //no logic to distinguish different elements in the file anyway + if (file == targetContainingFile) continue; + refWhat = getReference(targetContainingFile); + } - if (refWhat != null) { - ((RefElementImpl)refWhat).addInReference(refFile); - ((RefElementImpl)refFile).addOutReference(refWhat); - } - } + if (refWhat != null) { + ((RefElementImpl)refWhat).addInReference(refFile); + ((RefElementImpl)refFile).addOutReference(refWhat); } } - - Stream implicitRefs = managerExtension.extractExternalFileImplicitReferences(file); - implicitRefs.forEach(e -> { - RefElement superClassReference = getReference(e); - if (superClassReference != null) { - //in case of implicit inheritance, e.g. GroovyObject - //= no explicit reference is provided, dependency on groovy library could be treated as redundant though it is not - //inReference is not important in this case - ((RefElementImpl)refFile).addOutReference(superClassReference); - } - }); } - } - if (!referencesProcessed && element instanceof PsiFile) { - VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element); - if (virtualFile instanceof VirtualFileWithId) { - registerUnprocessed((VirtualFileWithId)virtualFile); + Stream implicitRefs = externalFileManagerExtension.extractExternalFileImplicitReferences(file); + implicitRefs.forEach(e -> { + RefElement superClassReference = getReference(e); + if (superClassReference != null) { + //in case of implicit inheritance, e.g. GroovyObject + //= no explicit reference is provided, dependency on groovy library could be treated as redundant though it is not + //inReference is not important in this case + ((RefElementImpl)refFile).addOutReference(superClassReference); + } + }); + + if (element instanceof PsiFile) { + externalFileManagerExtension.markExternalReferencesProcessed(refFile); } } }