diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index ebc571aa9987..8dda9b8170c3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.javac.ast; +import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; import com.sun.source.tree.Tree; import com.sun.source.util.JavacTask; @@ -24,6 +25,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.ClientCodeException; import com.sun.tools.javac.util.Name; +import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar; import org.jetbrains.jps.javac.ast.api.JavacRefSymbol; @@ -33,9 +35,7 @@ import javax.lang.model.element.TypeElement; import javax.tools.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; final class JavacReferenceCollectorListener implements TaskListener { private final JavacFileReferencesRegistrar[] myFullASTListeners; @@ -44,10 +44,7 @@ final class JavacReferenceCollectorListener implements TaskListener { private Name myAsterisk; - private int myRemainDeclarations; - private JCTree.JCCompilationUnit myCurrentCompilationUnit; - private Set myCollectedReferences; - private List myCollectedDefinitions; + private final Map myIncompletelyProcessedFiles = new THashMap(10); static void installOn(JavaCompiler.CompilationTask task, JavacFileReferencesRegistrar[] fullASTListenerArray, @@ -87,57 +84,83 @@ final class JavacReferenceCollectorListener implements TaskListener { try { if (e.getKind() == TaskEvent.Kind.ANALYZE) { // javac creates an event on each processed top level declared class not file - if (myCurrentCompilationUnit != e.getCompilationUnit()) { - myCurrentCompilationUnit = (JCTree.JCCompilationUnit)e.getCompilationUnit(); - myCollectedDefinitions = new ArrayList(); - myCollectedReferences = new THashSet(); - myRemainDeclarations = myCurrentCompilationUnit.getTypeDecls().size() - 1; - scanImports(myCurrentCompilationUnit, myCollectedReferences); - for(JavacFileReferencesRegistrar r: myOnlyImportsListeners) { - r.registerFile(e.getSourceFile().getName(), myCollectedReferences, myCollectedDefinitions); - } - } - else { - myRemainDeclarations--; + final JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)e.getCompilationUnit(); + final String fileName = e.getSourceFile().getName(); + final List declarations = unit.getTypeDecls(); + final int size = declarations.size(); + + boolean isFileDataComplete = true; + boolean submitImportsOnlyData = true; + JCTree declarationToProcess = null; + final Set collectedReferences; + final List collectedDefinitions; + final TypeElement analyzedElement = e.getTypeElement(); + + switch (size) { + case 0: + collectedReferences = IncompletelyProcessedFile.createReferenceHolder(); + collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder(); + break; + case 1: + collectedReferences = IncompletelyProcessedFile.createReferenceHolder(); + collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder(); + declarationToProcess = declarations.get(0); + break; + default: + IncompletelyProcessedFile incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName); + if (incompletelyProcessedFile == null) { + myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile = new IncompletelyProcessedFile(size)); + } else { + submitImportsOnlyData = false; + } + + if (--incompletelyProcessedFile.remainDeclarations == 0) { + myIncompletelyProcessedFiles.remove(fileName); + } else { + isFileDataComplete = false; + } + collectedReferences = incompletelyProcessedFile.collectedReferences; + collectedDefinitions = incompletelyProcessedFile.collectedDefinitions; + + for (JCTree declaration : declarations) { + if (declaration.type != null && declaration.type.tsym == analyzedElement) { + declarationToProcess = declaration; + break; + } + } + + if (declarationToProcess == null) throw new IllegalStateException("Can't find tree for " + analyzedElement.getQualifiedName()); } + if (submitImportsOnlyData) { + scanImports(unit, collectedReferences); + for (JavacFileReferencesRegistrar r : myOnlyImportsListeners) { + r.registerFile(fileName, collectedReferences, collectedDefinitions); + } + } + if (myFullASTListeners.length == 0) return; JavacTreeScannerSink sink = new JavacTreeScannerSink() { @Override public void sinkReference(JavacRefSymbol ref) { - myCollectedReferences.add(ref); + collectedReferences.add(ref); } @Override public void sinkDeclaration(JavacRefSymbol def) { - myCollectedDefinitions.add(def); + collectedDefinitions.add(def); } }; + myAstScanner.scan(declarationToProcess, sink); - if (myFullASTListeners.length != 0) { - TypeElement analyzedElement = e.getTypeElement(); - for (JCTree tree : myCurrentCompilationUnit.getTypeDecls()) { - if (tree.type != null && tree.type.tsym == analyzedElement) { - myAstScanner.scan(tree, sink); - } + if (isFileDataComplete) { + for (JCTree.JCAnnotation annotation : unit.getPackageAnnotations()) { + myAstScanner.scan(annotation, sink); + } + + for (JavacFileReferencesRegistrar r : myFullASTListeners) { + r.registerFile(e.getSourceFile().getName(), collectedReferences, collectedDefinitions); } } - - if (myRemainDeclarations == 0) { - if (myFullASTListeners.length != 0) { - for (JCTree.JCAnnotation annotation : myCurrentCompilationUnit.getPackageAnnotations()) { - myAstScanner.scan(annotation, sink); - } - } - - for(JavacFileReferencesRegistrar r: myFullASTListeners) { - r.registerFile(e.getSourceFile().getName(), myCollectedReferences, myCollectedDefinitions); - } - - myCurrentCompilationUnit = null; - myCollectedDefinitions = null; - myCollectedReferences = null; - } - } } catch (Exception ex) { @@ -186,4 +209,22 @@ final class JavacReferenceCollectorListener implements TaskListener { collector.add(new JavacRefSymbol(symbol, Tree.Kind.IMPORT)); } } + + private static class IncompletelyProcessedFile { + private final Set collectedReferences = createReferenceHolder(); + private final List collectedDefinitions = createDefinitionHolder(); + private int remainDeclarations; + + private IncompletelyProcessedFile(int remainDeclarations) { + this.remainDeclarations = remainDeclarations; + } + + private static Set createReferenceHolder() { + return new THashSet(); + } + + private static List createDefinitionHolder() { + return new ArrayList(); + } + } } diff --git a/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Bar.java b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Bar.java new file mode 100644 index 000000000000..674500385a39 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Bar.java @@ -0,0 +1,13 @@ +class Bar0 { + void mm() { + Boo1 b = new Boo1(); + b.mm(); + } +} + +class Bar1 { + void mm() { + Foo0 f = new Foo0(); + f.mm(); + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Boo.java b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Boo.java new file mode 100644 index 000000000000..eb89a76ba9e6 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Boo.java @@ -0,0 +1,13 @@ +class Boo0 { + void mm() { + Foo1 f = new Foo1(); + f.mm(); + } +} + +class Boo1 { + void mm() { + Bar1 b = new Bar1(); + b.mm(); + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Foo.java b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Foo.java new file mode 100644 index 000000000000..8885d0738b16 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/Foo.java @@ -0,0 +1,13 @@ +class Foo0 { + void mm() { + Boo0 b = new Boo0(); + b.mm(); + } +} + +class Foo1 { + void mm() { + Bar0 b = new Bar0(); + b.mm(); + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/initialIndex.txt new file mode 100644 index 000000000000..ad182082e3d5 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/multiFileMultiUnitCompilation/initialIndex.txt @@ -0,0 +1,31 @@ +Backward Hierarchy: +java.lang.Object -> Bar0 Bar1 Boo0 Boo1 Foo0 Foo1 + +Backward References: +Bar0 in Bar Foo +Bar0.(0) in Bar +Bar0.mm(0) in Bar Foo +Bar1 in Bar Boo +Bar1.(0) in Bar +Bar1.mm(0) in Bar Boo +Boo0 in Boo Foo +Boo0.(0) in Boo +Boo0.mm(0) in Boo Foo +Boo1 in Bar Boo +Boo1.(0) in Boo +Boo1.mm(0) in Bar Boo +Foo0 in Bar Foo +Foo0.(0) in Foo +Foo0.mm(0) in Bar Foo +Foo1 in Boo Foo +Foo1.(0) in Foo +Foo1.mm(0) in Boo Foo +java.lang.Object.(0) in Bar Boo Foo + +Class Definitions: +Bar0 in Bar +Bar1 in Bar +Boo0 in Boo +Boo1 in Boo +Foo0 in Foo +Foo1 in Foo \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt index dcadcdbf1d34..ad92bd979c2f 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt @@ -94,6 +94,10 @@ class ReferenceIndexTest : ReferenceIndexTestBase() { assertIndexOnRebuild("Foo.java") } + fun testMultiFileMultiUnitCompilation() { + assertIndexOnRebuild("Foo.java", "Boo.java", "Bar.java") + } + fun testNestedClasses() { assertIndexOnRebuild("Foo.java") }