rewrite functional expression search to use stub index

This commit is contained in:
peter
2016-07-22 17:41:21 +02:00
parent 05c9e5acd0
commit 2a93e063bd
16 changed files with 649 additions and 540 deletions
@@ -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;
}
}
@@ -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() {
@@ -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;
}
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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() {
@@ -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);
@@ -4,6 +4,13 @@ class Foo {
Foo(I i) {}
}
enum SomeEnum {
FOO(() -> 5);
SomeEnum(I i) {
}
}
interface <caret>I {
int foo();
}
@@ -0,0 +1,13 @@
interface I {
void hmmm();
}
class Test<T extends I> {
void someUnlikelyName(T action) {
}
{
someUnlikelyName(() -> {});
}
}
@@ -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