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 236d6c979419..ffe84393350b 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 @@ -88,6 +88,100 @@ public class JavaFunctionalExpressionSearcher implements QueryExecutor consumer, + final Set highLevelModules) { + final Project project = PsiUtilCore.getProjectInReadAction(aClass); + final ModulesScope modulesScope = new ModulesScope(highLevelModules, project); + final GlobalSearchScope useScope = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public GlobalSearchScope compute() { + return modulesScope.intersectWith(convertToGlobalScope(project, searchScope.intersectWith(aClass.getUseScope()))); + } + }); + + //collect all files with '::' and '->' in useScope + final GlobalSearchScope filesScope = getFilesWithFunctionalExpressionsScope(project, useScope); + + //collect all methods with parameter of functional interface or free type parameter type + final Collection methodCandidates = getCandidateMethodsWithSuitableParams(aClass, project, useScope); + + final LinkedHashSet filesToProcess = new LinkedHashSet(); + final MethodSignature functionalInterfaceMethod = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public MethodSignature compute() { + return LambdaUtil.getFunction(aClass); + } + }); + LOG.assertTrue(functionalInterfaceMethod != null); + final int expectedFunExprParamsCount = functionalInterfaceMethod.getParameterTypes().length; + final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance(); + + //find all usages of method candidates in files with functional expressions + for (final PsiMethod psiMethod : methodCandidates) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + if (!psiMethod.isValid()) return; + final int parametersCount = psiMethod.getParameterList().getParametersCount(); + final boolean varArgs = psiMethod.isVarArgs(); + final PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + final GlobalSearchScope methodUseScope = modulesScope.intersectWith(convertToGlobalScope(project, psiMethod.getUseScope())); + fileBasedIndex.processValues(JavaFunctionalExpressionIndex.JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID, psiMethod.getName(), null, + //functional expressions checker: number and type of parameters at call site should correspond to + //candidate method currently check + new SuitableFilesProcessor(filesToProcess, + expectedFunExprParamsCount, + parametersCount, + varArgs, + parameters), + useScope.intersectWith(methodUseScope)); + } + }); + } + + //search for functional expressions in non-call contexts + collectFilesWithTypeOccurrencesAndFieldAssignments(aClass, filesScope, filesToProcess); + + LOG.info("#usage files: " + filesToProcess.size()); + return ContainerUtil.process(filesToProcess, new ReadActionProcessor() { + @Override + public boolean processInReadAction(VirtualFile file) { + //resolve functional expressions to ensure that functional expression type is appropriate + return processFileWithFunctionalInterfaces(aClass, expectedFunExprParamsCount, consumer, file); + } + }); + } + + private static Collection getCandidateMethodsWithSuitableParams(final PsiClass aClass, + final Project project, + final GlobalSearchScope useScope) { + return ApplicationManager.getApplication().runReadAction(new Computable>() { + @Override + public Collection compute() { + final String functionalInterfaceName = aClass.getName(); + JavaMethodParameterTypesIndex parameterTypesIndex = JavaMethodParameterTypesIndex.getInstance(); + + LinkedHashSet methods = new LinkedHashSet(parameterTypesIndex.get(functionalInterfaceName, project, useScope)); + + methods.addAll(parameterTypesIndex.get(JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME, project, + GlobalSearchScope.allScope(project))); + return methods; + } + }); + } + + @NotNull + private static GlobalSearchScope getFilesWithFunctionalExpressionsScope(Project project, GlobalSearchScope useScope) { + final PsiSearchHelperImpl helper = (PsiSearchHelperImpl)PsiSearchHelper.SERVICE.getInstance(project); + final HashSet files = new HashSet(); + final CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(files); + helper.processFilesWithText(useScope, UsageSearchContext.IN_CODE, true, "::", processor); + helper.processFilesWithText(useScope, UsageSearchContext.IN_CODE, true, "->", processor); + + return GlobalSearchScope.filesScope(project, files); + } + @NotNull private static GlobalSearchScope convertToGlobalScope(Project project, SearchScope useScope) { final GlobalSearchScope scope; @@ -109,102 +203,7 @@ public class JavaFunctionalExpressionSearcher implements QueryExecutor consumer, - final Set highLevelModules) { - final Project project = PsiUtilCore.getProjectInReadAction(aClass); - final ModulesScope modulesScope = new ModulesScope(highLevelModules, project); - final GlobalSearchScope useScope = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public GlobalSearchScope compute() { - return modulesScope.intersectWith(convertToGlobalScope(project, searchScope.intersectWith(aClass.getUseScope()))); - } - }); - - final PsiSearchHelperImpl helper = (PsiSearchHelperImpl)PsiSearchHelper.SERVICE.getInstance(project); - final HashSet files = new HashSet(); - final CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(files); - helper.processFilesWithText(useScope, UsageSearchContext.IN_CODE, true, "::", processor); - helper.processFilesWithText(useScope, UsageSearchContext.IN_CODE, true, "->", processor); - - final GlobalSearchScope filesScope = GlobalSearchScope.filesScope(project, files); - - final Collection lambdaCandidates = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Collection compute() { - final String functionalInterfaceName = aClass.getName(); - JavaMethodParameterTypesIndex parameterTypesIndex = JavaMethodParameterTypesIndex.getInstance(); - - LinkedHashSet methods = new LinkedHashSet(parameterTypesIndex.get(functionalInterfaceName, project, useScope)); - - methods.addAll(parameterTypesIndex.get(JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME, project, - GlobalSearchScope.allScope(project))); - return methods; - } - }); - - final LinkedHashSet usageFiles = new LinkedHashSet(); - final MethodSignature functionalInterfaceMethod = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public MethodSignature compute() { - return LambdaUtil.getFunction(aClass); - } - }); - LOG.assertTrue(functionalInterfaceMethod != null); - final int expectedFunExprParamsCount = functionalInterfaceMethod.getParameterTypes().length; - final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance(); - for (final PsiMethod psiMethod : lambdaCandidates) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (!psiMethod.isValid()) return; - final int parametersCount = psiMethod.getParameterList().getParametersCount(); - final boolean varArgs = psiMethod.isVarArgs(); - final PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); - final GlobalSearchScope methodUseScope = modulesScope.intersectWith(convertToGlobalScope(project, psiMethod.getUseScope())); - fileBasedIndex.processValues(JavaFunctionalExpressionIndex.JAVA_FUNCTIONAL_EXPRESSION_INDEX_ID, psiMethod.getName(), null, - new FileBasedIndex.ValueProcessor>() { - @Override - public boolean process(VirtualFile file, Collection holders) { - for (JavaFunctionalExpressionIndex.IndexHolder holder : holders) { - if (holder.getLambdaParamsNumber() == expectedFunExprParamsCount && - (varArgs ? holder.getMethodArgsLength() >= parametersCount - 1 : holder.getMethodArgsLength() == parametersCount) && - canBeFunctional(holder) - ) { - usageFiles.add(file); - break; - } - } - return true; - } - - private boolean canBeFunctional(JavaFunctionalExpressionIndex.IndexHolder holder) { - final int paramIdx = holder.getFunctionExpressionIndex(); - PsiType paramType = parameters[paramIdx >= parametersCount ? parametersCount - 1 : paramIdx].getType(); - if (paramType instanceof PsiEllipsisType) { - paramType = ((PsiEllipsisType)paramType).getComponentType(); - } - final PsiClass functionalCandidate = PsiUtil.resolveClassInClassTypeOnly(paramType); - return functionalCandidate instanceof PsiTypeParameter || - LambdaUtil.isFunctionalClass(functionalCandidate); - } - }, useScope.intersectWith(methodUseScope)); - } - }); - } - - collectFilesWithAssignments(aClass, filesScope, usageFiles); - - LOG.info("#usage files: " + usageFiles.size()); - return ContainerUtil.process(usageFiles, new ReadActionProcessor() { - @Override - public boolean processInReadAction(VirtualFile file) { - return processFileWithFunctionalInterfaces(aClass, expectedFunExprParamsCount, consumer, file); - } - }); - } - + /** * Collect files where: * aClass is used, e.g. in type declaration or method return type; @@ -221,9 +220,9 @@ public class JavaFunctionalExpressionSearcher implements QueryExecutor */ - private static void collectFilesWithAssignments(PsiClass aClass, - GlobalSearchScope filesScope, - final LinkedHashSet usageFiles) { + private static void collectFilesWithTypeOccurrencesAndFieldAssignments(PsiClass aClass, + GlobalSearchScope filesScope, + final LinkedHashSet usageFiles) { final Set fields = new LinkedHashSet(); for (final PsiReference reference : ReferencesSearch.search(aClass, filesScope)) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -304,4 +303,55 @@ public class JavaFunctionalExpressionSearcher implements QueryExecutor> { + private final LinkedHashSet myFilesToProcess; + private final int myExpectedFunExprParamsCount; + private final int myParametersCount; + private final boolean myVarArgs; + private final PsiParameter[] myParameters; + + public SuitableFilesProcessor(LinkedHashSet filesToProcess, + int expectedFunExprParamsCount, + int parametersCount, + boolean varArgs, + PsiParameter[] parameters) { + myFilesToProcess = filesToProcess; + myExpectedFunExprParamsCount = expectedFunExprParamsCount; + myParametersCount = parametersCount; + myVarArgs = varArgs; + myParameters = parameters; + } + + @Override + public boolean process(VirtualFile file, Collection holders) { + for (JavaFunctionalExpressionIndex.IndexHolder holder : holders) { + if (holder.getLambdaParamsNumber() == myExpectedFunExprParamsCount) { + final boolean suitableParamNumbers; + if (myVarArgs) { + suitableParamNumbers = holder.getMethodArgsLength() >= myParametersCount - 1; + } + else { + suitableParamNumbers = holder.getMethodArgsLength() == myParametersCount; + } + if (suitableParamNumbers && canBeFunctional(holder)) { + myFilesToProcess.add(file); + 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); + } + } }