mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda search: detect more boolean lambdas, use explicit types in methods and casts, introduce a parameter object in the search
This commit is contained in:
+78
-91
@@ -33,6 +33,7 @@ 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.FunctionalExpressionKey.CallLocation;
|
||||
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey.Location;
|
||||
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;
|
||||
@@ -64,7 +65,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
* The least number of candidate files with functional expressions that directly scanning them becomes expensive
|
||||
* and more advanced ways of searching become necessary: e.g. first searching for methods where the functional interface class is used
|
||||
* and then for their usages,
|
||||
*/
|
||||
*/
|
||||
public static final int SMART_SEARCH_THRESHOLD = 5;
|
||||
|
||||
@Override
|
||||
@@ -88,25 +89,22 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
|
||||
for (PsiClass funInterface : funInterfaces) {
|
||||
final GlobalSearchScope useScope;
|
||||
final int samParamCount;
|
||||
final PsiType samType;
|
||||
try (AccessToken ignored = ReadAction.start()) {
|
||||
if (!funInterface.isValid()) continue;
|
||||
|
||||
useScope = convertToGlobalScope(funInterface.getProject(), funInterface.getUseScope().intersectWith(queryParameters.getEffectiveSearchScope()));
|
||||
SamDescriptor descriptor = ReadAction.compute(() -> {
|
||||
if (!funInterface.isValid()) return null;
|
||||
|
||||
final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(funInterface);
|
||||
LOG.assertTrue(functionalInterfaceMethod != null);
|
||||
samParamCount = functionalInterfaceMethod.getParameterList().getParameters().length;
|
||||
samType = functionalInterfaceMethod.getReturnType();
|
||||
if (samType == null) continue;
|
||||
}
|
||||
final PsiType samType = functionalInterfaceMethod.getReturnType();
|
||||
if (samType == null) return null;
|
||||
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries =
|
||||
collectQueryKeys(useScope, funInterface, samParamCount, samType, highLevelModules);
|
||||
return new SamDescriptor(funInterface, functionalInterfaceMethod, samType,
|
||||
convertToGlobalScope(funInterface.getProject(),
|
||||
funInterface.getUseScope().intersectWith(queryParameters.getEffectiveSearchScope())));
|
||||
});
|
||||
if (descriptor == null) return;
|
||||
|
||||
for (PsiFunctionalExpression expression : getCandidates(useScope, funInterface.getProject(), queries)) {
|
||||
MultiMap<Location, GlobalSearchScope> queries = collectQueryKeys(descriptor, highLevelModules);
|
||||
for (PsiFunctionalExpression expression : getCandidates(descriptor, queries)) {
|
||||
if (!processExpression(consumer, funInterface, expression)) {
|
||||
return;
|
||||
}
|
||||
@@ -130,23 +128,22 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<? extends PsiFunctionalExpression> getCandidates(GlobalSearchScope useScope,
|
||||
Project project,
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries) {
|
||||
private static Collection<? extends PsiFunctionalExpression> getCandidates(SamDescriptor descriptor, MultiMap<Location, GlobalSearchScope> queries) {
|
||||
MultiMap<PsiFile, PsiFunctionalExpression> exprs = MultiMap.createLinked();
|
||||
for (Map.Entry<FunctionalExpressionKey, Collection<GlobalSearchScope>> entry : queries.entrySet()) {
|
||||
for (Map.Entry<Location, Collection<GlobalSearchScope>> entry : queries.entrySet()) {
|
||||
ReadAction.run(() -> {
|
||||
ProgressManager.checkCanceled();
|
||||
GlobalSearchScope combinedScope = useScope.intersectWith(
|
||||
GlobalSearchScope combinedScope = descriptor.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;
|
||||
});
|
||||
for (FunctionalExpressionKey key : descriptor.generateKeys(entry.getKey())) {
|
||||
StubIndex.getInstance().processElements(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key,
|
||||
descriptor.samClass.getProject(), combinedScope, null,
|
||||
PsiFunctionalExpression.class,
|
||||
expression -> {
|
||||
exprs.putValue(expression.getContainingFile(), expression);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,34 +153,19 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MultiMap<FunctionalExpressionKey, GlobalSearchScope> collectQueryKeys(GlobalSearchScope useScope,
|
||||
PsiClass aClass,
|
||||
int samParamCount,
|
||||
@NotNull PsiType samType,
|
||||
Set<Module> candidateModules) {
|
||||
MultiMap<FunctionalExpressionKey, GlobalSearchScope> queries = MultiMap.createSet();
|
||||
|
||||
ReadAction.run(() -> {
|
||||
if (!samType.isValid()) return;
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
private static MultiMap<Location, GlobalSearchScope> collectQueryKeys(SamDescriptor descriptor, Set<Module> candidateModules) {
|
||||
MultiMap<Location, GlobalSearchScope> queries = MultiMap.createSet();
|
||||
queries.putValue(Location.UNKNOWN, descriptor.useScope);
|
||||
queries.putValue(new FunctionalExpressionKey.TypedLocation(assertNotNull(descriptor.samClass.getName())), descriptor.useScope);
|
||||
|
||||
//collect all methods with parameter of functional interface or free type parameter type
|
||||
for (final PsiMethod psiMethod : getCandidateMethodsWithSuitableParams(aClass, useScope, candidateModules, samParamCount, samType)) {
|
||||
for (final PsiMethod psiMethod : getCandidateMethodsWithSuitableParams(descriptor, candidateModules)) {
|
||||
ReadAction.run(() -> {
|
||||
if (!psiMethod.isValid() || !samType.isValid()) return;
|
||||
if (!psiMethod.isValid()) return;
|
||||
|
||||
final GlobalSearchScope methodUseScope = convertToGlobalScope(psiMethod.getProject(), psiMethod.getUseScope());
|
||||
for (FunctionalExpressionKey key : getQueryKeys(aClass, samParamCount, samType, psiMethod)) {
|
||||
queries.putValue(key, methodUseScope);
|
||||
for (Location location : getPossibleCallLocations(descriptor.samClass, psiMethod)) {
|
||||
queries.putValue(location, methodUseScope);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -191,11 +173,8 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<FunctionalExpressionKey> getQueryKeys(PsiClass samClass,
|
||||
int samParamCount,
|
||||
PsiType samType,
|
||||
PsiMethod calledMethod) {
|
||||
Set<FunctionalExpressionKey> keys = new HashSet<>();
|
||||
private static Set<Location> getPossibleCallLocations(PsiClass samClass, PsiMethod calledMethod) {
|
||||
Set<Location> keys = new HashSet<>();
|
||||
|
||||
String methodName = calledMethod.getName();
|
||||
PsiParameter[] parameters = calledMethod.getParameterList().getParameters();
|
||||
@@ -204,7 +183,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
if (canPassFunctionalExpression(samClass, parameter)) {
|
||||
for (int argCount : getPossibleArgCounts(parameters, paramIndex)) {
|
||||
for (int argIndex : getPossibleArgIndices(parameter, paramIndex, argCount)) {
|
||||
keys.addAll(generateKeys(samParamCount, samType, new CallLocation(methodName, argCount, argIndex)));
|
||||
keys.add(new CallLocation(methodName, argCount, argIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,24 +192,6 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static List<FunctionalExpressionKey> generateKeys(int samMethodParamsCount,
|
||||
PsiType samType,
|
||||
FunctionalExpressionKey.Location location) {
|
||||
List<FunctionalExpressionKey> result = new ArrayList<>();
|
||||
for (int lambdaParamCount : new int[]{FunctionalExpressionKey.UNKNOWN_PARAM_COUNT, samMethodParamsCount}) {
|
||||
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;
|
||||
}
|
||||
|
||||
private static int[] getPossibleArgCounts(PsiParameter[] parameters, int paramIndex) {
|
||||
if (parameters[parameters.length - 1].isVarArgs()) {
|
||||
return IntStream
|
||||
@@ -268,20 +229,16 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
return highLevelModules;
|
||||
}
|
||||
|
||||
private static Collection<PsiMethod> getCandidateMethodsWithSuitableParams(final PsiClass samClass,
|
||||
final GlobalSearchScope useScope,
|
||||
final Set<Module> candidateModules,
|
||||
int expectedFunExprParamsCount,
|
||||
PsiType samType) {
|
||||
private static Collection<PsiMethod> getCandidateMethodsWithSuitableParams(SamDescriptor descriptor, Set<Module> candidateModules) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Collection<PsiMethod>>() {
|
||||
@Override
|
||||
public Collection<PsiMethod> compute() {
|
||||
if (!samClass.isValid()) return Collections.emptyList();
|
||||
if (!descriptor.samClass.isValid()) return Collections.emptyList();
|
||||
|
||||
GlobalSearchScope visibleFromCandidates = combineResolveScopes(candidateModules, samClass);
|
||||
GlobalSearchScope visibleFromCandidates = combineResolveScopes(candidateModules, descriptor.samClass);
|
||||
if (visibleFromCandidates == null) return Collections.emptyList();
|
||||
|
||||
Set<String> usedMethodNames = collectMethodNamesCalledWithFunExpressions(samType, expectedFunExprParamsCount, useScope);
|
||||
Set<String> usedMethodNames = collectMethodNamesCalledWithFunExpressions(descriptor);
|
||||
|
||||
Set<PsiMethod> methods = ContainerUtil.newLinkedHashSet();
|
||||
Processor<PsiMethod> methodProcessor = method -> {
|
||||
@@ -293,8 +250,8 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
|
||||
StubIndexKey<String, PsiMethod> key = JavaMethodParameterTypesIndex.getInstance().getKey();
|
||||
StubIndex index = StubIndex.getInstance();
|
||||
Project project = samClass.getProject();
|
||||
index.processElements(key, assertNotNull(samClass.getName()), project, useScope.intersectWith(visibleFromCandidates), PsiMethod.class, methodProcessor);
|
||||
Project project = descriptor.samClass.getProject();
|
||||
index.processElements(key, assertNotNull(descriptor.samClass.getName()), project, descriptor.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;
|
||||
@@ -303,18 +260,16 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<String> collectMethodNamesCalledWithFunExpressions(PsiType samType, int samParamCount, GlobalSearchScope useScope) {
|
||||
boolean booleanCompatible = FunctionalExpressionKey.isBooleanCompatible(samType);
|
||||
boolean isVoid = PsiType.VOID.equals(samType);
|
||||
|
||||
private static Set<String> collectMethodNamesCalledWithFunExpressions(SamDescriptor descriptor) {
|
||||
Set<String> usedMethodNames = new HashSet<>();
|
||||
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key -> {
|
||||
ProgressManager.checkCanceled();
|
||||
if (key.canRepresent(samParamCount, booleanCompatible, isVoid) && key.location instanceof CallLocation) {
|
||||
if (key.canRepresent(descriptor.samParamCount, descriptor.booleanCompatible, descriptor.isVoid) &&
|
||||
key.location instanceof CallLocation) {
|
||||
usedMethodNames.add(((CallLocation)key.location).methodName);
|
||||
}
|
||||
return true;
|
||||
}, useScope, null);
|
||||
}, descriptor.useScope, null);
|
||||
return usedMethodNames;
|
||||
}
|
||||
|
||||
@@ -378,4 +333,36 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase<PsiFunct
|
||||
return LambdaUtil.isFunctionalClass(candidate) ||
|
||||
Arrays.stream(candidate.getAllMethods()).filter(m -> !m.hasModifierProperty(PsiModifier.DEFAULT)).count() == 0;
|
||||
}
|
||||
|
||||
private static class SamDescriptor {
|
||||
final PsiClass samClass;
|
||||
final GlobalSearchScope useScope;
|
||||
final int samParamCount;
|
||||
final boolean booleanCompatible;
|
||||
final boolean isVoid;
|
||||
|
||||
SamDescriptor(PsiClass samClass, PsiMethod samMethod, PsiType samType, GlobalSearchScope useScope) {
|
||||
this.samClass = samClass;
|
||||
this.useScope = useScope;
|
||||
this.samParamCount = samMethod.getParameterList().getParametersCount();
|
||||
this.booleanCompatible = FunctionalExpressionKey.isBooleanCompatible(samType);
|
||||
this.isVoid = PsiType.VOID.equals(samType);
|
||||
}
|
||||
|
||||
List<FunctionalExpressionKey> generateKeys(Location location) {
|
||||
List<FunctionalExpressionKey> result = new ArrayList<>();
|
||||
for (int lambdaParamCount : new int[]{FunctionalExpressionKey.UNKNOWN_PARAM_COUNT, samParamCount}) {
|
||||
result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.UNKNOWN, location));
|
||||
if (isVoid) {
|
||||
result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.VOID, location));
|
||||
} else {
|
||||
if (booleanCompatible) {
|
||||
result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.BOOLEAN, location));
|
||||
}
|
||||
result.add(new FunctionalExpressionKey(lambdaParamCount, FunctionalExpressionKey.CoarseType.NON_VOID, location));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -24,6 +25,10 @@ import com.intellij.psi.tree.IElementType;
|
||||
* N.B Please use {@link PsiPolyadicExpression} instead as a more general form of an infix operator-expression.
|
||||
*/
|
||||
public interface PsiBinaryExpression extends PsiPolyadicExpression {
|
||||
TokenSet BOOLEAN_OPERATION_TOKENS = TokenSet.create(JavaTokenType.EQEQ, JavaTokenType.NE,
|
||||
JavaTokenType.LT, JavaTokenType.GT, JavaTokenType.LE, JavaTokenType.GE,
|
||||
JavaTokenType.OROR, JavaTokenType.ANDAND);
|
||||
|
||||
/**
|
||||
* Returns the left operand of the expression.
|
||||
*
|
||||
|
||||
@@ -1464,14 +1464,7 @@ public class TypeConversionUtil {
|
||||
if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType);
|
||||
return lType;
|
||||
}
|
||||
if (sign == JavaTokenType.EQEQ ||
|
||||
sign == JavaTokenType.NE ||
|
||||
sign == JavaTokenType.LT ||
|
||||
sign == JavaTokenType.GT ||
|
||||
sign == JavaTokenType.LE ||
|
||||
sign == JavaTokenType.GE ||
|
||||
sign == JavaTokenType.OROR ||
|
||||
sign == JavaTokenType.ANDAND) {
|
||||
if (PsiBinaryExpression.BOOLEAN_OPERATION_TOKENS.contains(sign)) {
|
||||
return PsiType.BOOLEAN;
|
||||
}
|
||||
if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) {
|
||||
|
||||
+50
-21
@@ -19,6 +19,7 @@ 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.PsiBinaryExpression;
|
||||
import com.intellij.psi.PsiFunctionalExpression;
|
||||
import com.intellij.psi.impl.cache.RecordUtil;
|
||||
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey.CoarseType;
|
||||
@@ -78,18 +79,24 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
int argIndex = args == null ? -1 : getArgIndex(args, funExpr);
|
||||
String methodName = call == null ? null : getCalledMethodName(tree, call);
|
||||
return methodName == null || argIndex < 0
|
||||
? createVarLocation(tree, funExpr)
|
||||
? createTypedLocation(tree, funExpr)
|
||||
: new FunctionalExpressionKey.CallLocation(methodName, args.size(), argIndex);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static FunctionalExpressionKey.Location createVarLocation(LighterAST tree, LighterASTNode funExpr) {
|
||||
LighterASTNode var = findParent(tree, funExpr, TokenSet.create(LOCAL_VARIABLE, FIELD));
|
||||
if (var != null) {
|
||||
LighterASTNode typeElement = LightTreeUtil.firstChildOfType(tree, var, TYPE);
|
||||
private static FunctionalExpressionKey.Location createTypedLocation(LighterAST tree, LighterASTNode funExpr) {
|
||||
LighterASTNode scope = skipExpressionsUp(tree, funExpr, TokenSet.create(LOCAL_VARIABLE, FIELD, TYPE_CAST_EXPRESSION, RETURN_STATEMENT));
|
||||
if (scope != null) {
|
||||
if (scope.getTokenType() == RETURN_STATEMENT) {
|
||||
scope = LightTreeUtil.getParentOfType(tree, scope,
|
||||
TokenSet.create(METHOD),
|
||||
TokenSet.orSet(ElementType.MEMBER_BIT_SET, TokenSet.create(LAMBDA_EXPRESSION)));
|
||||
}
|
||||
|
||||
LighterASTNode typeElement = LightTreeUtil.firstChildOfType(tree, scope, TYPE);
|
||||
String typeText = getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, typeElement, JAVA_CODE_REFERENCE));
|
||||
if (typeText != null) {
|
||||
return new FunctionalExpressionKey.VariableLocation(typeText);
|
||||
return new FunctionalExpressionKey.TypedLocation(typeText);
|
||||
}
|
||||
}
|
||||
return FunctionalExpressionKey.Location.UNKNOWN;
|
||||
@@ -115,7 +122,10 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
if (funExpr.getTokenType() == METHOD_REF_EXPRESSION) return CoarseType.UNKNOWN;
|
||||
|
||||
LighterASTNode block = LightTreeUtil.firstChildOfType(tree, funExpr, CODE_BLOCK);
|
||||
if (block == null) return CoarseType.UNKNOWN;
|
||||
if (block == null) {
|
||||
LighterASTNode expr = findExpressionChild(funExpr, tree);
|
||||
return isBooleanExpression(tree, expr) ? CoarseType.BOOLEAN : CoarseType.UNKNOWN;
|
||||
}
|
||||
|
||||
final Ref<Boolean> returnsSomething = Ref.create(null);
|
||||
final AtomicBoolean isBoolean = new AtomicBoolean();
|
||||
@@ -124,20 +134,20 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
@Override
|
||||
public void visitNode(@NotNull LighterASTNode element) {
|
||||
IElementType type = element.getTokenType();
|
||||
if (type == LAMBDA_EXPRESSION || ElementType.MEMBER_BIT_SET.contains(type) || !returnsSomething.isNull()) {
|
||||
if (type == LAMBDA_EXPRESSION || ElementType.MEMBER_BIT_SET.contains(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == RETURN_STATEMENT) {
|
||||
LighterASTNode expr = LightTreeUtil.firstChildOfType(tree, element, ElementType.EXPRESSION_BIT_SET);
|
||||
LighterASTNode expr = findExpressionChild(element, tree);
|
||||
returnsSomething.set(expr != null);
|
||||
if (isBooleanLiteral(tree, expr)) {
|
||||
if (isBooleanExpression(tree, expr)) {
|
||||
isBoolean.set(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ElementType.JAVA_STATEMENT_BIT_SET.contains(type) && type != THROW_STATEMENT) {
|
||||
if (type == EXPRESSION_STATEMENT) {
|
||||
hasStatements.set(true);
|
||||
}
|
||||
|
||||
@@ -156,10 +166,30 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
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;
|
||||
private static LighterASTNode findExpressionChild(@NotNull LighterASTNode element, LighterAST tree) {
|
||||
return LightTreeUtil.firstChildOfType(tree, element, ElementType.EXPRESSION_BIT_SET);
|
||||
}
|
||||
|
||||
private static boolean isBooleanExpression(LighterAST tree, @Nullable LighterASTNode expr) {
|
||||
if (expr == null) return false;
|
||||
|
||||
IElementType type = expr.getTokenType();
|
||||
if (type == LITERAL_EXPRESSION) {
|
||||
IElementType child = tree.getChildren(expr).get(0).getTokenType();
|
||||
return child == JavaTokenType.TRUE_KEYWORD || child == JavaTokenType.FALSE_KEYWORD;
|
||||
}
|
||||
if (type == POLYADIC_EXPRESSION || type == BINARY_EXPRESSION) {
|
||||
return LightTreeUtil.firstChildOfType(tree, expr, PsiBinaryExpression.BOOLEAN_OPERATION_TOKENS) != null;
|
||||
}
|
||||
if (type == PREFIX_EXPRESSION) {
|
||||
return tree.getChildren(expr).get(0).getTokenType() == JavaTokenType.EXCL;
|
||||
}
|
||||
if (type == PARENTH_EXPRESSION) {
|
||||
return isBooleanExpression(tree, findExpressionChild(expr, tree));
|
||||
}
|
||||
if (type == CONDITIONAL_EXPRESSION) {
|
||||
List<LighterASTNode> children = LightTreeUtil.getChildrenOfType(tree, expr, ElementType.EXPRESSION_BIT_SET);
|
||||
return children.size() == 3 && (isBooleanExpression(tree, children.get(1)) || isBooleanExpression(tree, children.get(2)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -208,7 +238,7 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
|
||||
@Nullable
|
||||
private static LighterASTNode getContainingCall(LighterAST tree, LighterASTNode node) {
|
||||
LighterASTNode expressionList = findParent(tree, node, TokenSet.create(EXPRESSION_LIST));
|
||||
LighterASTNode expressionList = skipExpressionsUp(tree, node, TokenSet.create(EXPRESSION_LIST));
|
||||
if (expressionList != null) {
|
||||
LighterASTNode parent = tree.getParent(expressionList);
|
||||
if (parent != null && parent.getTokenType() == ANONYMOUS_CLASS) {
|
||||
@@ -221,13 +251,12 @@ public abstract class FunctionalExpressionElementType<T extends PsiFunctionalExp
|
||||
return null;
|
||||
}
|
||||
|
||||
private static LighterASTNode findParent(LighterAST tree, LighterASTNode node, TokenSet elementType) {
|
||||
private static LighterASTNode skipExpressionsUp(LighterAST tree, @NotNull LighterASTNode node, TokenSet types) {
|
||||
node = tree.getParent(node);
|
||||
while (node != null) {
|
||||
final IElementType type = node.getTokenType();
|
||||
if (elementType.contains(type)) return node;
|
||||
if (ElementType.JAVA_STATEMENT_BIT_SET.contains(type) ||
|
||||
ElementType.MEMBER_BIT_SET.contains(type) ||
|
||||
ARRAY_INITIALIZER_EXPRESSION == type) return null;
|
||||
if (types.contains(type)) return node;
|
||||
if (type != PARENTH_EXPRESSION && type != CONDITIONAL_EXPRESSION) return null;
|
||||
node = tree.getParent(node);
|
||||
}
|
||||
return null;
|
||||
|
||||
+9
-9
@@ -57,7 +57,7 @@ public class FunctionalExpressionKey {
|
||||
byte locationType = dataStream.readByte();
|
||||
if (locationType == 0) return Location.UNKNOWN;
|
||||
if (locationType == 1) return CallLocation.deserializeCall(dataStream);
|
||||
if (locationType == 2) return VariableLocation.deserializeField(dataStream);
|
||||
if (locationType == 2) return TypedLocation.deserializeField(dataStream);
|
||||
throw new AssertionError(locationType);
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ public class FunctionalExpressionKey {
|
||||
dataStream.writeByte(1);
|
||||
((CallLocation)location).serializeCall(dataStream);
|
||||
}
|
||||
else if (location instanceof VariableLocation) {
|
||||
else if (location instanceof TypedLocation) {
|
||||
dataStream.writeByte(2);
|
||||
((VariableLocation)location).serializeVariable(dataStream);
|
||||
((TypedLocation)location).serializeVariable(dataStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,19 +194,19 @@ public class FunctionalExpressionKey {
|
||||
|
||||
}
|
||||
|
||||
public static class VariableLocation implements Location {
|
||||
public static class TypedLocation implements Location {
|
||||
@NotNull public final String varType;
|
||||
|
||||
public VariableLocation(@NotNull String varType) {
|
||||
public TypedLocation(@NotNull String varType) {
|
||||
this.varType = varType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof VariableLocation)) return false;
|
||||
if (!(o instanceof TypedLocation)) return false;
|
||||
|
||||
VariableLocation location = (VariableLocation)o;
|
||||
TypedLocation location = (TypedLocation)o;
|
||||
|
||||
if (!varType.equals(location.varType)) return false;
|
||||
|
||||
@@ -225,8 +225,8 @@ public class FunctionalExpressionKey {
|
||||
.toString();
|
||||
}
|
||||
|
||||
public static VariableLocation deserializeField(DataInput dataStream) throws IOException {
|
||||
return new VariableLocation(IOUtil.readUTF(dataStream));
|
||||
public static TypedLocation deserializeField(DataInput dataStream) throws IOException {
|
||||
return new TypedLocation(IOUtil.readUTF(dataStream));
|
||||
}
|
||||
|
||||
public void serializeVariable(DataOutput dataStream) throws IOException {
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.io.IOException;
|
||||
* @author max
|
||||
*/
|
||||
public class JavaFileElementType extends ILightStubFileElementType<PsiJavaFileStub> {
|
||||
public static final int STUB_VERSION = 32;
|
||||
public static final int STUB_VERSION = 33;
|
||||
|
||||
public JavaFileElementType() {
|
||||
super("java.FILE", JavaLanguage.INSTANCE);
|
||||
|
||||
@@ -147,4 +147,19 @@ public class LightTreeUtil {
|
||||
}
|
||||
tree.disposeChildren(children);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LighterASTNode getParentOfType(@NotNull LighterAST tree, @Nullable LighterASTNode node,
|
||||
@NotNull TokenSet types, @NotNull TokenSet stopAt) {
|
||||
if (node == null) return null;
|
||||
node = tree.getParent(node);
|
||||
while (node != null) {
|
||||
final IElementType type = node.getTokenType();
|
||||
if (types.contains(type)) return node;
|
||||
if (stopAt.contains(type)) return null;
|
||||
node = tree.getParent(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user