Merge branch 'db/method-chains-with-casts'

# Conflicts:
#	java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java
This commit is contained in:
Dmitry Batkovich
2017-09-25 12:40:26 +03:00
41 changed files with 947 additions and 354 deletions
@@ -30,6 +30,7 @@ import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import gnu.trove.THashSet;
import gnu.trove.TIntHashSet;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex;
@@ -178,6 +179,24 @@ class CompilerReferenceReader {
return myIndex;
}
TObjectIntHashMap<LightRef> getTypeCasts(@NotNull LightRef.LightClassHierarchyElementDef castType, @NotNull TIntHashSet fileIds) throws StorageException {
TObjectIntHashMap<LightRef> typeCastStats = new TObjectIntHashMap<>();
myIndex.get(CompilerIndices.BACK_CAST).getData(castType).forEach(new ValueContainer.ContainerAction<Collection<LightRef>>() {
@Override
public boolean perform(int id, Collection<LightRef> values) {
if (!fileIds.contains(id)) return true;
for (LightRef ref : values) {
if (!typeCastStats.adjustValue(ref, 1)) {
typeCastStats.put(ref, 1);
}
}
return true;
}
});
return typeCastStats;
}
static boolean exists(Project project) {
File buildDir = BuildManager.getInstance().getProjectSystemDirectory(project);
if (buildDir == null || CompilerBackwardReferenceIndex.versionDiffers(buildDir)) {
@@ -16,9 +16,11 @@
package com.intellij.compiler.backwardRefs;
import com.intellij.compiler.CompilerReferenceService;
import com.intellij.compiler.chainsSearch.SignatureAndOccurrences;
import com.intellij.compiler.chainsSearch.MethodRefAndOccurrences;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.backwardRefs.LightRef;
import org.jetbrains.jps.backwardRefs.SignatureData;
@@ -33,8 +35,13 @@ public abstract class CompilerReferenceServiceEx extends CompilerReferenceServic
}
@NotNull
public abstract SortedSet<SignatureAndOccurrences> findMethodReferenceOccurrences(@NotNull String rawReturnType,
@SignatureData.IteratorKind byte iteratorKind)
public abstract SortedSet<MethodRefAndOccurrences> findMethodReferenceOccurrences(@NotNull String rawReturnType,
@SignatureData.IteratorKind byte iteratorKind,
@NotNull ChainCompletionContext context)
throws ReferenceIndexUnavailableException;
@Nullable
public abstract LightRef.LightClassHierarchyElementDef mayCallOfTypeCast(@NotNull LightRef.JavaLightMethodRef method, int probabilityThreshold)
throws ReferenceIndexUnavailableException;
public abstract boolean mayHappen(@NotNull LightRef qualifier, @NotNull LightRef base, int probabilityThreshold)
@@ -20,8 +20,9 @@ import com.intellij.compiler.backwardRefs.view.CompilerReferenceFindUsagesTestIn
import com.intellij.compiler.backwardRefs.view.CompilerReferenceHierarchyTestInfo;
import com.intellij.compiler.backwardRefs.view.DirtyScopeTestInfo;
import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants;
import com.intellij.compiler.chainsSearch.MethodIncompleteSignature;
import com.intellij.compiler.chainsSearch.SignatureAndOccurrences;
import com.intellij.compiler.chainsSearch.MethodCall;
import com.intellij.compiler.chainsSearch.MethodRefAndOccurrences;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.compiler.server.BuildManager;
import com.intellij.compiler.server.BuildManagerListener;
import com.intellij.lang.injection.InjectedLanguageManager;
@@ -59,6 +60,8 @@ import com.intellij.util.io.PersistentEnumeratorBase;
import com.intellij.util.messages.MessageBusConnection;
import gnu.trove.THashSet;
import gnu.trove.TIntHashSet;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -220,8 +223,9 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
@NotNull
@Override
public SortedSet<SignatureAndOccurrences> findMethodReferenceOccurrences(@NotNull String rawReturnType,
@SignatureData.IteratorKind byte iteratorKind) {
public SortedSet<MethodRefAndOccurrences> findMethodReferenceOccurrences(@NotNull String rawReturnType,
@SignatureData.IteratorKind byte iteratorKind,
@NotNull ChainCompletionContext context) {
try {
myReadDataLock.lock();
try {
@@ -240,15 +244,15 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
.distinct()
.map(r -> {
int count = myReader.getOccurrenceCount(r);
return count <= 1 ? null : new SignatureAndOccurrences(
new MethodIncompleteSignature((LightRef.JavaLightMethodRef)r, sd, this),
return count <= 1 ? null : new MethodRefAndOccurrences(
new MethodCall((LightRef.JavaLightMethodRef)r, sd, context),
count);
}))
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(x -> x.getSignature(), Collectors.summarizingInt(x -> x.getOccurrenceCount())))
.entrySet()
.stream()
.map(e -> new SignatureAndOccurrences(e.getKey(), (int)e.getValue().getSum()))
.map(e -> new MethodRefAndOccurrences(e.getKey(), (int)e.getValue().getSum()))
.collect(Collectors.toCollection(TreeSet::new));
}
finally {
@@ -261,6 +265,52 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
}
}
/**
* finds one best candidate to do a cast type before given method call (eg.: <code>((B) a).someMethod()</code>). Follows given formula:
*
* #(files where method & type cast is occurred) / #(files where method is occurred) > 1 - 1 / probabilityThreshold
*/
@Nullable
@Override
public LightRef.LightClassHierarchyElementDef mayCallOfTypeCast(@NotNull LightRef.JavaLightMethodRef method, int probabilityThreshold)
throws ReferenceIndexUnavailableException {
try {
myReadDataLock.lock();
try {
if (myReader == null) throw new ReferenceIndexUnavailableException();
final TIntHashSet ids = myReader.getAllContainingFileIds(method);
LightRef.LightClassHierarchyElementDef owner = method.getOwner();
TObjectIntHashMap<LightRef> typeCasts = myReader.getTypeCasts(owner, ids);
LightRef[] best = {null};
int[] bestFileCount = {0};
typeCasts.forEachEntry(new TObjectIntProcedure<LightRef>() {
@Override
public boolean execute(LightRef operandType, int matchedFileCount) {
if (ids.size() > probabilityThreshold * (ids.size() - matchedFileCount)) {
if (best[0] == null || bestFileCount[0] < matchedFileCount) {
best[0] = operandType;
bestFileCount[0] = matchedFileCount;
}
}
return true;
}
});
return (LightRef.LightClassHierarchyElementDef)best[0];
}
finally {
myReadDataLock.unlock();
}
} catch (Exception e) {
onException(e, "conditional probability");
return null;
}
}
/**
* conditional probability P(ref1 | ref2) = P(ref1 * ref2) / P(ref2) > 1 - 1 / threshold
*
@@ -0,0 +1,64 @@
/*
* Copyright 2000-2017 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.
*/
package com.intellij.compiler.chainsSearch;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
public interface ChainOperation {
class TypeCast implements ChainOperation {
// we cast only to a class
private final PsiClass myOperandClass;
@NotNull private final PsiClass myCastClass;
public TypeCast(@NotNull PsiClass operandClass, @NotNull PsiClass castClass) {
myOperandClass= operandClass;
myCastClass = castClass;
}
public PsiClass getCastClass() {
return myCastClass;
}
@Override
public String toString() {
return "cast of " + myOperandClass.getName();
}
}
class MethodCall implements ChainOperation {
@NotNull
private final PsiMethod[] myCandidates;
public MethodCall(@NotNull PsiMethod[] candidates) {
if (candidates.length == 0) {
throw new IllegalStateException();
}
myCandidates = candidates;
}
@NotNull
public PsiMethod[] getCandidates() {
return myCandidates;
}
@Override
public String toString() {
return myCandidates[0].getName() + "()";
}
}
}
@@ -28,11 +28,11 @@ import java.util.*;
public class ChainSearcher {
@NotNull
public static List<MethodChain> search(int pathMaximalLength,
ChainSearchTarget searchTarget,
int maxResultSize,
ChainCompletionContext context,
CompilerReferenceServiceEx compilerReferenceServiceEx) {
public static List<OperationChain> search(int pathMaximalLength,
ChainSearchTarget searchTarget,
int maxResultSize,
ChainCompletionContext context,
CompilerReferenceServiceEx compilerReferenceServiceEx) {
SearchInitializer initializer = createInitializer(searchTarget, compilerReferenceServiceEx, context);
return search(compilerReferenceServiceEx, initializer, pathMaximalLength, maxResultSize, context);
}
@@ -41,49 +41,49 @@ public class ChainSearcher {
private static SearchInitializer createInitializer(ChainSearchTarget target,
CompilerReferenceServiceEx referenceServiceEx,
ChainCompletionContext context) {
SortedSet<SignatureAndOccurrences> methods = null;
SortedSet<MethodRefAndOccurrences> methods = Collections.emptySortedSet();
for (byte kind : target.getArrayKind()) {
SortedSet<SignatureAndOccurrences> currentMethods =
referenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), kind);
if (methods == null) {
methods = currentMethods;
} else {
methods = unionSortedSet(currentMethods, methods);
}
SortedSet<MethodRefAndOccurrences> currentMethods = referenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), kind, context);
methods = methods == null ? currentMethods : unionSortedSet(currentMethods, methods);
}
return new SearchInitializer(methods, context);
}
@NotNull
private static List<MethodChain> search(CompilerReferenceServiceEx referenceServiceEx,
SearchInitializer initializer,
int chainMaxLength,
int maxResultSize,
ChainCompletionContext context) {
LinkedList<MethodChain> q = initializer.getChainQueue();
private static List<OperationChain> search(CompilerReferenceServiceEx referenceServiceEx,
SearchInitializer initializer,
int chainMaxLength,
int maxResultSize,
ChainCompletionContext context) {
LinkedList<OperationChain> q = initializer.getChainQueue();
List<MethodChain> result = new ArrayList<>();
List<OperationChain> result = new ArrayList<>();
while (!q.isEmpty()) {
ProgressManager.checkCanceled();
MethodChain currentChain = q.poll();
MethodIncompleteSignature headSignature = currentChain.getHeadSignature();
OperationChain currentChain = q.poll();
RefChainOperation head = currentChain.getHead();
if (addChainIfTerminal(currentChain, result, chainMaxLength, context)) continue;
// otherwise try to find chain continuation
boolean updated = false;
SortedSet<SignatureAndOccurrences> candidates = referenceServiceEx.findMethodReferenceOccurrences(headSignature.getOwner(), SignatureData.ZERO_DIM);
for (SignatureAndOccurrences candidate : candidates) {
SortedSet<MethodRefAndOccurrences> candidates = referenceServiceEx.findMethodReferenceOccurrences(head.getQualifierRawName(), SignatureData.ZERO_DIM, context);
LightRef ref = head.getLightRef();
for (MethodRefAndOccurrences candidate : candidates) {
if (candidate.getOccurrenceCount() * ChainSearchMagicConstants.FILTER_RATIO < currentChain.getChainWeight()) {
break;
}
MethodIncompleteSignature sign = candidate.getSignature();
if ((sign.isStatic() || !sign.getOwner().equals(context.getTarget().getClassQName())) &&
referenceServiceEx.mayHappen(candidate.getSignature().getRef(), headSignature.getRef(), ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD)) {
MethodChain continuation = currentChain.continuation(candidate.getSignature(), candidate.getOccurrenceCount(), context);
MethodCall sign = candidate.getSignature();
if ((sign.isStatic() || !sign.getQualifierRawName().equals(context.getTarget().getClassQName())) &&
(!(ref instanceof LightRef.JavaLightMethodRef) ||
referenceServiceEx.mayHappen(candidate.getSignature().getLightRef(), ref, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD))) {
OperationChain
continuation = currentChain.continuationWithMethod(candidate.getSignature(), candidate.getOccurrenceCount(), context);
if (continuation != null) {
boolean stopChain = candidate.getSignature().isStatic() || context.hasQualifier(context.resolveQualifierClass(candidate.getSignature()));
boolean stopChain =
candidate.getSignature().isStatic() || context.hasQualifier(context.resolvePsiClass(candidate.getSignature().getQualifierDef()));
if (stopChain) {
addChainIfNotPresent(continuation, result);
}
@@ -95,6 +95,19 @@ public class ChainSearcher {
}
}
if (ref instanceof LightRef.JavaLightMethodRef) {
LightRef.LightClassHierarchyElementDef def =
referenceServiceEx.mayCallOfTypeCast((LightRef.JavaLightMethodRef)ref, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD);
if (def != null) {
OperationChain
continuation = currentChain.continuationWithCast(new TypeCast(def, head.getQualifierDef(), referenceServiceEx), context);
if (continuation != null) {
q.addFirst(continuation);
updated = true;
}
}
}
if (!updated) {
addChainIfQualifierCanBeOccurredInContext(currentChain, result, context, referenceServiceEx);
}
@@ -109,15 +122,18 @@ public class ChainSearcher {
/**
* To reduce false-positives we add a method to result only if its qualifier can be occurred together with context variables.
*/
private static void addChainIfQualifierCanBeOccurredInContext(MethodChain currentChain,
List<MethodChain> result,
private static void addChainIfQualifierCanBeOccurredInContext(OperationChain currentChain,
List<OperationChain> result,
ChainCompletionContext context,
CompilerReferenceServiceEx referenceServiceEx) {
if (!context.getTarget().getClassQName().equals(currentChain.getHeadSignature().getOwner())) {
RefChainOperation signature = currentChain.getHeadMethodCall();
// type cast + introduced qualifier: it's too complex chain
if (currentChain.hasCast()) return;
if (!context.getTarget().getClassQName().equals(signature.getQualifierRawName())) {
Set<LightRef> references = context.getContextClassReferences();
boolean isRelevantQualifier = false;
for (LightRef ref: references) {
if (referenceServiceEx.mayHappen(currentChain.getHeadSignature().getOwnerRef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) {
if (referenceServiceEx.mayHappen(signature.getQualifierDef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) {
isRelevantQualifier = true;
break;
}
@@ -129,10 +145,12 @@ public class ChainSearcher {
}
}
private static boolean addChainIfTerminal(MethodChain currentChain, List<MethodChain> result, int pathMaximalLength,
private static boolean addChainIfTerminal(OperationChain currentChain, List<OperationChain> result, int pathMaximalLength,
ChainCompletionContext context) {
if (currentChain.getHeadSignature().isStatic() ||
context.hasQualifier(context.resolveQualifierClass(currentChain.getHeadSignature())) ||
RefChainOperation signature = currentChain.getHeadMethodCall();
RefChainOperation head = currentChain.getHead();
if (((MethodCall)signature).isStatic() ||
context.hasQualifier(context.resolvePsiClass(head.getQualifierDef())) ||
currentChain.length() >= pathMaximalLength) {
addChainIfNotPresent(currentChain, result);
return true;
@@ -140,7 +158,7 @@ public class ChainSearcher {
return false;
}
private static void addChainIfNotPresent(MethodChain newChain, List<MethodChain> result) {
private static void addChainIfNotPresent(OperationChain newChain, List<OperationChain> result) {
if (result.isEmpty()) {
result.add(newChain);
return;
@@ -148,8 +166,8 @@ public class ChainSearcher {
boolean doAdd = true;
IntStack indicesToRemove = new IntStack();
for (int i = 0; i < result.size(); i++) {
MethodChain chain = result.get(i);
MethodChain.CompareResult r = MethodChain.compare(chain, newChain);
OperationChain chain = result.get(i);
OperationChain.CompareResult r = OperationChain.compare(chain, newChain);
switch (r) {
case LEFT_CONTAINS_RIGHT:
indicesToRemove.push(i);
@@ -1,161 +0,0 @@
/*
* Copyright 2000-2017 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.
*/
package com.intellij.compiler.chainsSearch;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static com.intellij.util.containers.ContainerUtil.newArrayList;
import static com.intellij.util.containers.ContainerUtil.reverse;
public class MethodChain {
private final List<PsiMethod[]> myRevertedPath;
private final MethodIncompleteSignature mySignature;
private final int myWeight;
private final PsiClass myQualifierClass;
@Nullable
public static MethodChain create(@NotNull MethodIncompleteSignature signature,
int weight,
@NotNull ChainCompletionContext context) {
PsiClass qualifier = context.resolveQualifierClass(signature);
if (qualifier == null || (!signature.isStatic() && InheritanceUtil.isInheritorOrSelf(context.getTarget().getTargetClass(), qualifier, true))) {
return null;
}
PsiMethod[] methods = context.resolve(signature);
if (methods.length == 0) return null;
Set<PsiClass> classes = Arrays.stream(methods)
.flatMap(m -> Arrays.stream(m.getParameterList().getParameters()))
.map(p -> PsiUtil.resolveClassInType(p.getType()))
.collect(Collectors.toSet());
PsiClass contextClass = context.getTarget().getTargetClass();
if (classes.contains(contextClass)) {
return null;
}
classes.add(contextClass);
return new MethodChain(qualifier, Collections.singletonList(methods), signature, weight);
}
public MethodChain(@NotNull PsiClass qualifierClass,
@NotNull List<PsiMethod[]> revertedPath,
MethodIncompleteSignature signature,
int weight) {
myQualifierClass = qualifierClass;
myRevertedPath = revertedPath;
mySignature = signature;
myWeight = weight;
}
@NotNull
public MethodIncompleteSignature getHeadSignature() {
return mySignature;
}
public int length() {
return myRevertedPath.size();
}
public PsiClass getQualifierClass() {
return myQualifierClass;
}
@NotNull
public PsiMethod[] getFirst() {
return myRevertedPath.get(0);
}
public List<PsiMethod[]> getPath() {
return reverse(myRevertedPath);
}
public int getChainWeight() {
return myWeight;
}
public MethodChain continuation(@NotNull MethodIncompleteSignature signature,
int weight,
@NotNull ChainCompletionContext context) {
MethodChain head = create(signature, weight, context);
if (head == null) return null;
ArrayList<PsiMethod[]> newRevertedPath = newArrayList();
newRevertedPath.addAll(myRevertedPath);
newRevertedPath.add(head.getPath().get(0));
return new MethodChain(head.getQualifierClass(), newRevertedPath, head.getHeadSignature(), Math.min(weight, getChainWeight()));
}
@Override
public String toString() {
return myQualifierClass.getName() + "." + reverse(myRevertedPath).stream().map(methods -> methods[0].getName() + "()").collect(Collectors.joining("."));
}
@SuppressWarnings("ConstantConditions")
public static CompareResult compare(@NotNull MethodChain left, @NotNull MethodChain right) {
if (left.length() == 0 || right.length() == 0) {
throw new IllegalStateException("chains can't be empty");
}
Iterator<PsiMethod[]> leftIterator = left.myRevertedPath.iterator();
Iterator<PsiMethod[]> rightIterator = right.myRevertedPath.iterator();
while (leftIterator.hasNext() && rightIterator.hasNext()) {
PsiMethod[] thisNext = leftIterator.next();
PsiMethod[] thatNext = rightIterator.next();
if (!lookSimilar(thisNext, thatNext)) {
return CompareResult.NOT_EQUAL;
}
}
if (leftIterator.hasNext() && !rightIterator.hasNext()) {
return CompareResult.LEFT_CONTAINS_RIGHT;
}
if (!leftIterator.hasNext() && rightIterator.hasNext()) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
return CompareResult.EQUAL;
}
public enum CompareResult {
LEFT_CONTAINS_RIGHT,
RIGHT_CONTAINS_LEFT,
EQUAL,
NOT_EQUAL
}
static boolean lookSimilar(PsiMethod[] methods1, PsiMethod[] methods2) {
PsiMethod repr1 = methods1[0];
PsiMethod repr2 = methods2[0];
if (repr1.hasModifierProperty(PsiModifier.STATIC) || repr2.hasModifierProperty(PsiModifier.STATIC)) return false;
if (!repr1.getName().equals(repr2.getName()) ||
repr1.getParameterList().getParametersCount() != repr2.getParameterList().getParametersCount()) {
return false;
}
Set<PsiMethod> methodSet1 = ContainerUtil.newHashSet(methods1);
Set<PsiMethod> methodSet2 = ContainerUtil.newHashSet(methods2);
if (ContainerUtil.intersects(methodSet1, methodSet2)) return true;
Set<PsiMethod> deepestSupers1 = methodSet1.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).collect(Collectors.toSet());
return methodSet2.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).anyMatch(deepestSupers1::contains);
}
}
@@ -16,6 +16,7 @@
package com.intellij.compiler.chainsSearch;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.completion.CastingLookupElementDecorator;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaChainLookupElement;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
@@ -34,31 +35,40 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.stream.Collectors;
public class MethodChainLookupRangingHelper {
@NotNull
public static LookupElement toLookupElement(MethodChain chain,
public static LookupElement toLookupElement(OperationChain chain,
ChainCompletionContext context) {
int unreachableParametersCount = 0;
int matchedParametersInContext = 0;
LookupElement chainLookupElement = null;
for (PsiMethod[] psiMethods : chain.getPath()) {
PsiMethod method = ObjectUtils.notNull(MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, context.getTarget().getTargetClass()));
Couple<Integer> info = calculateParameterInfo(method, context);
unreachableParametersCount += info.getFirst();
matchedParametersInContext += info.getSecond();
for (ChainOperation op : chain.getPath()) {
if (op instanceof ChainOperation.MethodCall) {
PsiMethod method = ObjectUtils.notNull(MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(((ChainOperation.MethodCall)op).getCandidates(),
context.getTarget().getTargetClass()));
Couple<Integer> info = calculateParameterInfo(method, context);
unreachableParametersCount += info.getFirst();
matchedParametersInContext += info.getSecond();
if (chainLookupElement == null) {
LookupElement qualifierLookupElement = createQualifierLookupElement(method, chain.getQualifierClass(), context);
LookupElement headLookupElement = createMethodLookupElement(method);
chainLookupElement = qualifierLookupElement == null ? headLookupElement : new JavaChainLookupElement(qualifierLookupElement, headLookupElement);
if (chainLookupElement == null) {
LookupElement qualifierLookupElement = method.hasModifierProperty(PsiModifier.STATIC) ? null : createQualifierLookupElement(chain.getQualifierClass(), context);
LookupElement headLookupElement = createMethodLookupElement(method);
chainLookupElement = qualifierLookupElement == null ? headLookupElement : new JavaChainLookupElement(qualifierLookupElement, headLookupElement);
} else {
chainLookupElement = new JavaChainLookupElement(chainLookupElement, new JavaMethodCallElement(method));
}
} else {
chainLookupElement = new JavaChainLookupElement(chainLookupElement, new JavaMethodCallElement(method));
if (chainLookupElement == null) {
chainLookupElement = createQualifierLookupElement(chain.getQualifierClass(), context);
}
PsiClass castClass = ((ChainOperation.TypeCast)op).getCastClass();
PsiClassType type = JavaPsiFacade.getElementFactory(castClass.getProject()).createType(castClass);
chainLookupElement = CastingLookupElementDecorator.createCastingElement(chainLookupElement, type);
}
}
@@ -100,9 +110,9 @@ public class MethodChainLookupRangingHelper {
};
}
@Nullable
private static LookupElement createQualifierLookupElement(@NotNull PsiMethod method, @NotNull PsiClass qualifierClass, @NotNull ChainCompletionContext context) {
if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
@NotNull
private static LookupElement createQualifierLookupElement(@NotNull PsiClass qualifierClass,
@NotNull ChainCompletionContext context) {
PsiNamedElement element = context.getQualifiers(qualifierClass).findFirst().orElse(null);
if (element == null) {
return new ChainCompletionNewVariableLookupElement(qualifierClass, context);
@@ -21,37 +21,16 @@ import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.text.EditDistance;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.Set;
import java.util.stream.Stream;
public final class MethodChainsSearchUtil {
private final static int COMMON_PART_MIN_LENGTH = 3;
private MethodChainsSearchUtil() {
}
public static boolean isSimilar(@NotNull String target,
@NotNull String candidate) {
return EditDistance.levenshtein(target, sanitizedToLowerCase(candidate), true) >= COMMON_PART_MIN_LENGTH;
}
@NotNull
public static String sanitizedToLowerCase(@NotNull String name) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (Character.isLetter(ch)) {
result.append(Character.toLowerCase(ch));
}
}
return result.toString();
}
@Nullable
public static PsiMethod getMethodWithMinNotPrimitiveParameters(@NotNull PsiMethod[] methods,
@NotNull PsiClass target) {
@@ -17,16 +17,16 @@ package com.intellij.compiler.chainsSearch;
import org.jetbrains.annotations.NotNull;
public class SignatureAndOccurrences implements Comparable<SignatureAndOccurrences> {
private final MethodIncompleteSignature mySignature;
public class MethodRefAndOccurrences implements Comparable<MethodRefAndOccurrences> {
private final MethodCall mySignature;
private final int myOccurrences;
public SignatureAndOccurrences(final MethodIncompleteSignature signature, final int occurrences) {
public MethodRefAndOccurrences(final MethodCall signature, final int occurrences) {
mySignature = signature;
myOccurrences = occurrences;
}
public MethodIncompleteSignature getSignature() {
public MethodCall getSignature() {
return mySignature;
}
@@ -35,7 +35,7 @@ public class SignatureAndOccurrences implements Comparable<SignatureAndOccurrenc
}
@Override
public int compareTo(@NotNull final SignatureAndOccurrences that) {
public int compareTo(@NotNull final MethodRefAndOccurrences that) {
final int sub = -getOccurrenceCount() + that.getOccurrenceCount();
if (sub != 0) {
return sub;
@@ -0,0 +1,198 @@
/*
* Copyright 2000-2017 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.
*/
package com.intellij.compiler.chainsSearch;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
public class OperationChain {
@NotNull
private final ChainOperation[] myReverseOperations;
private final RefChainOperation myHeadOperation;
private final MethodCall myHeadMethodCall;
private final int myWeight;
private final PsiClass myQualifierClass;
@Nullable
public static OperationChain create(@NotNull MethodCall signature,
int weight,
@NotNull ChainCompletionContext context) {
PsiClass qualifier = context.resolvePsiClass(signature.getQualifierDef());
if (qualifier == null || (!signature.isStatic() && InheritanceUtil.isInheritorOrSelf(context.getTarget().getTargetClass(), qualifier, true))) {
return null;
}
PsiMethod[] methods = context.resolve(signature);
if (methods.length == 0) return null;
Set<PsiClass> classes = Arrays.stream(methods)
.flatMap(m -> Arrays.stream(m.getParameterList().getParameters()))
.map(p -> PsiUtil.resolveClassInType(p.getType()))
.collect(Collectors.toSet());
PsiClass contextClass = context.getTarget().getTargetClass();
if (classes.contains(contextClass)) {
return null;
}
classes.add(contextClass);
return new OperationChain(qualifier, new ChainOperation[] {new ChainOperation.MethodCall(methods)}, signature, signature, weight);
}
private OperationChain(@NotNull PsiClass qualifierClass,
@NotNull ChainOperation[] reverseOperations,
RefChainOperation signature,
MethodCall headMethodSign,
int weight) {
myQualifierClass = qualifierClass;
myReverseOperations = reverseOperations;
myHeadOperation = signature;
myHeadMethodCall = headMethodSign;
myWeight = weight;
}
public boolean hasCast() {
return Arrays.stream(myReverseOperations).anyMatch(op -> op instanceof ChainOperation.TypeCast);
}
@NotNull
public MethodCall getHeadMethodCall() {
return myHeadMethodCall;
}
@NotNull
public RefChainOperation getHead() {
return myHeadOperation;
}
public int length() {
return myReverseOperations.length;
}
public PsiClass getQualifierClass() {
return myQualifierClass;
}
@NotNull
public PsiMethod[] getFirst() {
return ((ChainOperation.MethodCall) myReverseOperations[0]).getCandidates();
}
public ChainOperation[] getPath() {
return ArrayUtil.reverseArray(myReverseOperations);
}
public int getChainWeight() {
return myWeight;
}
@Nullable
OperationChain continuationWithMethod(@NotNull MethodCall signature,
int weight,
@NotNull ChainCompletionContext context) {
OperationChain head = create(signature, weight, context);
if (head == null) return null;
ChainOperation[] newReverseOperations = new ChainOperation[length() + 1];
System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length);
newReverseOperations[length()] = head.getPath()[0];
return new OperationChain(head.getQualifierClass(), newReverseOperations, head.getHead(), signature, Math.min(weight, getChainWeight()));
}
@Nullable
OperationChain continuationWithCast(@NotNull TypeCast cast,
@NotNull ChainCompletionContext context) {
PsiClass operand = context.resolvePsiClass(cast.getLightRef());
PsiClass castType = context.resolvePsiClass(cast.getCastTypeRef());
if (operand == null || castType == null) return null;
ChainOperation[] newReverseOperations = new ChainOperation[length() + 1];
System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length);
newReverseOperations[length()] = new ChainOperation.TypeCast(operand, castType);
return new OperationChain(operand, newReverseOperations, cast, myHeadMethodCall, getChainWeight());
}
@Override
public String toString() {
ChainOperation[] path = getPath();
return Arrays.toString(path) + " on " + myQualifierClass.getName();
}
@SuppressWarnings("ConstantConditions")
public static CompareResult compare(@NotNull OperationChain left, @NotNull OperationChain right) {
if (left.length() == 0 || right.length() == 0) {
throw new IllegalStateException("chains can't be empty");
}
int leftCurrentIdx = 0;
int rightCurrentIdx = 0;
while (leftCurrentIdx < left.length() && rightCurrentIdx < right.length()) {
ChainOperation thisNext = left.myReverseOperations[leftCurrentIdx];
ChainOperation thatNext = right.myReverseOperations[leftCurrentIdx];
if (!lookSimilar(thisNext, thatNext)) {
return CompareResult.NOT_EQUAL;
}
leftCurrentIdx++;
rightCurrentIdx++;
}
if (leftCurrentIdx < left.length() && rightCurrentIdx == right.length()) {
return CompareResult.LEFT_CONTAINS_RIGHT;
}
if (leftCurrentIdx == left.length() && rightCurrentIdx < right.length()) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
return CompareResult.EQUAL;
}
public enum CompareResult {
LEFT_CONTAINS_RIGHT,
RIGHT_CONTAINS_LEFT,
EQUAL,
NOT_EQUAL
}
static boolean lookSimilar(ChainOperation op1, ChainOperation op2) {
if (op1 instanceof ChainOperation.TypeCast || op2 instanceof ChainOperation.TypeCast) return false;
PsiMethod[] methods1 = ((ChainOperation.MethodCall)op1).getCandidates();
PsiMethod[] methods2 = ((ChainOperation.MethodCall)op2).getCandidates();
PsiMethod repr1 = methods1[0];
PsiMethod repr2 = methods2[0];
if (repr1.hasModifierProperty(PsiModifier.STATIC) || repr2.hasModifierProperty(PsiModifier.STATIC)) return false;
if (!repr1.getName().equals(repr2.getName()) ||
repr1.getParameterList().getParametersCount() != repr2.getParameterList().getParametersCount()) {
return false;
}
Set<PsiMethod> methodSet1 = ContainerUtil.newHashSet(methods1);
Set<PsiMethod> methodSet2 = ContainerUtil.newHashSet(methods2);
if (ContainerUtil.intersects(methodSet1, methodSet2)) return true;
Set<PsiMethod> deepestSupers1 = methodSet1.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).collect(Collectors.toSet());
return methodSet2.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).anyMatch(deepestSupers1::contains);
}
}
@@ -16,60 +16,74 @@
package com.intellij.compiler.chainsSearch
import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext
import com.intellij.compiler.chainsSearch.context.ChainSearchTarget
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.PsiArrayType
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.util.PsiUtil
import org.jetbrains.jps.backwardRefs.LightRef
import org.jetbrains.jps.backwardRefs.SignatureData
import java.util.function.Predicate
class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
private val signatureData: SignatureData,
private val refService: CompilerReferenceServiceEx) {
companion object {
sealed class RefChainOperation {
abstract val qualifierRawName: String
abstract val qualifierDef: LightRef.LightClassHierarchyElementDef
abstract val lightRef: LightRef
}
class TypeCast(override val lightRef: LightRef.LightClassHierarchyElementDef,
val castTypeRef: LightRef.LightClassHierarchyElementDef,
refService: CompilerReferenceServiceEx): RefChainOperation() {
override val qualifierRawName
get() = operandName.value
override val qualifierDef
get() = lightRef
private val operandName = lazy(LazyThreadSafetyMode.NONE) {
refService.getName(lightRef.name)
}
}
class MethodCall(override val lightRef: LightRef.JavaLightMethodRef,
private val signatureData: SignatureData,
private val context: ChainCompletionContext): RefChainOperation() {
private companion object {
val CONSTRUCTOR_METHOD_NAME = "<init>"
}
val name: String by lazy(LazyThreadSafetyMode.NONE) {
refService.getName(ref.name)
override val qualifierRawName: String
get() = owner.value
override val qualifierDef: LightRef.LightClassHierarchyElementDef
get() = lightRef.owner
private val name = lazy(LazyThreadSafetyMode.NONE) {
context.refService.getName(lightRef.name)
}
val ownerRef = ref.owner
val owner: String by lazy(LazyThreadSafetyMode.NONE) {
refService.getName(ref.owner.name)
private val owner = lazy(LazyThreadSafetyMode.NONE) {
context.refService.getName(lightRef.owner.name)
}
val rawReturnType: String by lazy(LazyThreadSafetyMode.NONE) {
refService.getName(signatureData.rawReturnType)
private val rawReturnType = lazy(LazyThreadSafetyMode.NONE) {
context.refService.getName(signatureData.rawReturnType)
}
val parameterCount: Int
get() = ref.parameterCount
val isStatic: Boolean
get() = signatureData.isStatic
fun resolveQualifier(project: Project,
resolveScope: GlobalSearchScope,
accessValidator: Predicate<PsiMember>): PsiClass? {
val clazz = JavaPsiFacade.getInstance(project).findClass(owner, resolveScope)
return if (clazz != null && accessValidator.test(clazz)) clazz else null
}
fun resolve(project: Project,
resolveScope: GlobalSearchScope,
accessValidator: Predicate<PsiMember>): Array<PsiMethod> {
if (CONSTRUCTOR_METHOD_NAME == name) {
fun resolve(): Array<PsiMethod> {
if (CONSTRUCTOR_METHOD_NAME == name.value) {
return PsiMethod.EMPTY_ARRAY
}
val aClass = resolveQualifier(project, resolveScope, accessValidator) ?: return PsiMethod.EMPTY_ARRAY
return aClass.findMethodsByName(name, true)
val aClass = context.resolvePsiClass(qualifierDef) ?: return PsiMethod.EMPTY_ARRAY
return aClass.findMethodsByName(name.value, true)
.filter { it.hasModifierProperty(PsiModifier.STATIC) == isStatic }
.filter { !it.isDeprecated }
.filter { accessValidator.test(it) }
.filter { context.accessValidator().test(it) }
.filter {
val returnType = it.returnType
when (signatureData.iteratorKind) {
@@ -77,7 +91,7 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
when (returnType) {
is PsiArrayType -> {
val componentType = returnType.componentType
componentType is PsiClassType && componentType.resolve()?.qualifiedName == rawReturnType
componentType is PsiClassType && componentType.resolve()?.qualifiedName == rawReturnType.value
}
else -> false
}
@@ -85,11 +99,11 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
SignatureData.ITERATOR_ONE_DIM -> {
val iteratorKind = ChainSearchTarget.getIteratorKind(PsiUtil.resolveClassInClassTypeOnly(returnType))
when {
iteratorKind != null -> PsiUtil.resolveClassInClassTypeOnly(PsiUtil.substituteTypeParameter(returnType, iteratorKind, 0, false))?.qualifiedName == rawReturnType
iteratorKind != null -> PsiUtil.resolveClassInClassTypeOnly(PsiUtil.substituteTypeParameter(returnType, iteratorKind, 0, false))?.qualifiedName == rawReturnType.value
else -> false
}
}
SignatureData.ZERO_DIM -> returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType
SignatureData.ZERO_DIM -> returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType.value
else -> throw IllegalStateException("kind is unsupported ${signatureData.iteratorKind}")
}
}
@@ -101,23 +115,23 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as MethodIncompleteSignature
other as MethodCall
if (ref.owner != other.ref.owner) return false
if (ref.name != other.ref.name) return false
if (lightRef.owner != other.lightRef.owner) return false
if (lightRef.name != other.lightRef.name) return false
if (signatureData != other.signatureData) return false
return true
}
override fun hashCode(): Int {
var result = ref.owner.hashCode()
result = 31 * result + ref.name.hashCode()
var result = lightRef.owner.hashCode()
result = 31 * result + lightRef.name.hashCode()
result = 31 * result + signatureData.hashCode()
return result
}
override fun toString(): String {
return owner + (if (isStatic) "" else "#") + name + "(" + parameterCount + ")"
return qualifierRawName + (if (isStatic) "." else "#") + name + "(" + lightRef.parameterCount + ")"
}
}
}
@@ -21,39 +21,39 @@ import java.util.*;
public class SearchInitializer {
private final ChainCompletionContext myContext;
private final LinkedList<MethodChain> myQueue;
private final LinkedHashMap<MethodIncompleteSignature, MethodChain> myChains;
private final LinkedList<OperationChain> myQueue;
private final LinkedHashMap<MethodCall, OperationChain> myChains;
public SearchInitializer(SortedSet<SignatureAndOccurrences> indexValues,
public SearchInitializer(SortedSet<MethodRefAndOccurrences> indexValues,
ChainCompletionContext context) {
myContext = context;
int size = indexValues.size();
List<MethodChain> chains = new ArrayList<>(size);
List<OperationChain> chains = new ArrayList<>(size);
populateFrequentlyUsedMethod(indexValues, chains);
myQueue = new LinkedList<>();
myChains = new LinkedHashMap<>(chains.size());
for (MethodChain chain : chains) {
MethodIncompleteSignature signature = chain.getHeadSignature();
for (OperationChain chain : chains) {
MethodCall signature = (MethodCall)chain.getHead();
myQueue.add(chain);
myChains.put(signature, chain);
}
}
public LinkedList<MethodChain> getChainQueue() {
public LinkedList<OperationChain> getChainQueue() {
return myQueue;
}
public LinkedHashMap<MethodIncompleteSignature, MethodChain> getChains() {
public LinkedHashMap<MethodCall, OperationChain> getChains() {
return myChains;
}
private void populateFrequentlyUsedMethod(SortedSet<SignatureAndOccurrences> signatures,
List<MethodChain> chains) {
private void populateFrequentlyUsedMethod(SortedSet<MethodRefAndOccurrences> signatures,
List<OperationChain> chains) {
int bestOccurrences = -1;
for (SignatureAndOccurrences indexValue : signatures) {
MethodChain methodChain = MethodChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext);
if (methodChain != null) {
chains.add(methodChain);
for (MethodRefAndOccurrences indexValue : signatures) {
OperationChain operationChain = OperationChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext);
if (operationChain != null) {
chains.add(operationChain);
int occurrences = indexValue.getOccurrenceCount();
if (bestOccurrences == -1) {
bestOccurrences = occurrences;
@@ -22,7 +22,7 @@ import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx;
import com.intellij.compiler.backwardRefs.ReferenceIndexUnavailableException;
import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants;
import com.intellij.compiler.chainsSearch.ChainSearcher;
import com.intellij.compiler.chainsSearch.MethodChain;
import com.intellij.compiler.chainsSearch.OperationChain;
import com.intellij.compiler.chainsSearch.MethodChainLookupRangingHelper;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.compiler.chainsSearch.context.ChainSearchTarget;
@@ -92,13 +92,13 @@ public class MethodChainCompletionContributor extends CompletionContributor {
private static List<LookupElement> searchForLookups(ChainCompletionContext context) {
CompilerReferenceServiceEx methodsUsageIndexReader = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(context.getProject());
ChainSearchTarget target = context.getTarget();
List<MethodChain> searchResult =
List<OperationChain> searchResult =
ChainSearcher.search(ChainSearchMagicConstants.MAX_CHAIN_SIZE,
target,
ChainSearchMagicConstants.MAX_SEARCH_RESULT_SIZE,
context,
methodsUsageIndexReader);
int maxWeight = searchResult.stream().mapToInt(MethodChain::getChainWeight).max().orElse(0);
int maxWeight = searchResult.stream().mapToInt(OperationChain::getChainWeight).max().orElse(0);
return searchResult
.stream()
@@ -17,7 +17,7 @@ package com.intellij.compiler.chainsSearch.context;
import com.intellij.compiler.CompilerReferenceService;
import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx;
import com.intellij.compiler.chainsSearch.MethodIncompleteSignature;
import com.intellij.compiler.chainsSearch.MethodCall;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyValue;
@@ -31,6 +31,7 @@ import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FactoryMap;
import gnu.trove.THashSet;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.backwardRefs.LightRef;
@@ -62,22 +63,23 @@ public class ChainCompletionContext {
@NotNull
private final PsiResolveHelper myResolveHelper;
@NotNull
private final Map<MethodIncompleteSignature, PsiClass> myQualifierClassResolver;
private final TIntObjectHashMap<PsiClass> myQualifierClassResolver;
@NotNull
private final Map<MethodIncompleteSignature, PsiMethod[]> myResolver;
private final Map<MethodCall, PsiMethod[]> myResolver;
@NotNull
private final CompilerReferenceServiceEx myRefServiceEx;
private final NotNullLazyValue<Set<LightRef>> myContextClassReferences = new NotNullLazyValue<Set<LightRef>>() {
@NotNull
@Override
protected Set<LightRef> compute() {
CompilerReferenceServiceEx referenceServiceEx = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(myProject);
return getContextTypes()
.stream()
.map(PsiUtil::resolveClassInType)
.filter(Objects::nonNull)
.map(c -> ClassUtil.getJVMClassName(c))
.filter(Objects::nonNull)
.mapToInt(c -> referenceServiceEx.getNameId(c))
.mapToInt(c -> myRefServiceEx.getNameId(c))
.filter(n -> n != 0)
.mapToObj(n -> new LightRef.JavaLightClassRef(n)).collect(Collectors.toSet());
}
@@ -92,8 +94,9 @@ public class ChainCompletionContext {
myResolveScope = context.getResolveScope();
myProject = context.getProject();
myResolveHelper = PsiResolveHelper.SERVICE.getInstance(myProject);
myQualifierClassResolver = FactoryMap.create(sign1 -> sign1.resolveQualifier(myProject, myResolveScope, accessValidator()));
myResolver = FactoryMap.create(sign -> sign.resolve(myProject, myResolveScope, accessValidator()));
myQualifierClassResolver = new TIntObjectHashMap<>();
myResolver = FactoryMap.create(sign -> sign.resolve());
myRefServiceEx = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(myProject);
}
@NotNull
@@ -113,6 +116,11 @@ public class ChainCompletionContext {
return false;
}
@NotNull
public CompilerReferenceServiceEx getRefService() {
return myRefServiceEx;
}
@NotNull
public PsiElement getContextPsi() {
return myContext;
@@ -159,16 +167,28 @@ public class ChainCompletionContext {
}
@Nullable
public PsiClass resolveQualifierClass(MethodIncompleteSignature sign) {
return myQualifierClassResolver.get(sign);
public PsiClass resolvePsiClass(LightRef.LightClassHierarchyElementDef aClass) {
int nameId = aClass.getName();
if (myQualifierClassResolver.contains(nameId)) {
return myQualifierClassResolver.get(nameId);
} else {
PsiClass psiClass = null;
String name = myRefServiceEx.getName(nameId);
PsiClass resolvedClass = JavaPsiFacade.getInstance(getProject()).findClass(name, myResolveScope);
if (resolvedClass != null && accessValidator().test(resolvedClass)) {
psiClass = resolvedClass;
}
myQualifierClassResolver.put(nameId, psiClass);
return psiClass;
}
}
@NotNull
public PsiMethod[] resolve(MethodIncompleteSignature sign) {
public PsiMethod[] resolve(MethodCall sign) {
return myResolver.get(sign);
}
private Predicate<PsiMember> accessValidator() {
public Predicate<PsiMember> accessValidator() {
return m -> myResolveHelper.isAccessible(m, myContext, null);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -79,7 +79,7 @@ public class CastingLookupElementDecorator extends LookupElementDecorator<Lookup
return myCastItem;
}
static LookupElement createCastingElement(final LookupElement delegate, PsiType castTo) {
public static LookupElement createCastingElement(final LookupElement delegate, PsiType castTo) {
return new CastingLookupElementDecorator(delegate, castTo);
}
}
@@ -0,0 +1,11 @@
interface Editor {}
interface EditorEx extends Editor {
MarkupModelEx getMarkupModel();
}
interface MarkupModelEx {}
class Test {
void m(Editor editor) {
MarkupModelEx m = <caret>
}
}
@@ -0,0 +1,19 @@
interface Editor {}
interface EditorEx extends Editor {
MarkupModelEx getMarkupModel();
}
interface MarkupModelEx {}
class Test {
void m(Editor editor) {
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
}
}
@@ -0,0 +1,15 @@
interface InspectionManager {
static InspectionManager getInstance() {
return null;
}
}
interface InspectionManagerEx extends InspectionManager {
GlobalInspectionContext createContext();
}
interface GlobalInspectionContext {}
class Test {
void m() {
GlobalInspectionContext c = <caret>
}
}
@@ -0,0 +1,24 @@
interface InspectionManager {
static InspectionManager getInstance() {
return null;
}
}
interface InspectionManagerEx extends InspectionManager {
GlobalInspectionContext createContext();
}
interface GlobalInspectionContext {}
class Test {
void m() {
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
((InspectionManagerEx)InspectionManager.getInstance()).createContext();
}
}
@@ -0,0 +1,11 @@
interface Editor {}
interface EditorEx extends Editor {
MarkupModelEx getMarkupModel();
}
interface MarkupModelEx {}
class Test {
void m() {
MarkupModelEx m = <caret>
}
}
@@ -0,0 +1,19 @@
interface Editor {}
interface EditorEx extends Editor {
MarkupModelEx getMarkupModel();
}
interface MarkupModelEx {}
class Test {
void m(Editor editor) {
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
((EditorEx)editor).getMarkupModel();
}
}
@@ -23,7 +23,9 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compiler.chainsSearch.ChainRelevance;
import com.intellij.compiler.chainsSearch.completion.MethodChainCompletionContributor;
import com.intellij.compiler.chainsSearch.completion.lookup.JavaRelevantChainLookupElement;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.SkipSlowTestLocally;
import com.intellij.util.SmartList;
@@ -44,6 +46,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
Registry.get(MethodChainCompletionContributor.REGISTRY_KEY).setValue(true, myFixture.getTestRootDisposable());
myDefaultAutoCompleteOnCodeCompletion = CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_CODE_COMPLETION;
CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = false;
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8);
}
@Override
@@ -240,6 +243,20 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
assertEquals("psiElement.getProject", element.getLookupString());
}
public void testChainWithCastOnContextVariable() {
JavaRelevantChainLookupElement element = assertOneElement(doCompletion());
assertEquals("(EditorEx)editor.getMarkupModel", element.toString());
}
public void testChainWithCastOnVariableOutsideContext() {
assertEmpty(doCompletion());
}
public void testChainWithCastOnStaticMethod() {
JavaRelevantChainLookupElement element = assertOneElement(doCompletion());
assertEquals("(InspectionManagerEx)getInstance().createContext", element.toString());
}
public void assertAdvisorLookupElementEquals(String lookupText,
int unreachableParametersCount,
int chainSize,
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -21,10 +21,7 @@ import com.sun.source.util.*;
import com.sun.tools.javac.util.ClientCodeException;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import org.jetbrains.jps.javac.ast.api.JavacNameTable;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import org.jetbrains.jps.javac.ast.api.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@@ -230,6 +227,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
myFileData = new JavacFileData(filePath,
createReferenceHolder(),
myDivideImportRefs ? createReferenceHolder() : EMPTY_T_OBJ_INT_MAP,
new ArrayList<JavacTypeCast>(),
createDefinitionHolder());
myTreeHelper = new JavacTreeHelper(unitTree, myTreeUtility);
}
@@ -242,6 +240,10 @@ final class JavacReferenceCollectorListener implements TaskListener {
myFileData.getDefs().add(def);
}
public void sinkTypeCast(JavacTypeCast typeCast) {
myFileData.getCasts().add(typeCast);
}
@Nullable
JavacRef.JavacElementRefBase asJavacRef(Element element) {
return asJavacRef(element, null);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -21,6 +21,7 @@ import com.sun.source.util.TreeScanner;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacNameTable;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
import javax.lang.model.element.*;
import javax.lang.model.type.ArrayType;
@@ -206,6 +207,24 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
return null;
}
@Override
public Tree visitTypeCast(TypeCastTree node, JavacReferenceCollectorListener.ReferenceCollector collector) {
super.visitTypeCast(node, collector);
Element castType = collector.getReferencedElement(node.getType());
if (castType == null) return null;
JavacRef.JavacElementRefBase castTypeElement = collector.asJavacRef(castType);
if (!(castTypeElement instanceof JavacRef.JavacClass)) return null;
TypeMirror operandType = collector.getType(node.getExpression());
if (operandType == null) return null;
JavacRef.JavacElementRefBase operandTypeElement = collector.asJavacRef(operandType);
if (!(operandTypeElement instanceof JavacRef.JavacClass)) return null;
collector.sinkTypeCast(new JavacTypeCast((JavacRef.JavacClass) operandTypeElement, (JavacRef.JavacClass) castTypeElement));
return null;
}
static JavacTreeRefScanner createASTScanner() {
try {
Class aClass = Class.forName("org.jetbrains.jps.javac.ast.Javac8RefScanner");
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -35,15 +35,18 @@ public class JavacFileData {
private final String myFilePath;
private final TObjectIntHashMap<JavacRef> myRefs;
private final TObjectIntHashMap<JavacRef> myImportRefs;
private final List<JavacTypeCast> myCasts;
private final List<JavacDef> myDefs;
public JavacFileData(@NotNull String path,
@NotNull TObjectIntHashMap<JavacRef> refs,
@NotNull TObjectIntHashMap<JavacRef> importRefs,
@NotNull List<JavacTypeCast> casts,
@NotNull List<JavacDef> defs) {
myFilePath = path;
myRefs = refs;
myImportRefs = importRefs;
myCasts = casts;
myDefs = defs;
}
@@ -62,6 +65,11 @@ public class JavacFileData {
return myImportRefs;
}
@NotNull
public List<JavacTypeCast> getCasts() {
return myCasts;
}
@NotNull
public List<JavacDef> getDefs() {
return myDefs;
@@ -75,6 +83,7 @@ public class JavacFileData {
stream.writeUTF(getFilePath());
saveRefs(stream, getRefs());
saveRefs(stream, getImportRefs());
saveCasts(stream, getCasts());
saveDefs(stream, getDefs());
}
catch (IOException e) {
@@ -91,6 +100,7 @@ public class JavacFileData {
return new JavacFileData(in.readUTF(),
readRefs(in),
readRefs(in),
readCasts(in),
readDefs(in));
}
catch (IOException e) {
@@ -246,4 +256,24 @@ public class JavacFileData {
});
return modifierList.isEmpty() ? Collections.<Modifier>emptySet() : EnumSet.copyOf(modifierList);
}
private static void saveCasts(@NotNull final DataOutput output, @NotNull List<JavacTypeCast> casts) throws IOException {
DataInputOutputUtilRt.writeSeq(output, casts, new ThrowableConsumer<JavacTypeCast, IOException>() {
@Override
public void consume(JavacTypeCast cast) throws IOException {
writeJavacRef(output, cast.getOperandType());
writeJavacRef(output, cast.getCastType());
}
});
}
@NotNull
private static List<JavacTypeCast> readCasts(@NotNull final DataInput input) throws IOException {
return DataInputOutputUtilRt.readSeq(input, new ThrowableComputable<JavacTypeCast, IOException>() {
@Override
public JavacTypeCast compute() throws IOException {
return new JavacTypeCast((JavacRef.JavacClass)readJavacRef(input), (JavacRef.JavacClass)readJavacRef(input));
}
});
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 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.
*/
package org.jetbrains.jps.javac.ast.api;
import org.jetbrains.annotations.NotNull;
public class JavacTypeCast {
@NotNull
private final JavacRef.JavacClass myOperandType;
@NotNull
private final JavacRef.JavacClass myCastType;
public JavacTypeCast(@NotNull JavacRef.JavacClass operandType, @NotNull JavacRef.JavacClass castType) {
myOperandType = operandType;
myCastType = castType;
}
@NotNull
public JavacRef.JavacClass getOperandType() {
return myOperandType;
}
@NotNull
public JavacRef.JavacClass getCastType() {
return myCastType;
}
}
@@ -24,25 +24,31 @@ import gnu.trove.TObjectIntHashMap;
import org.jetbrains.jps.backwardRefs.index.CompiledFileData;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
import java.io.IOException;
import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class BackwardReferenceIndexUtil {
private static final Logger LOG = Logger.getInstance(BackwardReferenceIndexUtil.class);
static void registerFile(String filePath,
TObjectIntHashMap<? extends JavacRef> refs,
List<JavacDef> defs,
Collection<JavacDef> defs,
Collection<JavacTypeCast> casts,
final BackwardReferenceIndexWriter writer) {
try {
final int fileId = writer.enumeratePath(filePath);
int funExprId = 0;
final Map<LightRef, Void> definitions = new HashMap<>(defs.size());
final Map<LightRef, Collection<LightRef>> backwardHierarchyMap = new HashMap<>();
final Map<SignatureData, Collection<LightRef>> signatureData = new THashMap<>();
Map<LightRef, Void> definitions = new HashMap<>(defs.size());
Map<LightRef, Collection<LightRef>> backwardHierarchyMap = new HashMap<>();
Map<SignatureData, Collection<LightRef>> signatureData = new THashMap<>();
THashMap<LightRef, Collection<LightRef>> castMap = new THashMap<>();
final AnonymousClassEnumerator anonymousClassEnumerator = new AnonymousClassEnumerator();
@@ -109,7 +115,16 @@ public class BackwardReferenceIndexUtil {
if (exception[0] != null) {
throw exception[0];
}
writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, convertedRefs, definitions, signatureData));
for (JavacTypeCast cast : casts) {
LightRef enumeratedCastType = writer.enumerateNames(cast.getCastType(), name -> null);
if (enumeratedCastType == null) continue;
LightRef enumeratedOperandType = writer.enumerateNames(cast.getOperandType(), name -> null);
if (enumeratedOperandType == null) continue;
castMap.computeIfAbsent(enumeratedCastType, t -> new SmartList<>()).add(enumeratedOperandType);
}
writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData));
}
catch (IOException e) {
writer.setRebuildCause(e);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -16,11 +16,12 @@
package org.jetbrains.jps.backwardRefs;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import java.util.List;
import java.util.Collection;
public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar {
private volatile BackwardReferenceIndexWriter myWriter;
@@ -41,7 +42,10 @@ public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar
}
@Override
public void registerFile(String filePath, TObjectIntHashMap<JavacRef> refs, List<JavacDef> defs) {
BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, myWriter);
public void registerFile(String filePath,
TObjectIntHashMap<JavacRef> refs,
Collection<JavacDef> defs,
Collection<JavacTypeCast> casts) {
BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, casts, myWriter);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -24,15 +24,18 @@ import java.util.Map;
public class CompiledFileData {
private final Map<LightRef, Collection<LightRef>> myBackwardHierarchyMap;
private final Map<LightRef, Collection<LightRef>> myCasts;
private final Map<LightRef, Integer> myReferences;
private final Map<LightRef, Void> myDefinitions;
private final Map<SignatureData, Collection<LightRef>> mySignatureData;
public CompiledFileData(@NotNull Map<LightRef, Collection<LightRef>> backwardHierarchyMap,
@NotNull Map<LightRef, Collection<LightRef>> casts,
@NotNull Map<LightRef, Integer> references,
@NotNull Map<LightRef, Void> definitions,
@NotNull Map<SignatureData, Collection<LightRef>> signatureData) {
myBackwardHierarchyMap = backwardHierarchyMap;
myCasts = casts;
myReferences = references;
myDefinitions = definitions;
mySignatureData = signatureData;
@@ -57,4 +60,9 @@ public class CompiledFileData {
public Map<SignatureData, Collection<LightRef>> getSignatureData() {
return mySignatureData;
}
@NotNull
public Map<LightRef, Collection<LightRef>> getCasts() {
return myCasts;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -16,7 +16,9 @@
package org.jetbrains.jps.backwardRefs.index;
import com.intellij.openapi.util.io.DataInputOutputUtilRt;
import com.intellij.util.indexing.*;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.IndexExtension;
import com.intellij.util.indexing.IndexId;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.KeyDescriptor;
@@ -35,18 +37,53 @@ import java.util.List;
public class CompilerIndices {
//TODO manage version separately
public final static int VERSION = 5;
public final static int VERSION = 6;
public final static IndexId<LightRef, Integer> BACK_USAGES = IndexId.create("back.refs");
public final static IndexId<LightRef, Collection<LightRef>> BACK_HIERARCHY = IndexId.create("back.hierarchy");
public final static IndexId<LightRef, Void> BACK_CLASS_DEF = IndexId.create("back.class.def");
public final static IndexId<SignatureData, Collection<LightRef>> BACK_MEMBER_SIGN = IndexId.create("back.member.sign");
public final static IndexId<LightRef, Collection<LightRef>> BACK_CAST = IndexId.create("back.cast");
public static List<IndexExtension<?, ?, CompiledFileData>> getIndices() {
return Arrays.asList(createBackwardClassDefinitionExtension(),
createBackwardUsagesExtension(),
createBackwardHierarchyExtension(),
createBackwardSignatureExtension());
createBackwardSignatureExtension(),
createBackwardCastExtension());
}
private static IndexExtension<LightRef, Collection<LightRef>, CompiledFileData> createBackwardCastExtension() {
return new IndexExtension<LightRef, Collection<LightRef>, CompiledFileData>() {
@NotNull
@Override
public IndexId<LightRef, Collection<LightRef>> getName() {
return BACK_CAST;
}
@NotNull
@Override
public DataIndexer<LightRef, Collection<LightRef>, CompiledFileData> getIndexer() {
return CompiledFileData::getCasts;
}
@NotNull
@Override
public KeyDescriptor<LightRef> getKeyDescriptor() {
return LightRefDescriptor.INSTANCE;
}
@NotNull
@Override
public DataExternalizer<Collection<LightRef>> getValueExternalizer() {
return createLightRefSeqExternalizer();
}
@Override
public int getVersion() {
return VERSION;
}
};
}
private static IndexExtension<LightRef, Integer, CompiledFileData> createBackwardUsagesExtension() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -78,7 +78,7 @@ public class InProcessRefCollectorCompilerToolExtension extends AbstractRefColle
public void consume(JavacFileData data) {
for (JavacFileReferencesRegistrar registrar : myRegistrars) {
if (registrar.isEnabled()) {
registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs());
registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs(), data.getCasts());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -17,7 +17,7 @@ package org.jetbrains.jps.javac.ast.api;
import gnu.trove.TObjectIntHashMap;
import java.util.List;
import java.util.Collection;
public interface JavacFileReferencesRegistrar {
void initialize();
@@ -26,5 +26,8 @@ public interface JavacFileReferencesRegistrar {
boolean onlyImports();
void registerFile(String filePath, TObjectIntHashMap<JavacRef> refs, List<JavacDef> defs);
void registerFile(String filePath,
TObjectIntHashMap<JavacRef> refs,
Collection<JavacDef> defs,
Collection<JavacTypeCast> casts);
}
@@ -0,0 +1,5 @@
class Foo {
void m(CharSequence c) {
((String)c).getBytes();
}
}
@@ -0,0 +1,18 @@
Backward Hierarchy:
java.lang.Object -> Foo
Backward References:
Foo in Foo occurrences = 1
Foo.m(1) in Foo occurrences = 1
java.lang.CharSequence in Foo occurrences = 1
java.lang.String in Foo occurrences = 1
java.lang.String.getBytes(0) in Foo occurrences = 1
Class Definitions:
Foo in Foo
Members Signatures:
Type Casts:
java.lang.String -> java.lang.CharSequence
@@ -0,0 +1,7 @@
import java.util.*;
class Foo {
void m(Object[] os) {
int length = ((String[]) os).length;
}
}
@@ -0,0 +1,14 @@
Backward Hierarchy:
java.lang.Object -> Foo
Backward References:
Array.length in Foo occurrences = 1
Foo in Foo occurrences = 1
Foo.m(1) in Foo occurrences = 1
java.lang.Object in Foo occurrences = 1
java.lang.String in Foo occurrences = 1
Class Definitions:
Foo in Foo
Members Signatures:
@@ -0,0 +1,7 @@
import java.util.*;
class Foo {
void m(Collection<String> c) {
((List<String>)c).get(1);
}
}
@@ -0,0 +1,19 @@
Backward Hierarchy:
java.lang.Object -> Foo
Backward References:
Foo in Foo occurrences = 1
Foo.m(1) in Foo occurrences = 1
java.lang.String in Foo occurrences = 2
java.util.Collection in Foo occurrences = 1
java.util.List in Foo occurrences = 1
java.util.List.get(1) in Foo occurrences = 1
Class Definitions:
Foo in Foo
Members Signatures:
Type Casts:
java.util.List -> java.util.Collection
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -181,5 +181,17 @@ class ReferenceIndexTest : ReferenceIndexTestBase() {
fun testDefaultConstructorUsage() {
assertIndexOnRebuild("Foo.java")
}
fun testCastData() {
assertIndexOnRebuild("Foo.java")
}
fun testCastDataArrays() {
assertIndexOnRebuild("Foo.java")
}
fun testCastDataGenerics() {
assertIndexOnRebuild("Foo.java")
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -160,6 +160,25 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() {
signs.sort()
result.append(signs.joinToString(separator = "\n"))
val typeCasts = mutableListOf<String>()
storage(index, CompilerIndices.BACK_CAST).processKeys {castType ->
val operands = mutableListOf<String>()
val valueIt = index[CompilerIndices.BACK_CAST].getData(castType).valueIterator
while (valueIt.hasNext()) {
val nextRefs = valueIt.next()
nextRefs.mapTo(operands) { it.asText(nameEnumerator) }
}
if (!operands.isEmpty()) {
typeCasts.add(castType.asText(nameEnumerator) + " -> " + operands.joinToString(separator = " "))
}
true
}
if (typeCasts.isNotEmpty()) {
result.append("\n\nType Casts:\n")
typeCasts.sort()
result.append(typeCasts.joinToString(separator = "\n"))
}
return result.toString()
}
finally {