mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'db/method-chains-with-casts'
# Conflicts: # java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java
This commit is contained in:
+19
@@ -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)) {
|
||||
|
||||
+10
-3
@@ -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)
|
||||
|
||||
+57
-7
@@ -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);
|
||||
}
|
||||
}
|
||||
+25
-15
@@ -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) {
|
||||
|
||||
+5
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
+57
-43
@@ -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;
|
||||
|
||||
+3
-3
@@ -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()
|
||||
|
||||
+31
-11
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface Editor {}
|
||||
interface EditorEx extends Editor {
|
||||
MarkupModelEx getMarkupModel();
|
||||
}
|
||||
interface MarkupModelEx {}
|
||||
|
||||
class Test {
|
||||
void m(Editor editor) {
|
||||
MarkupModelEx m = <caret>
|
||||
}
|
||||
}
|
||||
+19
@@ -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();
|
||||
}
|
||||
}
|
||||
+15
@@ -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>
|
||||
}
|
||||
}
|
||||
+24
@@ -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();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface Editor {}
|
||||
interface EditorEx extends Editor {
|
||||
MarkupModelEx getMarkupModel();
|
||||
}
|
||||
interface MarkupModelEx {}
|
||||
|
||||
class Test {
|
||||
void m() {
|
||||
MarkupModelEx m = <caret>
|
||||
}
|
||||
}
|
||||
+19
@@ -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();
|
||||
}
|
||||
}
|
||||
+17
@@ -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,
|
||||
|
||||
+7
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
+21
-6
@@ -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() {
|
||||
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user