From ec0f90dbbf3afcb0c9880db50b0d96586a56635a Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 25 Jul 2016 10:53:19 +0200 Subject: [PATCH] distinguish boolean lambdas for finer search granularity; make FunctionalExpressionKey#hashCode independent of enum hashCode --- .../JavaFunctionalExpressionSearcher.java | 83 +++++++++++-------- .../FunctionalExpressionElementType.java | 50 ++++++++--- .../java/stubs/FunctionalExpressionKey.java | 46 +++++++--- .../java/stubs/JavaMethodElementType.java | 4 +- .../psi/impl/source/JavaFileElementType.java | 2 +- 5 files changed, 126 insertions(+), 59 deletions(-) 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 432b8287b690..7209289bdfe3 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 @@ -45,7 +45,6 @@ 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.ThreeState; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.containers.MultiMap; @@ -86,7 +85,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase queries = - collectQueryKeys(useScope, funInterface, expectedFunExprParamsCount, isVoid, highLevelModules); + collectQueryKeys(useScope, funInterface, expectedFunExprParamsCount, samType, highLevelModules); for (PsiFunctionalExpression expression : getCandidates(useScope, funInterface.getProject(), queries)) { if (!processExpression(consumer, funInterface, expression)) { @@ -154,28 +153,30 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase collectQueryKeys(GlobalSearchScope useScope, PsiClass aClass, int samParamCount, - boolean samVoid, + PsiType samType, Set candidateModules) { - //collect all methods with parameter of functional interface or free type parameter type - Collection methodCandidates = getCandidateMethodsWithSuitableParams(aClass, useScope, candidateModules, samParamCount, samVoid); - MultiMap queries = MultiMap.createSet(); - for (FunctionalExpressionKey key : generateKeys(samParamCount, samVoid, FunctionalExpressionKey.Location.UNKNOWN)) { - queries.putValue(key, useScope); // check all fun-exprs that aren't inside calls or variables - } - for (FunctionalExpressionKey key : generateKeys(samParamCount, samVoid, - new FunctionalExpressionKey.VariableLocation(assertNotNull(aClass.getName())))) { - queries.putValue(key, useScope); - } + ReadAction.run(() -> { + if (!samType.isValid()) return; - //find all usages of method candidates in files with functional expressions - for (final PsiMethod psiMethod : methodCandidates) { + for (FunctionalExpressionKey key : generateKeys(samParamCount, samType, FunctionalExpressionKey.Location.UNKNOWN)) { + queries.putValue(key, useScope); // check all fun-exprs that aren't inside calls or variables + } + + for (FunctionalExpressionKey key : generateKeys(samParamCount, samType, + new FunctionalExpressionKey.VariableLocation(assertNotNull(aClass.getName())))) { + queries.putValue(key, useScope); + } + }); + + //collect all methods with parameter of functional interface or free type parameter type + for (final PsiMethod psiMethod : getCandidateMethodsWithSuitableParams(aClass, useScope, candidateModules, samParamCount, samType)) { ReadAction.run(() -> { - if (!psiMethod.isValid()) return; + if (!psiMethod.isValid() || !samType.isValid()) return; final GlobalSearchScope methodUseScope = convertToGlobalScope(psiMethod.getProject(), psiMethod.getUseScope()); - for (FunctionalExpressionKey key : getQueryKeys(aClass, samParamCount, samVoid, psiMethod)) { + for (FunctionalExpressionKey key : getQueryKeys(aClass, samParamCount, samType, psiMethod)) { queries.putValue(key, methodUseScope); } }); @@ -186,7 +187,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase getQueryKeys(PsiClass samClass, int samParamCount, - boolean samVoid, + PsiType samType, PsiMethod calledMethod) { Set keys = new HashSet<>(); @@ -197,7 +198,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase generateKeys(int samMethodParamsCount, - boolean samMethodVoid, + PsiType samType, FunctionalExpressionKey.Location location) { List result = new ArrayList<>(); for (int lambdaParamCount : new int[]{FunctionalExpressionKey.UNKNOWN_PARAM_COUNT, samMethodParamsCount}) { - result.add(new FunctionalExpressionKey(lambdaParamCount, ThreeState.UNSURE, location)); - result.add(new FunctionalExpressionKey(lambdaParamCount, ThreeState.fromBoolean(samMethodVoid), location)); + result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.UNKNOWN, location)); + if (PsiType.VOID.equals(samType)) { + result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.VOID, location)); + } else { + if (FunctionalExpressionKey.isBooleanCompatible(samType)) { + result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.BOOLEAN, location)); + } + result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.NON_VOID, location)); + } } return result; } @@ -258,7 +266,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase candidateModules, int expectedFunExprParamsCount, - boolean isVoid) { + PsiType samType) { return ApplicationManager.getApplication().runReadAction(new Computable>() { @Override public Collection compute() { @@ -267,14 +275,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase usedMethodNames = new HashSet<>(); - StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key -> { - ProgressManager.checkCanceled(); - if (key.canRepresent(expectedFunExprParamsCount, isVoid) && key.location instanceof CallLocation) { - usedMethodNames.add(((CallLocation)key.location).methodName); - } - return true; - }, useScope, null); + Set usedMethodNames = collectMethodNamesCalledWithFunExpressions(samType, expectedFunExprParamsCount, useScope); Set methods = ContainerUtil.newLinkedHashSet(); Processor methodProcessor = method -> { @@ -295,6 +296,22 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase collectMethodNamesCalledWithFunExpressions(PsiType samType, int samParamCount, GlobalSearchScope useScope) { + boolean booleanCompatible = FunctionalExpressionKey.isBooleanCompatible(samType); + boolean isVoid = PsiType.VOID.equals(samType); + + Set usedMethodNames = new HashSet<>(); + StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key -> { + ProgressManager.checkCanceled(); + if (key.canRepresent(samParamCount, booleanCompatible, isVoid) && key.location instanceof CallLocation) { + usedMethodNames.add(((CallLocation)key.location).methodName); + } + return true; + }, useScope, null); + return usedMethodNames; + } + @Nullable private static GlobalSearchScope combineResolveScopes(Set candidateModules, PsiClass samClass) { List scopes = candidateModules.stream() 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 index 25d12044ef6c..ed77e343b2b6 100644 --- 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 @@ -21,6 +21,7 @@ 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.FunctionalExpressionKey.CoarseType; import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys; import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.tree.ElementType; @@ -32,12 +33,12 @@ import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; -import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import static com.intellij.psi.impl.source.tree.JavaElementType.*; @@ -66,7 +67,7 @@ public abstract class FunctionalExpressionElementType createStub(LighterAST tree, LighterASTNode funExpr, StubElement parentStub) { return new FunctionalExpressionStub(parentStub, this, new FunctionalExpressionKey(getFunExprParameterCount(tree, funExpr), - isVoid(tree, funExpr), + calcType(tree, funExpr), calcLocation(tree, funExpr))); } @@ -110,32 +111,57 @@ public abstract class FunctionalExpressionElementType isVoid = Ref.create(ThreeState.UNSURE); + final Ref returnsSomething = Ref.create(null); + final AtomicBoolean isBoolean = new AtomicBoolean(); + final AtomicBoolean hasStatements = new AtomicBoolean(); 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) { + if (type == LAMBDA_EXPRESSION || ElementType.MEMBER_BIT_SET.contains(type) || !returnsSomething.isNull()) { return; } if (type == RETURN_STATEMENT) { - boolean noExpr = LightTreeUtil.firstChildOfType(tree, element, ElementType.EXPRESSION_BIT_SET) == null; - isVoid.set(ThreeState.fromBoolean(noExpr)); + LighterASTNode expr = LightTreeUtil.firstChildOfType(tree, element, ElementType.EXPRESSION_BIT_SET); + returnsSomething.set(expr != null); + if (isBooleanLiteral(tree, expr)) { + isBoolean.set(true); + } return; } + if (ElementType.JAVA_STATEMENT_BIT_SET.contains(type) && type != THROW_STATEMENT) { + hasStatements.set(true); + } + super.visitNode(element); } }.visitNode(block); - return isVoid.get(); + if (isBoolean.get()) { + return CoarseType.BOOLEAN; + } + + if (returnsSomething.isNull()) { + return hasStatements.get() ? CoarseType.VOID : CoarseType.UNKNOWN; + } + + return returnsSomething.get() ? CoarseType.NON_VOID : CoarseType.VOID; + } + + private static boolean isBooleanLiteral(LighterAST tree, LighterASTNode expr) { + if (expr != null && expr.getTokenType() == LITERAL_EXPRESSION) { + IElementType type = tree.getChildren(expr).get(0).getTokenType(); + return type == JavaTokenType.TRUE_KEYWORD || type == JavaTokenType.FALSE_KEYWORD; + } + return false; } private static int getFunExprParameterCount(LighterAST tree, LighterASTNode funExpr) { @@ -199,7 +225,9 @@ public abstract class FunctionalExpressionElementType= 0 && samParamCount != lambdaParameterCount) return false; + + switch (lambdaType) { + case VOID: return isVoid; + case NON_VOID: return !isVoid; + case BOOLEAN: return booleanCompatible; + default: return true; + } + } + + public static boolean isBooleanCompatible(PsiType samType) { + return PsiType.BOOLEAN.equals(samType) || TypeConversionUtil.isAssignableFromPrimitiveWrapper(TypeConversionUtil.erasure(samType)); } @Override @@ -87,7 +98,7 @@ public class FunctionalExpressionKey { FunctionalExpressionKey key = (FunctionalExpressionKey)o; if (lambdaParameterCount != key.lambdaParameterCount) return false; - if (isVoid != key.isVoid) return false; + if (lambdaType != key.lambdaType) return false; if (!location.equals(key.location)) return false; return true; @@ -96,7 +107,7 @@ public class FunctionalExpressionKey { @Override public int hashCode() { int result = lambdaParameterCount; - result = 31 * result + isVoid.hashCode(); + result = 31 * result + lambdaType.ordinal(); result = 31 * result + location.hashCode(); return result; } @@ -105,13 +116,18 @@ public class FunctionalExpressionKey { public String toString() { return Objects.toStringHelper(this) .add("lambdaParameterCount", lambdaParameterCount) - .add("isVoid", isVoid) + .add("type", lambdaType) .add("location", location) .toString(); } public interface Location { Location UNKNOWN = new Location() { + @Override + public String toString() { + return "UNKNOWN"; + } + @Override public int hashCode() { return 0; @@ -217,4 +233,8 @@ public class FunctionalExpressionKey { IOUtil.writeUTF(dataStream, varType); } } + + public enum CoarseType { + VOID, UNKNOWN, BOOLEAN, NON_VOID + } } 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 d1c88551b0b3..659d7fd0afff 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 @@ -165,7 +165,9 @@ public abstract class JavaMethodElementType extends JavaStubElementType { - public static final int STUB_VERSION = 30; + public static final int STUB_VERSION = 31; public JavaFileElementType() { super("java.FILE", JavaLanguage.INSTANCE);