mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
rewrite functional expression search to use stub index
This commit is contained in:
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author max
|
||||
*/
|
||||
package com.intellij.psi.impl.java.stubs.index;
|
||||
|
||||
import com.intellij.psi.PsiFunctionalExpression;
|
||||
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey;
|
||||
import com.intellij.psi.stubs.AbstractStubIndex;
|
||||
import com.intellij.psi.stubs.StubIndexKey;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JavaFunctionalExpressionIndex extends AbstractStubIndex<FunctionalExpressionKey, PsiFunctionalExpression> {
|
||||
private static final KeyDescriptor<FunctionalExpressionKey> KEY_DESCRIPTOR = new KeyDescriptor<FunctionalExpressionKey>() {
|
||||
@Override
|
||||
public int getHashCode(FunctionalExpressionKey value) {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(FunctionalExpressionKey val1, FunctionalExpressionKey val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NotNull DataOutput out, FunctionalExpressionKey value) throws IOException {
|
||||
value.serializeKey(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalExpressionKey read(@NotNull DataInput in) throws IOException {
|
||||
return FunctionalExpressionKey.deserializeKey(in);
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyDescriptor<FunctionalExpressionKey> getKeyDescriptor() {
|
||||
return KEY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public StubIndexKey<FunctionalExpressionKey, PsiFunctionalExpression> getKey() {
|
||||
return JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS;
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -37,6 +37,11 @@ public class JavaMethodParameterTypesIndex extends StringStubIndexExtension<PsiM
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return super.getVersion() + 1;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public StubIndexKey<String, PsiMethod> getKey() {
|
||||
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.psi.impl.search;
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.*;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.DataInputOutputUtil;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<String, Collection<JavaFunctionalExpressionIndex.IndexHolder>> implements PsiDependentIndex {
|
||||
static final ID<String, Collection<IndexHolder>> JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID = ID.create("java.functional.expression");
|
||||
private static final String THIS_REF_NAME = "this";
|
||||
private static final String SUPER_REF_NAME = "super";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ID<String, Collection<IndexHolder>> getName() {
|
||||
return JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataIndexer<String, Collection<IndexHolder>, FileContent> getIndexer() {
|
||||
return inputData -> {
|
||||
if (!JavaStubElementTypes.JAVA_FILE.shouldBuildStubFor(inputData.getFile())) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
final CharSequence contentAsText = inputData.getContentAsText();
|
||||
if (!StringUtil.contains(contentAsText, "::") && !StringUtil.contains(contentAsText, "->")) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final PsiFile file = ((FileContentImpl)inputData).getPsiFileForPsiDependentIndex();
|
||||
if (!(file instanceof PsiJavaFile)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final HashMap<String, Collection<IndexHolder>> methodsMap = ContainerUtil.newHashMap();
|
||||
for (PsiFunctionalExpression expression : SyntaxTraverser.psiTraverser().withRoot(file).filter(PsiFunctionalExpression.class)) {
|
||||
final PsiExpressionList expressionList =
|
||||
PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, true, PsiStatement.class, PsiModifierListOwner.class);
|
||||
if (expressionList != null) {
|
||||
final PsiElement parent = expressionList.getParent();
|
||||
String methodName = null;
|
||||
if (parent instanceof PsiMethodCallExpression) {
|
||||
methodName = ((PsiMethodCallExpression)parent).getMethodExpression().getReferenceName();
|
||||
if (methodName != null) {
|
||||
final boolean thisRef = methodName.equals(THIS_REF_NAME);
|
||||
if (thisRef || methodName.equals(SUPER_REF_NAME)) {
|
||||
methodName = null;
|
||||
final PsiClass containingClass = PsiTreeUtil.getParentOfType(parent, PsiClass.class);
|
||||
if (containingClass != null) {
|
||||
if (thisRef) {
|
||||
methodName = containingClass.getName();
|
||||
} else {
|
||||
final PsiReferenceList extendsList = containingClass.getExtendsList();
|
||||
if (extendsList != null) {
|
||||
final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements();
|
||||
if (referenceElements.length > 0) {
|
||||
methodName = referenceElements[0].getReferenceName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiNewExpression) {
|
||||
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)parent).getClassOrAnonymousClassReference();
|
||||
if (classReference != null) {
|
||||
methodName = classReference.getReferenceName();
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiEnumConstant) {
|
||||
final PsiClass containingClass = ((PsiEnumConstant)parent).getContainingClass();
|
||||
if (containingClass != null) {
|
||||
final String shortEnumName = containingClass.getName();
|
||||
if (shortEnumName != null) { //should be always true as enums can't be local
|
||||
methodName = shortEnumName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (methodName != null) {
|
||||
Collection<IndexHolder> holders = methodsMap.get(methodName);
|
||||
if (holders == null) {
|
||||
holders = new HashSet<>();
|
||||
methodsMap.put(methodName, holders);
|
||||
}
|
||||
holders.add(new IndexHolder(expression instanceof PsiLambdaExpression ? ((PsiLambdaExpression)expression).getParameterList().getParametersCount() : -1,
|
||||
expressionList.getExpressions().length,
|
||||
LambdaUtil.getLambdaIdx(expressionList, expression)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return methodsMap;
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyDescriptor<String> getKeyDescriptor() {
|
||||
return EnumeratorStringDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataExternalizer<Collection<IndexHolder>> getValueExternalizer() {
|
||||
return new DataExternalizer<Collection<IndexHolder>>() {
|
||||
@Override
|
||||
public void save(@NotNull DataOutput out, Collection<IndexHolder> holders) throws IOException {
|
||||
DataInputOutputUtil.writeINT(out, holders.size());
|
||||
for (IndexHolder holder : holders) {
|
||||
DataInputOutputUtil.writeINT(out, holder.getLambdaParamsNumber());
|
||||
DataInputOutputUtil.writeINT(out, holder.getMethodArgsLength());
|
||||
DataInputOutputUtil.writeINT(out, holder.getFunctionExpressionIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IndexHolder> read(@NotNull DataInput in) throws IOException {
|
||||
int l = DataInputOutputUtil.readINT(in);
|
||||
final Collection<IndexHolder> holders = new HashSet<>(l);
|
||||
while (l-- > 0) {
|
||||
holders.add(new IndexHolder(DataInputOutputUtil.readINT(in),
|
||||
DataInputOutputUtil.readINT(in),
|
||||
DataInputOutputUtil.readINT(in)));
|
||||
}
|
||||
return holders;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileBasedIndex.InputFilter getInputFilter() {
|
||||
return new DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dependsOnFileContent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static class IndexHolder {
|
||||
private final int myLambdaParamsNumber;
|
||||
private final int myMethodArgsLength;
|
||||
private final int myFunctionExpressionIndex;
|
||||
|
||||
IndexHolder(int lambdaParamsNumber, int methodArgsLength, int functionExpressionIndex) {
|
||||
myLambdaParamsNumber = lambdaParamsNumber;
|
||||
myMethodArgsLength = methodArgsLength;
|
||||
myFunctionExpressionIndex = functionExpressionIndex;
|
||||
}
|
||||
|
||||
int getLambdaParamsNumber() {
|
||||
return myLambdaParamsNumber;
|
||||
}
|
||||
|
||||
int getMethodArgsLength() {
|
||||
return myMethodArgsLength;
|
||||
}
|
||||
|
||||
int getFunctionExpressionIndex() {
|
||||
return myFunctionExpressionIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
IndexHolder holder = (IndexHolder)o;
|
||||
|
||||
if (myLambdaParamsNumber != holder.myLambdaParamsNumber) return false;
|
||||
if (myMethodArgsLength != holder.myMethodArgsLength) return false;
|
||||
if (myFunctionExpressionIndex != holder.myFunctionExpressionIndex) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myLambdaParamsNumber;
|
||||
result = 31 * result + myMethodArgsLength;
|
||||
result = 31 * result + myFunctionExpressionIndex;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
-231
@@ -15,37 +15,43 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.search;
|
||||
|
||||
import com.intellij.openapi.application.*;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.QueryExecutorBase;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey;
|
||||
import com.intellij.psi.impl.java.stubs.JavaMethodElementType;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaMethodParameterTypesIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
|
||||
import com.intellij.psi.search.*;
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.stubs.StubIndex;
|
||||
import com.intellij.psi.stubs.StubIndexKey;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Processors;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunctionalExpression, FunctionalExpressionSearch.SearchParameters> {
|
||||
private static final Logger LOG = Logger.getInstance("#" + JavaFunctionalExpressionSearcher.class.getName());
|
||||
@@ -86,46 +92,136 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
|
||||
//collect all files with '::' and '->' in useScope
|
||||
Set<VirtualFile> candidateFiles = getFilesWithFunctionalExpressionsScope(project, new JavaSourceFilterScope(useScope));
|
||||
if (candidateFiles.size() < SMART_SEARCH_THRESHOLD) {
|
||||
searchInFiles(aClass, consumer, candidateFiles, expectedFunExprParamsCount, isVoid);
|
||||
if (candidateFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GlobalSearchScope candidateScope = GlobalSearchScope.filesScope(project, candidateFiles);
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries =
|
||||
collectQueryKeys(useScope, aClass, project, expectedFunExprParamsCount, isVoid, candidateFiles);
|
||||
|
||||
//collect all methods with parameter of functional interface or free type parameter type
|
||||
final Collection<PsiMethod> methodCandidates = getCandidateMethodsWithSuitableParams(aClass, project, useScope, candidateFiles, candidateScope);
|
||||
|
||||
final Set<VirtualFile> filesToProcess = new LinkedHashSet<>();
|
||||
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
|
||||
|
||||
//find all usages of method candidates in files with functional expressions
|
||||
for (final PsiMethod psiMethod : methodCandidates) {
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
if (!psiMethod.isValid()) return;
|
||||
final int parametersCount = psiMethod.getParameterList().getParametersCount();
|
||||
final boolean varArgs = psiMethod.isVarArgs();
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
final GlobalSearchScope methodUseScope = convertToGlobalScope(project, psiMethod.getUseScope());
|
||||
final LinkedHashMap<VirtualFile, Set<JavaFunctionalExpressionIndex.IndexHolder>> holders = new LinkedHashMap<>();
|
||||
//functional expressions checker: number and type of parameters at call site should correspond to candidate method currently check
|
||||
final SuitableFilesProcessor processor = new SuitableFilesProcessor(holders, expectedFunExprParamsCount, parametersCount, varArgs, parameters);
|
||||
fileBasedIndex.processValues(JavaFunctionalExpressionIndex.JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID, psiMethod.getName(), null, processor, useScope.intersectWith(methodUseScope));
|
||||
for (Map.Entry<VirtualFile, Set<JavaFunctionalExpressionIndex.IndexHolder>> entry : holders.entrySet()) {
|
||||
for (JavaFunctionalExpressionIndex.IndexHolder holder : entry.getValue()) {
|
||||
if (processor.canBeFunctional(holder)) {
|
||||
filesToProcess.add(entry.getKey());
|
||||
break;
|
||||
}
|
||||
for (PsiFunctionalExpression expression : getCandidates(useScope, project, queries)) {
|
||||
if (!ReadAction.compute(() -> {
|
||||
if (expression.isValid() &&
|
||||
InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(expression.getFunctionalInterfaceType()), aClass, true)) {
|
||||
if (!consumer.process(expression)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<? extends PsiFunctionalExpression> getCandidates(GlobalSearchScope useScope,
|
||||
Project project,
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries) {
|
||||
MultiMap<PsiFile, PsiFunctionalExpression> exprs = MultiMap.createLinked();
|
||||
for (Map.Entry<FunctionalExpressionKey, Collection<GlobalSearchScope>> entry : queries.entrySet()) {
|
||||
ReadAction.run(() -> {
|
||||
ProgressManager.checkCanceled();
|
||||
GlobalSearchScope combinedScope = useScope.intersectWith(
|
||||
GlobalSearchScope.union(entry.getValue().toArray(new GlobalSearchScope[0])));
|
||||
StubIndex.getInstance().processElements(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS,
|
||||
entry.getKey(),
|
||||
project, combinedScope, null,
|
||||
PsiFunctionalExpression.class,
|
||||
expression -> {
|
||||
exprs.putValue(expression.getContainingFile(), expression);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//search for functional expressions in non-call contexts
|
||||
collectFilesWithTypeOccurrencesAndFieldAssignments(aClass, candidateScope, filesToProcess);
|
||||
Collection<? extends PsiFunctionalExpression> result = exprs.values();
|
||||
LOG.info("checking " + result.size() + " fun-expressions in " + exprs.size() + " files");
|
||||
return result;
|
||||
}
|
||||
|
||||
searchInFiles(aClass, consumer, filesToProcess, expectedFunExprParamsCount, isVoid);
|
||||
@NotNull
|
||||
private static MultiMap<FunctionalExpressionKey, GlobalSearchScope> collectQueryKeys(GlobalSearchScope useScope,
|
||||
PsiClass aClass,
|
||||
Project project,
|
||||
int samParamCount,
|
||||
boolean samVoid,
|
||||
Set<VirtualFile> candidateFiles) {
|
||||
//collect all methods with parameter of functional interface or free type parameter type
|
||||
Collection<PsiMethod> methodCandidates = getCandidateMethodsWithSuitableParams(aClass, project, useScope, candidateFiles, samParamCount, samVoid);
|
||||
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries = MultiMap.createSet();
|
||||
for (FunctionalExpressionKey key : generateKeys(samParamCount, samVoid, "", -1, -1)) {
|
||||
queries.putValue(key, useScope); // check all fun-exprs that aren't inside calls
|
||||
}
|
||||
|
||||
//find all usages of method candidates in files with functional expressions
|
||||
for (final PsiMethod psiMethod : methodCandidates) {
|
||||
ReadAction.run(() -> {
|
||||
if (!psiMethod.isValid()) return;
|
||||
|
||||
final GlobalSearchScope methodUseScope = convertToGlobalScope(project, psiMethod.getUseScope());
|
||||
for (FunctionalExpressionKey key : getQueryKeys(aClass, samParamCount, samVoid, psiMethod)) {
|
||||
queries.putValue(key, methodUseScope);
|
||||
}
|
||||
});
|
||||
}
|
||||
return queries;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<FunctionalExpressionKey> getQueryKeys(PsiClass samClass,
|
||||
int samParamCount,
|
||||
boolean samVoid,
|
||||
PsiMethod calledMethod) {
|
||||
Set<FunctionalExpressionKey> keys = new HashSet<>();
|
||||
|
||||
String methodName = calledMethod.getName();
|
||||
PsiParameter[] parameters = calledMethod.getParameterList().getParameters();
|
||||
for (int paramIndex = 0; paramIndex < parameters.length; paramIndex++) {
|
||||
PsiParameter parameter = parameters[paramIndex];
|
||||
if (canPassFunctionalExpression(samClass, parameter)) {
|
||||
for (int argCount : getPossibleArgCounts(parameters, paramIndex)) {
|
||||
for (int argIndex : getPossibleArgIndices(parameter, paramIndex, argCount)) {
|
||||
keys.addAll(generateKeys(samParamCount, samVoid, methodName, argCount, argIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static List<FunctionalExpressionKey> generateKeys(int samMethodParamsCount,
|
||||
boolean samMethodVoid,
|
||||
String methodName, int argCount, int argIndex) {
|
||||
List<FunctionalExpressionKey> result = new ArrayList<>();
|
||||
for (int lambdaParamCount : new int[]{FunctionalExpressionKey.UNKNOWN_PARAM_COUNT, samMethodParamsCount}) {
|
||||
result.add(new FunctionalExpressionKey(methodName, lambdaParamCount, argCount, argIndex, ThreeState.UNSURE));
|
||||
result.add(new FunctionalExpressionKey(methodName, lambdaParamCount, argCount, argIndex, ThreeState.fromBoolean(samMethodVoid)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int[] getPossibleArgCounts(PsiParameter[] parameters, int paramIndex) {
|
||||
if (parameters[parameters.length - 1].isVarArgs()) {
|
||||
return IntStream
|
||||
.rangeClosed(parameters.length - 1, FunctionalExpressionKey.MAX_ARG_COUNT)
|
||||
.filter(i -> i > paramIndex)
|
||||
.toArray();
|
||||
}
|
||||
return new int[]{Math.min(parameters.length, FunctionalExpressionKey.MAX_ARG_COUNT)};
|
||||
}
|
||||
|
||||
private static int[] getPossibleArgIndices(PsiParameter parameter, int paramIndex, int argCount) {
|
||||
if (parameter.isVarArgs()) {
|
||||
return IntStream
|
||||
.rangeClosed(paramIndex + 1, FunctionalExpressionKey.MAX_ARG_COUNT)
|
||||
.filter(i -> i < argCount)
|
||||
.toArray();
|
||||
}
|
||||
return new int[]{Math.min(paramIndex, FunctionalExpressionKey.MAX_ARG_COUNT)};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -145,51 +241,44 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
return highLevelModules;
|
||||
}
|
||||
|
||||
private static void searchInFiles(final PsiClass aClass,
|
||||
final Processor<PsiFunctionalExpression> consumer,
|
||||
Set<VirtualFile> filesToProcess, final int expectedFunExprParamsCount, boolean isVoid) {
|
||||
LOG.info("#usage files: " + filesToProcess.size());
|
||||
ContainerUtil.process(filesToProcess, new ReadActionProcessor<VirtualFile>() {
|
||||
@Override
|
||||
public boolean processInReadAction(VirtualFile file) {
|
||||
//resolve functional expressions to ensure that functional expression type is appropriate
|
||||
return !file.isValid() || processFileWithFunctionalInterfaces(aClass, expectedFunExprParamsCount, isVoid, consumer, file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Collection<PsiMethod> getCandidateMethodsWithSuitableParams(final PsiClass aClass,
|
||||
final Project project,
|
||||
final GlobalSearchScope useScope,
|
||||
final Set<VirtualFile> candidateFiles,
|
||||
final GlobalSearchScope candidateScope) {
|
||||
int expectedFunExprParamsCount,
|
||||
boolean isVoid) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Collection<PsiMethod>>() {
|
||||
@Override
|
||||
public Collection<PsiMethod> compute() {
|
||||
if (!aClass.isValid()) return Collections.emptyList();
|
||||
@Override
|
||||
public Collection<PsiMethod> compute() {
|
||||
if (!aClass.isValid()) return Collections.emptyList();
|
||||
|
||||
GlobalSearchScope visibleFromCandidates = combineResolveScopes(project, candidateFiles);
|
||||
GlobalSearchScope visibleFromCandidates = combineResolveScopes(project, candidateFiles);
|
||||
|
||||
Set<String> usedMethodNames = new HashSet<>();
|
||||
FileBasedIndex.getInstance().processAllKeys(JavaFunctionalExpressionIndex.JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID,
|
||||
Processors.cancelableCollectProcessor(usedMethodNames), candidateScope, null);
|
||||
Set<String> usedMethodNames = new HashSet<>();
|
||||
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key -> {
|
||||
ProgressManager.checkCanceled();
|
||||
if (key.canRepresent(expectedFunExprParamsCount, isVoid)) {
|
||||
usedMethodNames.add(key.methodName);
|
||||
}
|
||||
return true;
|
||||
}, useScope, null);
|
||||
|
||||
Set<PsiMethod> methods = ContainerUtil.newLinkedHashSet();
|
||||
Processor<PsiMethod> methodProcessor = method -> {
|
||||
if (usedMethodNames.contains(method.getName())) {
|
||||
methods.add(method);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Set<PsiMethod> methods = ContainerUtil.newLinkedHashSet();
|
||||
Processor<PsiMethod> methodProcessor = method -> {
|
||||
if (usedMethodNames.contains(method.getName())) {
|
||||
methods.add(method);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
StubIndexKey<String, PsiMethod> key = JavaMethodParameterTypesIndex.getInstance().getKey();
|
||||
StubIndex index = StubIndex.getInstance();
|
||||
index.processElements(key, aClass.getName(), project, useScope.intersectWith(visibleFromCandidates), PsiMethod.class, methodProcessor);
|
||||
index.processElements(key, JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME, project, visibleFromCandidates, PsiMethod.class, methodProcessor);
|
||||
LOG.info("#methods: " + methods.size());
|
||||
return methods;
|
||||
}
|
||||
});
|
||||
StubIndexKey<String, PsiMethod> key = JavaMethodParameterTypesIndex.getInstance().getKey();
|
||||
StubIndex index = StubIndex.getInstance();
|
||||
index.processElements(key, aClass.getName(), project, useScope.intersectWith(visibleFromCandidates), PsiMethod.class, methodProcessor);
|
||||
index.processElements(key, JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME, project, visibleFromCandidates, PsiMethod.class, methodProcessor);
|
||||
LOG.info("#methods: " + methods.size());
|
||||
return methods;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -228,168 +317,17 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect files where:
|
||||
* aClass is used, e.g. in type declaration or method return type;
|
||||
* fields with type aClass are used on the left side of assignments. Should find Bar of the following example
|
||||
<pre/>
|
||||
class Foo {
|
||||
Runnable myRunnable;
|
||||
|
||||
private static boolean canPassFunctionalExpression(PsiClass sam, PsiParameter parameter) {
|
||||
PsiType paramType = parameter.getType();
|
||||
if (paramType instanceof PsiEllipsisType) {
|
||||
paramType = ((PsiEllipsisType)paramType).getComponentType();
|
||||
}
|
||||
|
||||
class Bar {
|
||||
void foo(Foo foo){
|
||||
foo.myRunnable = () -> {};
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
private static void collectFilesWithTypeOccurrencesAndFieldAssignments(@NotNull PsiClass aClass,
|
||||
@NotNull GlobalSearchScope filesScope,
|
||||
@NotNull Set<VirtualFile> usageFiles) {
|
||||
final Set<PsiField> fields = new LinkedHashSet<>();
|
||||
for (final PsiReference reference : ReferencesSearch.search(aClass, filesScope)) {
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element != null) {
|
||||
ContainerUtil.addIfNotNull(usageFiles, PsiUtilCore.getVirtualFile(element));
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiTypeElement) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiField &&
|
||||
!((PsiField)gParent).hasModifierProperty(PsiModifier.PRIVATE) &&
|
||||
!((PsiField)gParent).hasModifierProperty(PsiModifier.FINAL)) {
|
||||
fields.add((PsiField)gParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
PsiClass functionalCandidate = PsiUtil.resolveClassInClassTypeOnly(paramType);
|
||||
if (functionalCandidate instanceof PsiTypeParameter) {
|
||||
return InheritanceUtil.isInheritorOrSelf(sam, PsiUtil.resolveClassInClassTypeOnly(TypeConversionUtil.erasure(paramType)), true);
|
||||
}
|
||||
|
||||
for (PsiField field : fields) {
|
||||
ReferencesSearch.search(field, filesScope).forEach(new ReadActionProcessor<PsiReference>() {
|
||||
@Override
|
||||
public boolean processInReadAction(PsiReference fieldRef) {
|
||||
final PsiElement fieldElement = fieldRef.getElement();
|
||||
final PsiAssignmentExpression varElementParent = PsiTreeUtil.getParentOfType(fieldElement, PsiAssignmentExpression.class);
|
||||
if (varElementParent != null && PsiTreeUtil.isAncestor(varElementParent.getLExpression(), fieldElement, false)) {
|
||||
ContainerUtil.addIfNotNull(usageFiles, PsiUtilCore.getVirtualFile(fieldElement));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean processFileWithFunctionalInterfaces(final PsiClass aClass,
|
||||
final int expectedParamCount,
|
||||
boolean isVoid,
|
||||
final Processor<PsiFunctionalExpression> consumer,
|
||||
VirtualFile file) {
|
||||
final PsiFile psiFile = aClass.getManager().findFile(file);
|
||||
if (psiFile != null) {
|
||||
final Ref<Boolean> ref = new Ref<>(true);
|
||||
psiFile.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (!ref.get()) {
|
||||
return;
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
|
||||
private void visitFunctionalExpression(PsiFunctionalExpression expression) {
|
||||
PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
|
||||
if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(functionalInterfaceType), aClass, true)) {
|
||||
if (!consumer.process(expression)) {
|
||||
ref.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
super.visitLambdaExpression(expression);
|
||||
if (expression.getParameterList().getParametersCount() == expectedParamCount) {
|
||||
if (isVoid) {
|
||||
final PsiElement body = expression.getBody();
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
final PsiReturnStatement[] statements = PsiUtil.findReturnStatements((PsiCodeBlock)body);
|
||||
for (PsiReturnStatement statement : statements) {
|
||||
if (statement.getReturnValue() != null) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
visitFunctionalExpression(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
|
||||
super.visitMethodReferenceExpression(expression);
|
||||
visitFunctionalExpression(expression);
|
||||
}
|
||||
});
|
||||
if (!ref.get()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class SuitableFilesProcessor implements FileBasedIndex.ValueProcessor<Collection<JavaFunctionalExpressionIndex.IndexHolder>> {
|
||||
private final Map<VirtualFile, Set<JavaFunctionalExpressionIndex.IndexHolder>> myHolders;
|
||||
private final int myExpectedFunExprParamsCount;
|
||||
private final int myParametersCount;
|
||||
private final boolean myVarArgs;
|
||||
private final PsiParameter[] myParameters;
|
||||
|
||||
SuitableFilesProcessor(Map<VirtualFile, Set<JavaFunctionalExpressionIndex.IndexHolder>> holders,
|
||||
int expectedFunExprParamsCount,
|
||||
int parametersCount,
|
||||
boolean varArgs,
|
||||
PsiParameter[] parameters) {
|
||||
myHolders = holders;
|
||||
myExpectedFunExprParamsCount = expectedFunExprParamsCount;
|
||||
myParametersCount = parametersCount;
|
||||
myVarArgs = varArgs;
|
||||
myParameters = parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(VirtualFile file, Collection<JavaFunctionalExpressionIndex.IndexHolder> holders) {
|
||||
Set<JavaFunctionalExpressionIndex.IndexHolder> savedHolders = myHolders.get(file);
|
||||
for (JavaFunctionalExpressionIndex.IndexHolder holder : holders) {
|
||||
final int lambdaParamsNumber = holder.getLambdaParamsNumber();
|
||||
if (lambdaParamsNumber == myExpectedFunExprParamsCount || lambdaParamsNumber == -1) {
|
||||
final boolean suitableParamNumbers;
|
||||
if (myVarArgs) {
|
||||
suitableParamNumbers = holder.getMethodArgsLength() >= myParametersCount - 1;
|
||||
}
|
||||
else {
|
||||
suitableParamNumbers = holder.getMethodArgsLength() == myParametersCount;
|
||||
}
|
||||
if (suitableParamNumbers) {
|
||||
if (savedHolders == null) {
|
||||
savedHolders = new LinkedHashSet<>();
|
||||
myHolders.put(file, savedHolders);
|
||||
}
|
||||
savedHolders.add(holder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canBeFunctional(JavaFunctionalExpressionIndex.IndexHolder holder) {
|
||||
final int paramIdx = holder.getFunctionExpressionIndex();
|
||||
PsiType paramType = myParameters[paramIdx >= myParametersCount ? myParametersCount - 1 : paramIdx].getType();
|
||||
if (paramType instanceof PsiEllipsisType) {
|
||||
paramType = ((PsiEllipsisType)paramType).getComponentType();
|
||||
}
|
||||
final PsiClass functionalCandidate = PsiUtil.resolveClassInClassTypeOnly(paramType);
|
||||
return functionalCandidate instanceof PsiTypeParameter ||
|
||||
LambdaUtil.isFunctionalClass(functionalCandidate);
|
||||
}
|
||||
return InheritanceUtil.isInheritorOrSelf(functionalCandidate, sam, true);
|
||||
}
|
||||
}
|
||||
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.psi.impl.java.stubs;
|
||||
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiFunctionalExpression;
|
||||
import com.intellij.psi.impl.cache.RecordUtil;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
|
||||
import com.intellij.psi.impl.source.Constants;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.LightTreeUtil;
|
||||
import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor;
|
||||
import com.intellij.psi.stubs.IndexSink;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.psi.stubs.StubOutputStream;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ThreeState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
|
||||
|
||||
public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExpression> extends JavaStubElementType<FunctionalExpressionStub<T>,T> {
|
||||
public FunctionalExpressionElementType(String debugName) {
|
||||
super(debugName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(@NotNull FunctionalExpressionStub<T> stub, @NotNull StubOutputStream dataStream) throws IOException {
|
||||
stub.getIndexKey().serializeKey(dataStream);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FunctionalExpressionStub<T> deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||
return new FunctionalExpressionStub<T>(parentStub, this, FunctionalExpressionKey.deserializeKey(dataStream));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indexStub(@NotNull FunctionalExpressionStub<T> stub, @NotNull IndexSink sink) {
|
||||
sink.occurrence(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, stub.getIndexKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalExpressionStub<T> createStub(LighterAST tree, LighterASTNode funExpr, StubElement parentStub) {
|
||||
LighterASTNode call = getContainingCall(tree, funExpr);
|
||||
List<LighterASTNode> args = getArgList(tree, call);
|
||||
int argCount = args == null ? -1 : args.size();
|
||||
int argIndex = args == null ? -1 : getArgIndex(args, funExpr);
|
||||
|
||||
int lambdaParamCount = getFunExprParameterCount(tree, funExpr);
|
||||
|
||||
String methodName = call != null && argIndex >= 0 ? getCalledMethodName(tree, call) : "";
|
||||
FunctionalExpressionKey key = new FunctionalExpressionKey(methodName, lambdaParamCount, argCount, argIndex, isVoid(tree, funExpr));
|
||||
return new FunctionalExpressionStub<T>(parentStub, this, key);
|
||||
}
|
||||
|
||||
private static int getArgIndex(List<LighterASTNode> args, LighterASTNode expr) {
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
if (args.get(i).getEndOffset() >= expr.getEndOffset()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<LighterASTNode> getArgList(LighterAST tree, LighterASTNode call) {
|
||||
LighterASTNode anonClass = LightTreeUtil.firstChildOfType(tree, call, ANONYMOUS_CLASS);
|
||||
LighterASTNode exprList = LightTreeUtil.firstChildOfType(tree, anonClass != null ? anonClass : call, EXPRESSION_LIST);
|
||||
return exprList == null ? null : LightTreeUtil.getChildrenOfType(tree, exprList, ElementType.EXPRESSION_BIT_SET);
|
||||
}
|
||||
|
||||
private static ThreeState isVoid(final LighterAST tree, LighterASTNode funExpr) {
|
||||
if (funExpr.getTokenType() == METHOD_REF_EXPRESSION) return ThreeState.UNSURE;
|
||||
|
||||
LighterASTNode block = LightTreeUtil.firstChildOfType(tree, funExpr, CODE_BLOCK);
|
||||
if (block == null) return ThreeState.UNSURE;
|
||||
|
||||
final Ref<ThreeState> isVoid = Ref.create(ThreeState.UNSURE);
|
||||
new RecursiveLighterASTNodeWalkingVisitor(tree) {
|
||||
@Override
|
||||
public void visitNode(@NotNull LighterASTNode element) {
|
||||
IElementType type = element.getTokenType();
|
||||
if (type == LAMBDA_EXPRESSION || ElementType.MEMBER_BIT_SET.contains(type) || isVoid.get() != ThreeState.UNSURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == RETURN_STATEMENT) {
|
||||
boolean noExpr = LightTreeUtil.firstChildOfType(tree, element, ElementType.EXPRESSION_BIT_SET) == null;
|
||||
isVoid.set(ThreeState.fromBoolean(noExpr));
|
||||
return;
|
||||
}
|
||||
|
||||
super.visitNode(element);
|
||||
}
|
||||
}.visitNode(block);
|
||||
|
||||
return isVoid.get();
|
||||
}
|
||||
|
||||
private static int getFunExprParameterCount(LighterAST tree, LighterASTNode funExpr) {
|
||||
if (funExpr.getTokenType() == METHOD_REF_EXPRESSION) return FunctionalExpressionKey.UNKNOWN_PARAM_COUNT;
|
||||
|
||||
assert funExpr.getTokenType() == LAMBDA_EXPRESSION;
|
||||
|
||||
LighterASTNode paramList = LightTreeUtil.firstChildOfType(tree, funExpr, PARAMETER_LIST);
|
||||
assert paramList != null;
|
||||
return LightTreeUtil.getChildrenOfType(tree, paramList, Constants.PARAMETER_BIT_SET).size();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getCalledMethodName(LighterAST tree, LighterASTNode call) {
|
||||
if (call.getTokenType() == NEW_EXPRESSION) {
|
||||
LighterASTNode anonClass = LightTreeUtil.firstChildOfType(tree, call, ANONYMOUS_CLASS);
|
||||
LighterASTNode ref = LightTreeUtil.firstChildOfType(tree, anonClass != null ? anonClass : call, JAVA_CODE_REFERENCE);
|
||||
return ref == null ? "" : getNameIdentifierText(tree, ref);
|
||||
}
|
||||
|
||||
LighterASTNode methodExpr = tree.getChildren(call).get(0);
|
||||
if (LightTreeUtil.firstChildOfType(tree, methodExpr, JavaTokenType.SUPER_KEYWORD) != null) {
|
||||
return getSuperClassName(tree, call);
|
||||
}
|
||||
if (LightTreeUtil.firstChildOfType(tree, methodExpr, JavaTokenType.THIS_KEYWORD) != null) {
|
||||
return getNameIdentifierText(tree, findClass(tree, call));
|
||||
}
|
||||
|
||||
return getNameIdentifierText(tree, methodExpr);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getSuperClassName(LighterAST tree, LighterASTNode call) {
|
||||
LighterASTNode aClass = findClass(tree, call);
|
||||
LighterASTNode extendsList = LightTreeUtil.firstChildOfType(tree, aClass, EXTENDS_LIST);
|
||||
return getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, extendsList, JAVA_CODE_REFERENCE));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getNameIdentifierText(LighterAST tree, LighterASTNode idOwner) {
|
||||
LighterASTNode id = LightTreeUtil.firstChildOfType(tree, idOwner, JavaTokenType.IDENTIFIER);
|
||||
return id != null ? RecordUtil.intern(tree.getCharTable(), id) : "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LighterASTNode getContainingCall(LighterAST tree, LighterASTNode node) {
|
||||
LighterASTNode expressionList = findExpressionList(tree, node);
|
||||
if (expressionList != null) {
|
||||
LighterASTNode parent = tree.getParent(expressionList);
|
||||
if (parent != null && parent.getTokenType() == ANONYMOUS_CLASS) {
|
||||
parent = tree.getParent(parent);
|
||||
}
|
||||
if (parent != null && (parent.getTokenType() == METHOD_CALL_EXPRESSION || parent.getTokenType() == NEW_EXPRESSION)) {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static LighterASTNode findExpressionList(LighterAST tree, LighterASTNode node) {
|
||||
while (node != null) {
|
||||
final IElementType type = node.getTokenType();
|
||||
if (ElementType.JAVA_STATEMENT_BIT_SET.contains(type) || ElementType.MEMBER_BIT_SET.contains(type)) return null;
|
||||
if (type == EXPRESSION_LIST) return node;
|
||||
node = tree.getParent(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static LighterASTNode findClass(LighterAST tree, LighterASTNode node) {
|
||||
while (node != null) {
|
||||
final IElementType type = node.getTokenType();
|
||||
if (type == CLASS) return node;
|
||||
node = tree.getParent(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.psi.impl.java.stubs;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.io.DataInputOutputUtil;
|
||||
import com.intellij.util.io.IOUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class FunctionalExpressionKey {
|
||||
public static final int UNKNOWN_PARAM_COUNT = -1;
|
||||
public static final int MAX_ARG_COUNT = 10;
|
||||
@NotNull public final String methodName;
|
||||
public final int lambdaParameterCount;
|
||||
public final int methodArgsLength;
|
||||
public final int callArgIndex;
|
||||
public final ThreeState isVoid;
|
||||
|
||||
public FunctionalExpressionKey(@NotNull String methodName,
|
||||
int lambdaParameterCount,
|
||||
int methodArgsLength,
|
||||
int callArgIndex,
|
||||
ThreeState isVoid) {
|
||||
this.methodName = methodName;
|
||||
this.lambdaParameterCount = lambdaParameterCount;
|
||||
this.methodArgsLength = Math.min(methodArgsLength, MAX_ARG_COUNT);
|
||||
this.callArgIndex = Math.min(callArgIndex, MAX_ARG_COUNT - 1);
|
||||
this.isVoid = isVoid;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionalExpressionKey deserializeKey(@NotNull DataInput dataStream) throws IOException {
|
||||
String methodName = IOUtil.readUTF(dataStream);
|
||||
int parameterCount = DataInputOutputUtil.readINT(dataStream);
|
||||
int methodArgsLength = DataInputOutputUtil.readINT(dataStream);
|
||||
int argIndex = DataInputOutputUtil.readINT(dataStream);
|
||||
ThreeState voidCompatible = ThreeState.values()[dataStream.readByte()];
|
||||
return new FunctionalExpressionKey(methodName, parameterCount, methodArgsLength, argIndex, voidCompatible);
|
||||
}
|
||||
|
||||
public boolean canRepresent(int samParamCount, boolean samVoid) {
|
||||
return (samParamCount == lambdaParameterCount || lambdaParameterCount == -1) &&
|
||||
(isVoid == ThreeState.UNSURE || samVoid == isVoid.toBoolean());
|
||||
}
|
||||
|
||||
public void serializeKey(@NotNull DataOutput dataStream) throws IOException {
|
||||
IOUtil.writeUTF(dataStream, methodName);
|
||||
DataInputOutputUtil.writeINT(dataStream, lambdaParameterCount);
|
||||
DataInputOutputUtil.writeINT(dataStream, methodArgsLength);
|
||||
DataInputOutputUtil.writeINT(dataStream, callArgIndex);
|
||||
dataStream.writeByte(isVoid.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof FunctionalExpressionKey)) return false;
|
||||
|
||||
FunctionalExpressionKey key = (FunctionalExpressionKey)o;
|
||||
|
||||
if (lambdaParameterCount != key.lambdaParameterCount) return false;
|
||||
if (methodArgsLength != key.methodArgsLength) return false;
|
||||
if (callArgIndex != key.callArgIndex) return false;
|
||||
if (!methodName.equals(key.methodName)) return false;
|
||||
if (isVoid != key.isVoid) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = methodName.hashCode();
|
||||
result = 31 * result + lambdaParameterCount;
|
||||
result = 31 * result + methodArgsLength;
|
||||
result = 31 * result + callArgIndex;
|
||||
result = 31 * result + isVoid.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper(this)
|
||||
.add("methodName", methodName)
|
||||
.add("lambdaParameterCount", lambdaParameterCount)
|
||||
.add("methodArgsLength", methodArgsLength)
|
||||
.add("callArgIndex", callArgIndex)
|
||||
.add("isVoid", isVoid)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -19,10 +19,19 @@ import com.intellij.psi.PsiFunctionalExpression;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FunctionalExpressionStub<T extends PsiFunctionalExpression> extends StubBase<T> {
|
||||
@NotNull private final FunctionalExpressionKey myIndexKey;
|
||||
|
||||
protected FunctionalExpressionStub(StubElement parent,
|
||||
IStubElementType elementType) {
|
||||
IStubElementType elementType, @NotNull FunctionalExpressionKey indexKey) {
|
||||
super(parent, elementType);
|
||||
myIndexKey = indexKey;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FunctionalExpressionKey getIndexKey() {
|
||||
return myIndexKey;
|
||||
}
|
||||
}
|
||||
+50
-17
@@ -18,9 +18,8 @@ package com.intellij.psi.impl.java.stubs;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiNameHelper;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.cache.ModifierFlags;
|
||||
import com.intellij.psi.impl.cache.RecordUtil;
|
||||
import com.intellij.psi.impl.cache.TypeInfo;
|
||||
import com.intellij.psi.impl.java.stubs.impl.PsiMethodStubImpl;
|
||||
@@ -38,11 +37,14 @@ import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.psi.stubs.StubOutputStream;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.BitUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.StringRef;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -154,28 +156,17 @@ public abstract class JavaMethodElementType extends JavaStubElementType<PsiMetho
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> methodTypeParams = null;
|
||||
Set<String> methodTypeParams = getVisibleTypeParameters(stub);
|
||||
for (StubElement stubElement : stub.getChildrenStubs()) {
|
||||
if (stubElement instanceof PsiTypeParameterListStub) {
|
||||
for (Object tStub : stubElement.getChildrenStubs()) {
|
||||
if (tStub instanceof PsiTypeParameterStub) {
|
||||
if (methodTypeParams == null) {
|
||||
methodTypeParams = new HashSet<String>();
|
||||
}
|
||||
methodTypeParams.add(((PsiTypeParameterStub)tStub).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (stubElement instanceof PsiParameterListStub) {
|
||||
if (stubElement instanceof PsiParameterListStub) {
|
||||
for (StubElement paramStub : ((PsiParameterListStub)stubElement).getChildrenStubs()) {
|
||||
if (paramStub instanceof PsiParameterStub) {
|
||||
TypeInfo type = ((PsiParameterStub)paramStub).getType(false);
|
||||
String typeName = PsiNameHelper.getShortClassName(type.text);
|
||||
if (TypeConversionUtil.isPrimitive(typeName) || TypeConversionUtil.isPrimitiveWrapper(typeName)) continue;
|
||||
sink.occurrence(JavaStubIndexKeys.METHOD_TYPES, typeName);
|
||||
if (methodTypeParams != null && methodTypeParams.contains(typeName)) {
|
||||
if (methodTypeParams.contains(typeName)) {
|
||||
sink.occurrence(JavaStubIndexKeys.METHOD_TYPES, TYPE_PARAMETER_PSEUDO_NAME);
|
||||
methodTypeParams = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,4 +174,46 @@ public abstract class JavaMethodElementType extends JavaStubElementType<PsiMetho
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<String> getVisibleTypeParameters(@NotNull StubElement<?> stub) {
|
||||
Set<String> result = null;
|
||||
while (stub != null) {
|
||||
Set<String> names = getOwnTypeParameterNames(stub);
|
||||
if (!names.isEmpty()) {
|
||||
if (result == null) result = ContainerUtil.newHashSet();
|
||||
result.addAll(names);
|
||||
}
|
||||
|
||||
if (isStatic(stub)) break;
|
||||
|
||||
stub = stub.getParentStub();
|
||||
}
|
||||
return result == null ? Collections.<String>emptySet() : result;
|
||||
}
|
||||
|
||||
private static boolean isStatic(@NotNull StubElement<?> stub) {
|
||||
if (stub instanceof PsiMemberStub) {
|
||||
StubElement<PsiModifierList> modList = stub.findChildStubByType(JavaStubElementTypes.MODIFIER_LIST);
|
||||
if (modList instanceof PsiModifierListStub) {
|
||||
return BitUtil.isSet(((PsiModifierListStub)modList).getModifiersMask(),
|
||||
ModifierFlags.NAME_TO_MODIFIER_FLAG_MAP.get(PsiModifier.STATIC));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Set<String> getOwnTypeParameterNames(StubElement<?> stubElement) {
|
||||
StubElement<PsiTypeParameterList> typeParamList = stubElement.findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST);
|
||||
if (typeParamList == null) return Collections.emptySet();
|
||||
|
||||
Set<String> methodTypeParams = null;
|
||||
for (Object tStub : typeParamList.getChildrenStubs()) {
|
||||
if (tStub instanceof PsiTypeParameterStub) {
|
||||
if (methodTypeParams == null) methodTypeParams = new HashSet<String>();
|
||||
methodTypeParams.add(((PsiTypeParameterStub)tStub).getName());
|
||||
}
|
||||
}
|
||||
return methodTypeParams == null ? Collections.<String>emptySet() : methodTypeParams;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-25
@@ -16,20 +16,15 @@
|
||||
package com.intellij.psi.impl.java.stubs;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiLambdaExpression;
|
||||
import com.intellij.psi.impl.source.tree.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLambdaExpressionImpl;
|
||||
import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
|
||||
import com.intellij.psi.stubs.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LambdaExpressionElementType extends JavaStubElementType<FunctionalExpressionStub<PsiLambdaExpression>,PsiLambdaExpression> {
|
||||
public class LambdaExpressionElementType extends FunctionalExpressionElementType<PsiLambdaExpression> {
|
||||
public LambdaExpressionElementType() {
|
||||
super("LAMBDA_EXPRESSION");
|
||||
}
|
||||
@@ -39,30 +34,11 @@ public class LambdaExpressionElementType extends JavaStubElementType<FunctionalE
|
||||
return new PsiLambdaExpressionImpl(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalExpressionStub<PsiLambdaExpression> createStub(LighterAST tree, LighterASTNode node, StubElement parentStub) {
|
||||
return new FunctionalExpressionStub<PsiLambdaExpression>(parentStub, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiLambdaExpression createPsi(@NotNull FunctionalExpressionStub<PsiLambdaExpression> stub) {
|
||||
return new PsiLambdaExpressionImpl(stub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(@NotNull FunctionalExpressionStub<PsiLambdaExpression> stub, @NotNull StubOutputStream dataStream) throws IOException {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FunctionalExpressionStub<PsiLambdaExpression> deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||
return new FunctionalExpressionStub<PsiLambdaExpression>(parentStub, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indexStub(@NotNull FunctionalExpressionStub<PsiLambdaExpression> stub, @NotNull IndexSink sink) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ASTNode createCompositeNode() {
|
||||
|
||||
+1
-26
@@ -16,21 +16,15 @@
|
||||
package com.intellij.psi.impl.java.stubs;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiMethodReferenceExpression;
|
||||
import com.intellij.psi.impl.source.tree.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl;
|
||||
import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
|
||||
import com.intellij.psi.stubs.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MethodReferenceElementType extends
|
||||
JavaStubElementType<FunctionalExpressionStub<PsiMethodReferenceExpression>, PsiMethodReferenceExpression> {
|
||||
public class MethodReferenceElementType extends FunctionalExpressionElementType<PsiMethodReferenceExpression> {
|
||||
public MethodReferenceElementType() {
|
||||
super("METHOD_REF_EXPRESSION");
|
||||
}
|
||||
@@ -40,30 +34,11 @@ public class MethodReferenceElementType extends
|
||||
return new PsiMethodReferenceExpressionImpl(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalExpressionStub<PsiMethodReferenceExpression> createStub(LighterAST tree, LighterASTNode node, StubElement parentStub) {
|
||||
return new FunctionalExpressionStub<PsiMethodReferenceExpression>(parentStub, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiMethodReferenceExpression createPsi(@NotNull FunctionalExpressionStub<PsiMethodReferenceExpression> stub) {
|
||||
return new PsiMethodReferenceExpressionImpl(stub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(@NotNull FunctionalExpressionStub<PsiMethodReferenceExpression> stub, @NotNull StubOutputStream dataStream) throws IOException {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FunctionalExpressionStub<PsiMethodReferenceExpression> deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||
return new FunctionalExpressionStub<PsiMethodReferenceExpression>(parentStub, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indexStub(@NotNull FunctionalExpressionStub<PsiMethodReferenceExpression> stub, @NotNull IndexSink sink) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ASTNode createCompositeNode() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi.impl.java.stubs.index;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey;
|
||||
import com.intellij.psi.stubs.StubIndexKey;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,7 @@ public class JavaStubIndexKeys {
|
||||
public static final StubIndexKey<String, PsiAnonymousClass> ANONYMOUS_BASEREF = StubIndexKey.createIndexKey("java.anonymous.baseref");
|
||||
public static final StubIndexKey<String, PsiMethod> METHOD_TYPES = StubIndexKey.createIndexKey("java.method.parameter.types");
|
||||
public static final StubIndexKey<String, PsiClass> CLASS_SHORT_NAMES = StubIndexKey.createIndexKey("java.class.shortname");
|
||||
public static final StubIndexKey<FunctionalExpressionKey, PsiFunctionalExpression> FUNCTIONAL_EXPRESSIONS = StubIndexKey.createIndexKey("java.functional.expressions");
|
||||
public static final StubIndexKey<Integer, PsiClass> CLASS_FQN = StubIndexKey.createIndexKey("java.class.fqn");
|
||||
public static final StubIndexKey<String, PsiJavaModule> MODULE_NAMES = StubIndexKey.createIndexKey("java.module.name");
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.io.IOException;
|
||||
* @author max
|
||||
*/
|
||||
public class JavaFileElementType extends ILightStubFileElementType<PsiJavaFileStub> {
|
||||
public static final int STUB_VERSION = 28;
|
||||
public static final int STUB_VERSION = 29;
|
||||
|
||||
public JavaFileElementType() {
|
||||
super("java.FILE", JavaLanguage.INSTANCE);
|
||||
|
||||
+7
@@ -4,6 +4,13 @@ class Foo {
|
||||
Foo(I i) {}
|
||||
}
|
||||
|
||||
enum SomeEnum {
|
||||
FOO(() -> 5);
|
||||
|
||||
SomeEnum(I i) {
|
||||
}
|
||||
}
|
||||
|
||||
interface <caret>I {
|
||||
int foo();
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
interface I {
|
||||
void hmmm();
|
||||
}
|
||||
|
||||
class Test<T extends I> {
|
||||
|
||||
void someUnlikelyName(T action) {
|
||||
}
|
||||
|
||||
{
|
||||
someUnlikelyName(() -> {});
|
||||
}
|
||||
}
|
||||
+8
-16
@@ -16,9 +16,7 @@
|
||||
package com.intellij.codeInsight.daemon.lambda;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.idea.Bombed;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.search.JavaFunctionalExpressionSearcher;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
@@ -27,7 +25,6 @@ import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -40,7 +37,6 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
doTestOneExpression();
|
||||
}
|
||||
|
||||
@Bombed(month = Calendar.AUGUST, day = 1, user = "ann peter")
|
||||
public void testFieldDeclaredInFileWithoutFunctionalInterfaces() throws Exception {
|
||||
myFixture.addClass("class B {" +
|
||||
" void f(A a) {" +
|
||||
@@ -50,7 +46,6 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
myFixture.addClass("public class A {" +
|
||||
" public I r;" +
|
||||
"}");
|
||||
addManyLambdas();
|
||||
|
||||
doTestOneExpression();
|
||||
}
|
||||
@@ -61,7 +56,6 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
" <T> void foo(T... r) {}\n" +
|
||||
" void bar(J i){foo(i, i, () -> {});}\n" +
|
||||
"}");
|
||||
addManyLambdas();
|
||||
|
||||
doTestOneExpression();
|
||||
}
|
||||
@@ -99,6 +93,13 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
assertEquals(1, references.size());
|
||||
}
|
||||
|
||||
public void testMethodWithClassTypeParameter() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
|
||||
PsiClass runnable = JavaPsiFacade.getInstance(getProject()).findClass("I", GlobalSearchScope.allScope(getProject()));
|
||||
assertSize(1, FunctionalExpressionSearch.search(runnable).findAll());
|
||||
}
|
||||
|
||||
public void testClassFromJdk() {
|
||||
doTestIndexSearch("(e) -> true");
|
||||
}
|
||||
@@ -110,8 +111,6 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
public void doTestIndexSearch(String expected) {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
|
||||
addManyLambdas();
|
||||
|
||||
PsiClass predicate = JavaPsiFacade.getInstance(getProject()).findClass(Predicate.class.getName(), GlobalSearchScope.allScope(getProject()));
|
||||
assert predicate != null;
|
||||
final PsiFunctionalExpression next = assertOneElement(FunctionalExpressionSearch.search(predicate).findAll());
|
||||
@@ -129,15 +128,8 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
|
||||
" new Foo(() -> 3);\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
addManyLambdas();
|
||||
|
||||
assertSize(4, FunctionalExpressionSearch.search(findClassAtCaret()).findAll());
|
||||
}
|
||||
|
||||
private void addManyLambdas() {
|
||||
for (int i = 0; i < JavaFunctionalExpressionSearcher.SMART_SEARCH_THRESHOLD + 5; i++) {
|
||||
myFixture.addFileToProject("a" + i + ".java", "class Goo {{ Runnable r = () -> {} }}");
|
||||
}
|
||||
assertSize(5, FunctionalExpressionSearch.search(findClassAtCaret()).findAll());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user