java fun expr search: search at up-to-date offsets (EA-115728 - assert: JavaFunctionalExpressionSearcher.lambda$processFile$)

This commit is contained in:
peter
2018-07-19 09:52:02 +02:00
parent 7abaf7e4df
commit 11c5595382
3 changed files with 72 additions and 77 deletions
@@ -16,6 +16,7 @@
package com.intellij.psi.impl.java;
import com.google.common.base.MoreObjects;
import com.intellij.openapi.util.io.DataInputOutputUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.search.ApproximateResolver;
@@ -30,7 +31,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -38,19 +38,14 @@ import java.util.Set;
* @author peter
*/
public class FunExprOccurrence {
public final int funExprOffset;
private final int argIndex;
private final List<ReferenceChainLink> referenceContext;
public FunExprOccurrence(int funExprOffset,
int argIndex,
List<ReferenceChainLink> referenceContext) {
this.funExprOffset = funExprOffset;
public FunExprOccurrence(int argIndex, List<ReferenceChainLink> referenceContext) {
this.argIndex = argIndex;
this.referenceContext = referenceContext;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -58,7 +53,6 @@ public class FunExprOccurrence {
FunExprOccurrence that = (FunExprOccurrence)o;
if (funExprOffset != that.funExprOffset) return false;
if (argIndex != that.argIndex) return false;
if (!referenceContext.equals(that.referenceContext)) return false;
@@ -67,41 +61,25 @@ public class FunExprOccurrence {
@Override
public int hashCode() {
int result = funExprOffset;
result = 31 * result + argIndex;
result = 31 * result + referenceContext.hashCode();
return result;
return 31 * argIndex + referenceContext.hashCode();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("offset", funExprOffset)
.add("argIndex", argIndex)
.add("chain", referenceContext)
.toString();
}
void serialize(DataOutput out) throws IOException {
DataInputOutputUtil.writeINT(out, funExprOffset);
DataInputOutputUtil.writeINT(out, argIndex);
DataInputOutputUtil.writeINT(out, referenceContext.size());
for (ReferenceChainLink link : referenceContext) {
serializeLink(out, link);
}
DataInputOutputUtilRt.writeSeq(out, referenceContext, link -> serializeLink(out, link));
}
static FunExprOccurrence deserialize(DataInput in) throws IOException {
int offset = DataInputOutputUtil.readINT(in);
int argIndex = DataInputOutputUtil.readINT(in);
int contextSize = DataInputOutputUtil.readINT(in);
List<ReferenceChainLink> context = new ArrayList<>(contextSize);
for (int i = 0; i < contextSize; i++) {
context.add(deserializeLink(in));
}
return new FunExprOccurrence(offset, argIndex, context);
return new FunExprOccurrence(argIndex, DataInputOutputUtilRt.readSeq(in, () -> deserializeLink(in)));
}
private static void serializeLink(DataOutput out, ReferenceChainLink link) throws IOException {
@@ -35,7 +35,6 @@ import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.indexing.*;
@@ -54,8 +53,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<FunctionalExpressionKey, List<FunExprOccurrence>> implements PsiDependentIndex {
public static final ID<FunctionalExpressionKey, List<FunExprOccurrence>> INDEX_ID = ID.create("java.fun.expression");
public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<FunctionalExpressionKey, Map<Integer, FunExprOccurrence>> implements PsiDependentIndex {
public static final ID<FunctionalExpressionKey, Map<Integer, FunExprOccurrence>> INDEX_ID = ID.create("java.fun.expression");
private static final KeyDescriptor<FunctionalExpressionKey> KEY_DESCRIPTOR = new KeyDescriptor<FunctionalExpressionKey>() {
@Override
public int getHashCode(FunctionalExpressionKey value) {
@@ -339,18 +338,18 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<Funct
@Override
public int getVersion() {
return 2;
return 3;
}
@NotNull
@Override
public ID<FunctionalExpressionKey, List<FunExprOccurrence>> getName() {
public ID<FunctionalExpressionKey, Map<Integer, FunExprOccurrence>> getName() {
return INDEX_ID;
}
@NotNull
@Override
public DataIndexer<FunctionalExpressionKey, List<FunExprOccurrence>, FileContent> getIndexer() {
public DataIndexer<FunctionalExpressionKey, Map<Integer, FunExprOccurrence>, FileContent> getIndexer() {
return inputData -> {
CharSequence text = inputData.getContentAsText();
int[] offsets = ArrayUtil.mergeArrays(
@@ -358,7 +357,7 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<Funct
new StringSearcher("::", true, true).findAllOccurrences(text));
if (offsets.length == 0) return Collections.emptyMap();
Map<FunctionalExpressionKey, List<FunExprOccurrence>> result = new HashMap<>();
Map<FunctionalExpressionKey, Map<Integer, FunExprOccurrence>> result = new HashMap<>();
LighterAST tree = ((FileContentImpl)inputData).getLighterASTForPsiDependentIndex();
FileLocalResolver resolver = new FileLocalResolver(tree);
@@ -371,11 +370,8 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<Funct
FunctionalExpressionKey key = new FunctionalExpressionKey(getFunExprParameterCount(tree, element),
calcReturnType(tree, element),
calcExprType(element, resolver));
List<FunExprOccurrence> list = result.get(key);
if (list == null) {
result.put(key, list = new SmartList<>());
}
list.add(createOccurrence(element, resolver));
Map<Integer, FunExprOccurrence> map = result.computeIfAbsent(key, __ -> new LinkedHashMap<>());
map.put(element.getStartOffset(), createOccurrence(element, resolver));
}
}
@@ -398,30 +394,31 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<Funct
}
}
return new FunExprOccurrence(funExpr.getStartOffset(), argIndex,
createCallChain(resolver, chainExpr));
return new FunExprOccurrence(argIndex, createCallChain(resolver, chainExpr));
}
@NotNull
@Override
public DataExternalizer<List<FunExprOccurrence>> getValueExternalizer() {
return new DataExternalizer<List<FunExprOccurrence>>() {
public DataExternalizer<Map<Integer, FunExprOccurrence>> getValueExternalizer() {
return new DataExternalizer<Map<Integer, FunExprOccurrence>>() {
@Override
public void save(@NotNull DataOutput out, List<FunExprOccurrence> value) throws IOException {
public void save(@NotNull DataOutput out, Map<Integer, FunExprOccurrence> value) throws IOException {
DataInputOutputUtil.writeINT(out, value.size());
for (FunExprOccurrence info : value) {
info.serialize(out);
for (Map.Entry<Integer, FunExprOccurrence> entry : value.entrySet()) {
DataInputOutputUtil.writeINT(out, entry.getKey());
entry.getValue().serialize(out);
}
}
@Override
public List<FunExprOccurrence> read(@NotNull DataInput in) throws IOException {
public Map<Integer, FunExprOccurrence> read(@NotNull DataInput in) throws IOException {
int length = DataInputOutputUtil.readINT(in);
List<FunExprOccurrence> list = new SmartList<>();
Map<Integer, FunExprOccurrence> map = new LinkedHashMap<>();
for (int i = 0; i < length; i++) {
list.add(FunExprOccurrence.deserialize(in));
int offset = DataInputOutputUtil.readINT(in);
map.put(offset, FunExprOccurrence.deserialize(in));
}
return list;
return map;
}
};
}
@@ -54,8 +54,6 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
public void processQuery(@NotNull SearchParameters p, @NotNull Processor<? super PsiFunctionalExpression> consumer) {
List<SamDescriptor> descriptors = calcDescriptors(p);
Project project = PsiUtilCore.getProjectInReadAction(p.getElementToSearch());
if (project == null) return;
SearchScope searchScope = ReadAction.compute(() -> p.getEffectiveSearchScope());
if (searchScope instanceof GlobalSearchScope && !performSearchUsingCompilerIndices(descriptors,
(GlobalSearchScope)searchScope,
@@ -70,10 +68,10 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
PsiManager manager = ReadAction.compute(() -> p.getElementToSearch().getManager());
manager.startBatchFilesProcessingMode();
try {
processOffsets(descriptors, project, (file, offsets) -> {
processOffsets(descriptors, project, (file, occurrences) -> {
fileCount.incrementAndGet();
exprCount.addAndGet(offsets.size());
return processFile(consumer, descriptors, file, offsets);
exprCount.addAndGet(occurrences.size());
return processFile(consumer, descriptors, file, occurrences);
});
}
finally {
@@ -128,22 +126,26 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
@NotNull
private static MultiMap<VirtualFile, FunExprOccurrence> getAllOccurrences(List<SamDescriptor> descriptors) {
MultiMap<VirtualFile, FunExprOccurrence> result = MultiMap.createLinkedSet();
for (SamDescriptor descriptor : descriptors) {
descriptor.dumbService.runReadActionInSmartMode(() -> {
for (FunctionalExpressionKey key : descriptor.generateKeys()) {
FileBasedIndex.getInstance().processValues(JavaFunctionalExpressionIndex.INDEX_ID, key, null, (file, infos) -> {
ProgressManager.checkCanceled();
result.putValues(file, infos);
return true;
}, new JavaSourceFilterScope(descriptor.effectiveUseScope));
}
});
}
descriptors.get(0).dumbService.runReadActionInSmartMode(() -> processIndexValues(descriptors, null, (file, infos) -> {
result.putValues(file, infos.values());
return true;
}));
LOG.debug("Found " + result.values().size() + " fun-expressions in " + result.keySet().size() + " files");
return result;
}
private static void processOffsets(List<SamDescriptor> descriptors, Project project, PairProcessor<VirtualFile, List<Integer>> processor) {
private static void processIndexValues(List<SamDescriptor> descriptors,
VirtualFile inFile,
FileBasedIndex.ValueProcessor<Map<Integer, FunExprOccurrence>> processor) {
for (SamDescriptor descriptor : descriptors) {
GlobalSearchScope scope = new JavaSourceFilterScope(descriptor.effectiveUseScope);
for (FunctionalExpressionKey key : descriptor.keys) {
FileBasedIndex.getInstance().processValues(JavaFunctionalExpressionIndex.INDEX_ID, key, inFile, processor, scope);
}
}
}
private static void processOffsets(List<SamDescriptor> descriptors, Project project, PairProcessor<VirtualFile, Set<FunExprOccurrence>> processor) {
if (descriptors.isEmpty()) return;
List<PsiClass> samClasses = ContainerUtil.map(descriptors, d -> d.samClass);
@@ -151,10 +153,10 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
if (allCandidates.isEmpty()) return;
for (VirtualFile vFile : putLikelyFilesFirst(descriptors, allCandidates.keySet(), project)) {
List<FunExprOccurrence> toLoad = filterInapplicable(samClasses, vFile, allCandidates.get(vFile), project);
Set<FunExprOccurrence> toLoad = filterInapplicable(samClasses, vFile, allCandidates.get(vFile), project);
if (!toLoad.isEmpty()) {
LOG.trace("To load " + vFile.getPath() + " with values: " + toLoad);
if (!processor.process(vFile, ContainerUtil.map(toLoad, it -> it.funExprOffset))) {
if (!processor.process(vFile, toLoad)) {
return;
}
}
@@ -170,24 +172,25 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
}
@NotNull
private static List<FunExprOccurrence> filterInapplicable(List<PsiClass> samClasses,
VirtualFile vFile,
Collection<FunExprOccurrence> occurrences, Project project) {
private static Set<FunExprOccurrence> filterInapplicable(List<PsiClass> samClasses,
VirtualFile vFile,
Collection<FunExprOccurrence> occurrences, Project project) {
return DumbService.getInstance(project).runReadActionInSmartMode(
() -> project.isDisposed() ? Collections.emptyList()
: ContainerUtil.filter(occurrences, it -> it.canHaveType(samClasses, vFile)));
() -> new HashSet<>(ContainerUtil.filter(occurrences, it -> it.canHaveType(samClasses, vFile))));
}
private static boolean processFile(@NotNull Processor<? super PsiFunctionalExpression> consumer,
List<SamDescriptor> descriptors,
VirtualFile vFile, Collection<Integer> offsets) {
return ReadAction.compute(() -> {
VirtualFile vFile, Set<FunExprOccurrence> occurrences) {
return descriptors.get(0).dumbService.runReadActionInSmartMode(() -> {
PsiFile file = descriptors.get(0).samClass.getManager().findFile(vFile);
if (!(file instanceof PsiJavaFile)) {
LOG.error("Non-java file " + file + "; " + vFile);
return true;
}
List<Integer> offsets = getOccurrenceOffsets(descriptors, vFile, occurrences);
for (Integer offset : offsets) {
PsiFunctionalExpression expression = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiFunctionalExpression.class, false);
if (expression == null || expression.getTextRange().getStartOffset() != offset) {
@@ -204,6 +207,21 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
});
}
private static List<Integer> getOccurrenceOffsets(List<SamDescriptor> descriptors,
VirtualFile vFile,
Set<FunExprOccurrence> occurrences) {
List<Integer> offsets = new ArrayList<>();
processIndexValues(descriptors, vFile, (__, infos) -> {
for (Map.Entry<Integer, FunExprOccurrence> entry : infos.entrySet()) {
if (occurrences.contains(entry.getValue())) {
offsets.add(entry.getKey());
}
}
return true;
});
return offsets;
}
private static boolean hasType(List<SamDescriptor> descriptors, PsiFunctionalExpression expression) {
if (!canHaveType(expression, ContainerUtil.map(descriptors, d -> d.samClass))) return false;
@@ -268,6 +286,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
final boolean booleanCompatible;
final boolean isVoid;
final DumbService dumbService;
final List<FunctionalExpressionKey> keys;
GlobalSearchScope effectiveUseScope;
SamDescriptor(PsiClass samClass, PsiMethod samMethod, PsiType samType, GlobalSearchScope useScope) {
@@ -277,9 +296,10 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
this.booleanCompatible = FunctionalExpressionKey.isBooleanCompatible(samType);
this.isVoid = PsiType.VOID.equals(samType);
this.dumbService = DumbService.getInstance(samClass.getProject());
keys = generateKeys();
}
List<FunctionalExpressionKey> generateKeys() {
private List<FunctionalExpressionKey> generateKeys() {
String name = samClass.isValid() ? samClass.getName() : null;
if (name == null) return Collections.emptyList();