diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaFunctionalExpressionIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaFunctionalExpressionIndex.java new file mode 100644 index 000000000000..5f576c275227 --- /dev/null +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaFunctionalExpressionIndex.java @@ -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 { + private static final KeyDescriptor KEY_DESCRIPTOR = new KeyDescriptor() { + @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 getKeyDescriptor() { + return KEY_DESCRIPTOR; + } + + @Override + public int getVersion() { + return 1; + } + + @NotNull + @Override + public StubIndexKey getKey() { + return JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS; + } + +} \ No newline at end of file diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaMethodParameterTypesIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaMethodParameterTypesIndex.java index eea3d1c1f46e..e310232339c0 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaMethodParameterTypesIndex.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaMethodParameterTypesIndex.java @@ -37,6 +37,11 @@ public class JavaMethodParameterTypesIndex extends StringStubIndexExtension getKey() { diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionIndex.java deleted file mode 100644 index d1686e4cd428..000000000000 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionIndex.java +++ /dev/null @@ -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> implements PsiDependentIndex { - static final ID> 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> getName() { - return JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID; - } - - @NotNull - @Override - public DataIndexer, 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> 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 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 getKeyDescriptor() { - return EnumeratorStringDescriptor.INSTANCE; - } - - @NotNull - @Override - public DataExternalizer> getValueExternalizer() { - return new DataExternalizer>() { - @Override - public void save(@NotNull DataOutput out, Collection 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 read(@NotNull DataInput in) throws IOException { - int l = DataInputOutputUtil.readINT(in); - final Collection 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; - } - } -} diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java index b93176ad33a9..56d33c98c236 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java @@ -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 { private static final Logger LOG = Logger.getInstance("#" + JavaFunctionalExpressionSearcher.class.getName()); @@ -86,46 +92,136 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase' in useScope Set 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 queries = + collectQueryKeys(useScope, aClass, project, expectedFunExprParamsCount, isVoid, candidateFiles); - //collect all methods with parameter of functional interface or free type parameter type - final Collection methodCandidates = getCandidateMethodsWithSuitableParams(aClass, project, useScope, candidateFiles, candidateScope); - - final Set 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> 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> 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 getCandidates(GlobalSearchScope useScope, + Project project, + MultiMap queries) { + MultiMap exprs = MultiMap.createLinked(); + for (Map.Entry> 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 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 collectQueryKeys(GlobalSearchScope useScope, + PsiClass aClass, + Project project, + int samParamCount, + boolean samVoid, + Set candidateFiles) { + //collect all methods with parameter of functional interface or free type parameter type + Collection methodCandidates = getCandidateMethodsWithSuitableParams(aClass, project, useScope, candidateFiles, samParamCount, samVoid); + + MultiMap 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 getQueryKeys(PsiClass samClass, + int samParamCount, + boolean samVoid, + PsiMethod calledMethod) { + Set 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 generateKeys(int samMethodParamsCount, + boolean samMethodVoid, + String methodName, int argCount, int argIndex) { + List 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 consumer, - Set filesToProcess, final int expectedFunExprParamsCount, boolean isVoid) { - LOG.info("#usage files: " + filesToProcess.size()); - ContainerUtil.process(filesToProcess, new ReadActionProcessor() { - @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 getCandidateMethodsWithSuitableParams(final PsiClass aClass, final Project project, final GlobalSearchScope useScope, final Set candidateFiles, - final GlobalSearchScope candidateScope) { + int expectedFunExprParamsCount, + boolean isVoid) { return ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Collection compute() { - if (!aClass.isValid()) return Collections.emptyList(); + @Override + public Collection compute() { + if (!aClass.isValid()) return Collections.emptyList(); - GlobalSearchScope visibleFromCandidates = combineResolveScopes(project, candidateFiles); + GlobalSearchScope visibleFromCandidates = combineResolveScopes(project, candidateFiles); - Set usedMethodNames = new HashSet<>(); - FileBasedIndex.getInstance().processAllKeys(JavaFunctionalExpressionIndex.JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID, - Processors.cancelableCollectProcessor(usedMethodNames), candidateScope, null); + Set 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 methods = ContainerUtil.newLinkedHashSet(); - Processor methodProcessor = method -> { - if (usedMethodNames.contains(method.getName())) { - methods.add(method); - } - return true; - }; + Set methods = ContainerUtil.newLinkedHashSet(); + Processor methodProcessor = method -> { + if (usedMethodNames.contains(method.getName())) { + methods.add(method); + } + return true; + }; - StubIndexKey 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 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 - 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 = () -> {}; - } - } - - */ - private static void collectFilesWithTypeOccurrencesAndFieldAssignments(@NotNull PsiClass aClass, - @NotNull GlobalSearchScope filesScope, - @NotNull Set usageFiles) { - final Set 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() { - @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 consumer, - VirtualFile file) { - final PsiFile psiFile = aClass.getManager().findFile(file); - if (psiFile != null) { - final Ref 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> { - private final Map> myHolders; - private final int myExpectedFunExprParamsCount; - private final int myParametersCount; - private final boolean myVarArgs; - private final PsiParameter[] myParameters; - - SuitableFilesProcessor(Map> 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 holders) { - Set 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); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionElementType.java new file mode 100644 index 000000000000..7c3ae46975fd --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionElementType.java @@ -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 extends JavaStubElementType,T> { + public FunctionalExpressionElementType(String debugName) { + super(debugName); + } + + @Override + public void serialize(@NotNull FunctionalExpressionStub stub, @NotNull StubOutputStream dataStream) throws IOException { + stub.getIndexKey().serializeKey(dataStream); + } + + @NotNull + @Override + public FunctionalExpressionStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { + return new FunctionalExpressionStub(parentStub, this, FunctionalExpressionKey.deserializeKey(dataStream)); + } + + @Override + public void indexStub(@NotNull FunctionalExpressionStub stub, @NotNull IndexSink sink) { + sink.occurrence(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, stub.getIndexKey()); + } + + @Override + public FunctionalExpressionStub createStub(LighterAST tree, LighterASTNode funExpr, StubElement parentStub) { + LighterASTNode call = getContainingCall(tree, funExpr); + List 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(parentStub, this, key); + } + + private static int getArgIndex(List 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 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 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; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionKey.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionKey.java new file mode 100644 index 000000000000..252692deb987 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionKey.java @@ -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(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionStub.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionStub.java index b926e4631d3f..99a523272a6b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionStub.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/FunctionalExpressionStub.java @@ -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 extends StubBase { + @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; } } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaMethodElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaMethodElementType.java index 68f20356704a..d1c88551b0b3 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaMethodElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaMethodElementType.java @@ -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 methodTypeParams = null; + Set 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(); - } - 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 getVisibleTypeParameters(@NotNull StubElement stub) { + Set result = null; + while (stub != null) { + Set 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.emptySet() : result; + } + + private static boolean isStatic(@NotNull StubElement stub) { + if (stub instanceof PsiMemberStub) { + StubElement 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 getOwnTypeParameterNames(StubElement stubElement) { + StubElement typeParamList = stubElement.findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST); + if (typeParamList == null) return Collections.emptySet(); + + Set methodTypeParams = null; + for (Object tStub : typeParamList.getChildrenStubs()) { + if (tStub instanceof PsiTypeParameterStub) { + if (methodTypeParams == null) methodTypeParams = new HashSet(); + methodTypeParams.add(((PsiTypeParameterStub)tStub).getName()); + } + } + return methodTypeParams == null ? Collections.emptySet() : methodTypeParams; + } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/LambdaExpressionElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/LambdaExpressionElementType.java index 8cab424d6e5d..0b189dea3f80 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/LambdaExpressionElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/LambdaExpressionElementType.java @@ -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,PsiLambdaExpression> { +public class LambdaExpressionElementType extends FunctionalExpressionElementType { public LambdaExpressionElementType() { super("LAMBDA_EXPRESSION"); } @@ -39,30 +34,11 @@ public class LambdaExpressionElementType extends JavaStubElementType createStub(LighterAST tree, LighterASTNode node, StubElement parentStub) { - return new FunctionalExpressionStub(parentStub, this); - } - @Override public PsiLambdaExpression createPsi(@NotNull FunctionalExpressionStub stub) { return new PsiLambdaExpressionImpl(stub); } - @Override - public void serialize(@NotNull FunctionalExpressionStub stub, @NotNull StubOutputStream dataStream) throws IOException { - } - - @NotNull - @Override - public FunctionalExpressionStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { - return new FunctionalExpressionStub(parentStub, this); - } - - @Override - public void indexStub(@NotNull FunctionalExpressionStub stub, @NotNull IndexSink sink) { - } - @NotNull @Override public ASTNode createCompositeNode() { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/MethodReferenceElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/MethodReferenceElementType.java index 41cb46f0236c..f05c65c56f58 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/MethodReferenceElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/MethodReferenceElementType.java @@ -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, PsiMethodReferenceExpression> { +public class MethodReferenceElementType extends FunctionalExpressionElementType { public MethodReferenceElementType() { super("METHOD_REF_EXPRESSION"); } @@ -40,30 +34,11 @@ public class MethodReferenceElementType extends return new PsiMethodReferenceExpressionImpl(node); } - @Override - public FunctionalExpressionStub createStub(LighterAST tree, LighterASTNode node, StubElement parentStub) { - return new FunctionalExpressionStub(parentStub, this); - } - @Override public PsiMethodReferenceExpression createPsi(@NotNull FunctionalExpressionStub stub) { return new PsiMethodReferenceExpressionImpl(stub); } - @Override - public void serialize(@NotNull FunctionalExpressionStub stub, @NotNull StubOutputStream dataStream) throws IOException { - } - - @NotNull - @Override - public FunctionalExpressionStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { - return new FunctionalExpressionStub(parentStub, this); - } - - @Override - public void indexStub(@NotNull FunctionalExpressionStub stub, @NotNull IndexSink sink) { - } - @NotNull @Override public ASTNode createCompositeNode() { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/index/JavaStubIndexKeys.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/index/JavaStubIndexKeys.java index 5d9b78e09ed3..46624449cb5c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/index/JavaStubIndexKeys.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/index/JavaStubIndexKeys.java @@ -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 ANONYMOUS_BASEREF = StubIndexKey.createIndexKey("java.anonymous.baseref"); public static final StubIndexKey METHOD_TYPES = StubIndexKey.createIndexKey("java.method.parameter.types"); public static final StubIndexKey CLASS_SHORT_NAMES = StubIndexKey.createIndexKey("java.class.shortname"); + public static final StubIndexKey FUNCTIONAL_EXPRESSIONS = StubIndexKey.createIndexKey("java.functional.expressions"); public static final StubIndexKey CLASS_FQN = StubIndexKey.createIndexKey("java.class.fqn"); public static final StubIndexKey MODULE_NAMES = StubIndexKey.createIndexKey("java.module.name"); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java index d6074403ef3e..52ae7f0765a9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java @@ -38,7 +38,7 @@ import java.io.IOException; * @author max */ public class JavaFileElementType extends ILightStubFileElementType { - public static final int STUB_VERSION = 28; + public static final int STUB_VERSION = 29; public JavaFileElementType() { super("java.FILE", JavaLanguage.INSTANCE); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/ConstructorReferences.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/ConstructorReferences.java index e51e0df8af9b..195896b52261 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/ConstructorReferences.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/ConstructorReferences.java @@ -4,6 +4,13 @@ class Foo { Foo(I i) {} } +enum SomeEnum { + FOO(() -> 5); + + SomeEnum(I i) { + } +} + interface I { int foo(); } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/MethodWithClassTypeParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/MethodWithClassTypeParameter.java new file mode 100644 index 000000000000..08a204aa8e5c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/findUsages/MethodWithClassTypeParameter.java @@ -0,0 +1,13 @@ +interface I { + void hmmm(); +} + +class Test { + + void someUnlikelyName(T action) { + } + + { + someUnlikelyName(() -> {}); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FindFunctionalInterfaceTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FindFunctionalInterfaceTest.java index 7ab0bf361c20..f0447ffb6b0c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FindFunctionalInterfaceTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FindFunctionalInterfaceTest.java @@ -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 " 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 diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index b84caae7c5ed..3e8ba3cdd510 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -369,7 +369,6 @@ - @@ -1542,6 +1541,7 @@ +