javac ast: weigh completion for constructor

This commit is contained in:
Dmitry Batkovich
2017-04-03 15:53:26 +03:00
parent 6ede48ef08
commit 7bb56d7bac
15 changed files with 187 additions and 85 deletions
@@ -63,7 +63,7 @@ class CompilerHierarchyInfoImpl implements CompilerDirectHierarchyInfo {
@NotNull
public Stream<PsiElement> getHierarchyChildren() {
PsiManager psiManager = PsiManager.getInstance(myProject);
final LanguageLightRefAdapter adapter = ObjectUtils.notNull(CompilerReferenceServiceImpl.findAdapterForFileType(mySearchFileType));
final LanguageLightRefAdapter adapter = ObjectUtils.notNull(LanguageLightRefAdapter.findAdapter(mySearchFileType));
return myCandidatePerFile
.entrySet()
.stream()
@@ -30,9 +30,9 @@ import gnu.trove.THashSet;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.backwardRefs.NameEnumerator;
import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex;
import org.jetbrains.jps.backwardRefs.LightRef;
import org.jetbrains.jps.backwardRefs.NameEnumerator;
import org.jetbrains.jps.backwardRefs.index.CompilerIndices;
import java.io.File;
@@ -80,7 +80,7 @@ class CompilerReferenceReader {
@NotNull FileType fileType,
@NotNull CompilerHierarchySearchType searchType) throws StorageException {
GlobalSearchScope effectiveSearchScope = GlobalSearchScope.notScope(dirtyScope).intersectWith(searchScope);
LanguageLightRefAdapter adapter = CompilerReferenceServiceImpl.findAdapterForFileType(fileType);
LanguageLightRefAdapter adapter = LanguageLightRefAdapter.findAdapter(fileType);
LOG.assertTrue(adapter != null, "adapter is null for file type: " + fileType);
Class<? extends LightRef> requiredLightRefClass = searchType.getRequiredClass(adapter);
@@ -97,6 +97,22 @@ class CompilerReferenceReader {
return candidatesPerFile.isEmpty() ? Collections.emptyMap() : candidatesPerFile;
}
@Nullable
Integer getAnonymousCount(@NotNull LightRef.LightClassHierarchyElementDef classDef) throws StorageException {
if (hasMultipleDefinitions(classDef)) {
return null;
}
final int[] count = {0};
myIndex.get(CompilerIndices.BACK_HIERARCHY).getData(classDef).forEach(new ValueContainer.ContainerAction<Collection<LightRef>>() {
@Override
public boolean perform(int id, Collection<LightRef> value) {
count[0] += value.size();
return true;
}
});
return count[0];
}
@NotNull
NameEnumerator getNameEnumerator() {
return myIndex.getByteSeqEum();
@@ -110,12 +126,16 @@ class CompilerReferenceReader {
}
public int getOccurrenceCount(@NotNull LightRef element) throws StorageException {
final ValueContainer.ValueIterator<Integer> occurrenceIt = myIndex.get(CompilerIndices.BACK_USAGES).getData(element).getValueIterator();
int occurrenceCount = 0;
while (occurrenceIt.hasNext()) {
occurrenceCount += occurrenceIt.next();
}
return occurrenceCount;
int[] result = new int[]{0};
myIndex.get(CompilerIndices.BACK_USAGES).getData(element).forEach(
new ValueContainer.ContainerAction<Integer>() {
@Override
public boolean perform(int id, Integer value) {
result[0] += value;
return true;
}
});
return result[0];
}
static boolean exists(Project project) {
@@ -172,14 +192,7 @@ class CompilerReferenceReader {
LightRef.NamedLightRef curClass = q.pullFirst();
if (result.add(curClass)) {
if (checkBaseClassAmbiguity || curClass != hierarchyElement) {
DefCount count = getDefinitionCount(curClass);
if (count == DefCount.NONE) {
//diagnostic
String baseHierarchyElement = getNameEnumerator().getName(hierarchyElement.getName());
String curHierarchyElement = getNameEnumerator().getName(curClass.getName());
LOG.error("Can't get definition files for: " + curHierarchyElement + " base class: " + baseHierarchyElement);
}
if (count != DefCount.ONE) {
if (hasMultipleDefinitions(curClass)) {
return null;
}
}
@@ -197,8 +210,7 @@ class CompilerReferenceReader {
}
private enum DefCount { NONE, ONE, MANY}
@NotNull
private DefCount getDefinitionCount(LightRef def) throws StorageException {
private boolean hasMultipleDefinitions(LightRef.NamedLightRef def) throws StorageException {
DefCount[] result = new DefCount[]{DefCount.NONE};
myIndex.get(CompilerIndices.BACK_CLASS_DEF).getData(def).forEach(new ValueContainer.ContainerAction<Void>() {
@Override
@@ -214,6 +226,11 @@ class CompilerReferenceReader {
return false;
}
});
return result[0];
if (result[0] == DefCount.MANY) {
//diagnostic
String baseHierarchyElement = getNameEnumerator().getName(def.getName());
LOG.error("Can't get definition files for: " + baseHierarchyElement);
}
return result[0] == DefCount.MANY;
}
}
@@ -196,11 +196,11 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
@Nullable
@Override
public Integer getCompileTimeOccurrenceCount(@NotNull PsiElement element) {
public Integer getCompileTimeOccurrenceCount(@NotNull PsiElement element, boolean isConstructorSuggestion) {
if (!isServiceEnabledFor(element)) return null;
try {
return CachedValuesManager.getCachedValue(element,
() -> CachedValueProvider.Result.create(calculateOccurrenceCount(element),
() -> CachedValueProvider.Result.create(calculateOccurrenceCount(element, isConstructorSuggestion),
PsiModificationTracker.MODIFICATION_COUNT,
this));
}
@@ -209,19 +209,42 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
}
}
private Integer calculateOccurrenceCount(@NotNull PsiElement element) {
final CompilerElementInfo searchElementInfo = asCompilerElements(element, false);
private Integer calculateOccurrenceCount(@NotNull PsiElement element, boolean isConstructorSuggestion) {
LanguageLightRefAdapter adapter = null;
if (isConstructorSuggestion) {
adapter = ReadAction.compute(() -> LanguageLightRefAdapter.findAdapter(element));
if (!adapter.isClass(element)) {
return null;
}
}
final CompilerElementInfo searchElementInfo = asCompilerElements(element, false, false);
if (searchElementInfo == null) return null;
myReadDataLock.lock();
try {
if (myReader == null) return null;
try {
return myReader.getOccurrenceCount(searchElementInfo.searchElements[0]);
if (isConstructorSuggestion) {
int constructorOccurrences = 0;
for (PsiElement constructor : adapter.getInstantiableConstructors(element)) {
final LightRef lightConstructor = adapter.asLightUsage(constructor, myReader.getNameEnumerator());
if (lightConstructor != null) {
constructorOccurrences += myReader.getOccurrenceCount(lightConstructor);
}
}
final Integer anonymousCount = myReader.getAnonymousCount((LightRef.LightClassHierarchyElementDef)searchElementInfo.searchElements[0]);
return anonymousCount == null ? constructorOccurrences : (constructorOccurrences + anonymousCount);
} else {
return myReader.getOccurrenceCount(searchElementInfo.searchElements[0]);
}
}
catch (StorageException e) {
throw new RuntimeException(e);
}
catch (IOException e) {
throw new RuntimeException(e);
}
} finally {
myReadDataLock.unlock();
}
@@ -281,7 +304,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
@NotNull GlobalSearchScope useScope,
@NotNull FileType searchFileType,
@NotNull CompilerHierarchySearchType searchType) {
final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, false);
final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, false, true);
if (searchElementInfo == null) return null;
LightRef searchElement = searchElementInfo.searchElements[0];
@@ -311,7 +334,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
@Nullable
private TIntHashSet getReferentFileIds(@NotNull PsiElement element) {
final CompilerElementInfo compilerElementInfo = asCompilerElements(element, true);
final CompilerElementInfo compilerElementInfo = asCompilerElements(element, true, true);
if (compilerElementInfo == null) return null;
myReadDataLock.lock();
@@ -336,17 +359,21 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
}
@Nullable
private CompilerElementInfo asCompilerElements(@NotNull PsiElement psiElement, boolean buildHierarchyForLibraryElements) {
private CompilerElementInfo asCompilerElements(@NotNull PsiElement psiElement,
boolean buildHierarchyForLibraryElements,
boolean checkNotDirty) {
myReadDataLock.lock();
try {
if (myReader == null) return null;
VirtualFile file = PsiUtilCore.getVirtualFile(psiElement);
if (file == null) return null;
ElementPlace place = ElementPlace.get(file, myProjectFileIndex);
if (place == null || (place == ElementPlace.SRC && myDirtyScopeHolder.contains(file))) {
return null;
if (checkNotDirty) {
if (place == null || (place == ElementPlace.SRC && myDirtyScopeHolder.contains(file))) {
return null;
}
}
final LanguageLightRefAdapter adapter = findAdapterForFileType(file.getFileType());
final LanguageLightRefAdapter adapter = LanguageLightRefAdapter.findAdapter(file);
if (adapter == null) return null;
final LightRef ref = adapter.asLightUsage(psiElement, myReader.getNameEnumerator());
if (ref == null) return null;
@@ -410,15 +437,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
return myProject;
}
@Nullable
static LanguageLightRefAdapter findAdapterForFileType(@NotNull FileType fileType) {
for (LanguageLightRefAdapter adapter : LanguageLightRefAdapter.INSTANCES) {
if (adapter.getFileTypes().contains(fileType)) {
return adapter;
}
}
return null;
}
private static void executeOnBuildThread(Runnable compilationFinished) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
@@ -30,13 +30,14 @@ import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.backwardRefs.NameEnumerator;
import org.jetbrains.jps.backwardRefs.LightRef;
import org.jetbrains.jps.backwardRefs.NameEnumerator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
public class JavaLightUsageAdapter implements LanguageLightRefAdapter {
@NotNull
@@ -139,6 +140,25 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter {
return JavaCompilerElementRetriever.retrieveFunExpressionsByIndices(requiredIndices, file);
}
@Override
public boolean isClass(@NotNull PsiElement element) {
return element instanceof PsiClass;
}
@NotNull
@Override
public PsiElement[] getInstantiableConstructors(@NotNull PsiElement aClass) {
if (!(aClass instanceof PsiClass)) {
throw new IllegalArgumentException("parameter should be an instance of PsiClass: " + aClass);
}
PsiClass theClass = (PsiClass)aClass;
if (theClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
return PsiElement.EMPTY_ARRAY;
}
return Stream.of(theClass.getConstructors()).filter(c -> !c.hasModifierProperty(PsiModifier.PRIVATE)).toArray(s -> PsiElement.ARRAY_FACTORY.create(s));
}
@Override
public boolean isDirectInheritor(PsiElement candidate, PsiNamedElement baseClass) {
return ((PsiClass) candidate).isInheritor((PsiClass) baseClass, false);
@@ -16,10 +16,12 @@
package com.intellij.compiler.backwardRefs;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.backwardRefs.LightRef;
@@ -35,6 +37,28 @@ import java.util.Set;
public interface LanguageLightRefAdapter {
LanguageLightRefAdapter[] INSTANCES = new LanguageLightRefAdapter[]{new JavaLightUsageAdapter()};
@Nullable
static LanguageLightRefAdapter findAdapter(@NotNull VirtualFile file) {
final FileType fileType = file.getFileType();
return findAdapter(fileType);
}
@Nullable
static LanguageLightRefAdapter findAdapter(@NotNull FileType fileType) {
for (LanguageLightRefAdapter adapter : INSTANCES) {
if (adapter.getFileTypes().contains(fileType)) {
return adapter;
}
}
return null;
}
@Nullable
static LanguageLightRefAdapter findAdapter(@NotNull PsiElement element) {
final VirtualFile file = PsiUtilCore.getVirtualFile(element);
return file == null ? null : findAdapter(file);
}
@NotNull
Set<FileType> getFileTypes();
@@ -88,5 +112,10 @@ public interface LanguageLightRefAdapter {
PsiElement[] findFunExpressionsInFile(@NotNull Integer[] indices,
@NotNull PsiFileWithStubSupport file);
boolean isClass(@NotNull PsiElement element);
@NotNull
PsiElement[] getInstantiableConstructors(@NotNull PsiElement aClass);
boolean isDirectInheritor(PsiElement candidate, PsiNamedElement baseClass);
}
@@ -18,15 +18,11 @@ package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.compiler.CompilerReferenceService;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.LongAdder;
class PreferMostUsedWeigher extends LookupElementWeigher {
private final CompilerReferenceService myCompilerReferenceService;
private final boolean myConstructorSuggestion;
@@ -37,49 +33,23 @@ class PreferMostUsedWeigher extends LookupElementWeigher {
myConstructorSuggestion = constructorSuggestion;
}
// optimization: do not event create weigher if compiler indices aren't available for now
// optimization: do not even create weigher if compiler indices aren't available for now
@Nullable
static PreferMostUsedWeigher create(@NotNull PsiElement position) {
final CompilerReferenceService service = CompilerReferenceService.getInstance(position.getProject());
return service.isActive() ? new PreferMostUsedWeigher(service, JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) : null;
}
final static LongAdder l = new LongAdder();
@Nullable
@Override
public Integer weigh(@NotNull LookupElement element) {
final long ms = System.currentTimeMillis();
final PsiElement psi = ObjectUtils.tryCast(element.getObject(), PsiElement.class);
final Integer res;
if (psi == null || !(psi.isPhysical())) {
res = null;
return null;
}
else {
if (myConstructorSuggestion && psi instanceof PsiClass) {
int _res = 0;
boolean add = true;
for (PsiMethod method : ((PsiClass)psi).getConstructors()) {
final Integer count = myCompilerReferenceService.getCompileTimeOccurrenceCount(method);
if (count != null) {
_res += count;
} else {
add = false;
break;
}
}
if (add) {
res = _res;
} else {
res = null;
}
}
else {
res = myCompilerReferenceService.getCompileTimeOccurrenceCount(psi);
}
final Integer occurrenceCount = myCompilerReferenceService.getCompileTimeOccurrenceCount(psi, myConstructorSuggestion);
return occurrenceCount == null ? null : - occurrenceCount;
}
l.add(System.currentTimeMillis() - ms);
System.out.println("overhead " + l.longValue() + " for " + element);
return res;
}
}
@@ -74,7 +74,7 @@ public abstract class CompilerReferenceService extends AbstractProjectComponent
* @return count of references that were observed on compile-time (in the last compilation) or null if given element is not supported for some reason.
*/
@Nullable
public abstract Integer getCompileTimeOccurrenceCount(@NotNull PsiElement element);
public abstract Integer getCompileTimeOccurrenceCount(@NotNull PsiElement element, boolean isConstructorCompletion);
public abstract boolean isActive();
@@ -58,6 +58,17 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
return null;
}
@Override
public Tree visitNewClass(NewClassTree node, JavacReferenceCollectorListener.ReferenceCollector collector) {
if (node.getClassBody() == null) {
final Element element = collector.getReferencedElement(node);
if (element != null) {
collector.sinkReference(collector.asJavacRef(element));
}
}
return super.visitNewClass(node, collector);
}
@Override
public Tree visitVariable(VariableTree node, JavacReferenceCollectorListener.ReferenceCollector refCollector) {
final Element element = refCollector.getReferencedElement(node);
@@ -0,0 +1,14 @@
import java.util.*;
class Foo {
void m() {
List l1 = new ArrayList();
List l2 = new ArrayList();
List l3 = new ArrayList();
List l4 = new LinkedList();
List l5 = new LinkedList();
}
}
@@ -0,0 +1,16 @@
Backward Hierarchy:
java.lang.Object -> Foo
Backward References:
Foo in Foo occurrences = 1
Foo.<init>(0) in Foo occurrences = 1
Foo.m(0) in Foo occurrences = 1
java.lang.Object.<init>(0) in Foo occurrences = 1
java.util.ArrayList in Foo occurrences = 3
java.util.ArrayList.<init>(0) in Foo occurrences = 3
java.util.LinkedList in Foo occurrences = 2
java.util.LinkedList.<init>(0) in Foo occurrences = 2
java.util.List in Foo occurrences = 5
Class Definitions:
Foo in Foo
@@ -11,6 +11,7 @@ java.lang.Object.<init>(0) in Bar occurrences = 1
java.lang.System in Bar occurrences = 1
java.lang.System.out in Bar occurrences = 1
java.util.ArrayList in Bar occurrences = 1
java.util.ArrayList.<init>(0) in Bar occurrences = 1
Class Definitions:
Bar in Bar
@@ -3,10 +3,10 @@ java.lang.Object -> Bar Foo
Backward References:
Bar in Bar Foo occurrences = 3
Bar.<init>(0) in Bar occurrences = 1
Bar.<init>(0) in Bar Foo occurrences = 2
Bar.m(0) in Bar occurrences = 1
Foo in Bar Foo occurrences = 3
Foo.<init>(0) in Foo occurrences = 1
Foo.<init>(0) in Bar Foo occurrences = 2
Foo.m(0) in Foo occurrences = 1
java.lang.Object.<init>(0) in Bar Foo occurrences = 2
@@ -7,22 +7,22 @@ java.lang.Object -> Foo0
Backward References:
Bar0 in Bar Boo Foo occurrences = 4
Bar0.<init>(0) in Bar Boo occurrences = 2
Bar0.<init>(0) in Bar Boo Foo occurrences = 3
Bar0.mm(0) in Bar Foo occurrences = 2
Bar1 in Bar Boo occurrences = 3
Bar1.<init>(0) in Bar occurrences = 1
Bar1.<init>(0) in Bar Boo occurrences = 2
Bar1.mm(0) in Bar Boo occurrences = 2
Boo0 in Bar Boo Foo occurrences = 4
Boo0.<init>(0) in Bar Boo occurrences = 2
Boo0.<init>(0) in Bar Boo Foo occurrences = 3
Boo0.mm(0) in Boo Foo occurrences = 2
Boo1 in Bar Boo occurrences = 3
Boo1.<init>(0) in Boo occurrences = 1
Boo1.<init>(0) in Bar Boo occurrences = 2
Boo1.mm(0) in Bar Boo occurrences = 2
Foo0 in Bar Foo occurrences = 5
Foo0.<init>(0) in Bar Foo occurrences = 3
Foo0.<init>(0) in Bar Foo occurrences = 4
Foo0.mm(0) in Bar Foo occurrences = 2
Foo1 in Boo Foo occurrences = 4
Foo1.<init>(0) in Boo Foo occurrences = 2
Foo1.<init>(0) in Boo Foo occurrences = 3
Foo1.mm(0) in Boo Foo occurrences = 2
java.lang.Object.<init>(0) in Foo occurrences = 1
@@ -3,7 +3,7 @@ java.lang.Object -> Bar Foo
Backward References:
Bar in Bar Foo occurrences = 2
Bar.<init>(0) in Bar occurrences = 1
Bar.<init>(0) in Bar Foo occurrences = 2
Bar.m(0) in Bar occurrences = 1
Bar.xxx in Bar Foo occurrences = 2
Foo in Foo occurrences = 1
@@ -14,6 +14,7 @@ java.lang.Object.<init>(0) in Bar Foo occurrences = 2
java.lang.System in Foo occurrences = 3
java.lang.System.out in Foo occurrences = 3
java.util.ArrayList in Bar Foo occurrences = 8
java.util.ArrayList.<init>(0) in Bar Foo occurrences = 6
java.util.ArrayList.size(0) in Bar Foo occurrences = 6
Class Definitions:
@@ -132,5 +132,9 @@ class ReferenceIndexTest : ReferenceIndexTestBase() {
fun testOccurrences() {
assertIndexOnRebuild("Foo.java", "Bar.java")
}
fun testConstructors() {
assertIndexOnRebuild("Foo.java")
}
}