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:
peter
2016-07-29 19:44:32 +02:00
parent 3edd4620bf
commit ded3f46951
7 changed files with 159 additions and 130 deletions
@@ -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) {
@@ -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;
@@ -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;
}
}