diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaFunctionalExpressionIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaFunctionalExpressionIndex.java new file mode 100644 index 000000000000..263e959c26cd --- /dev/null +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaFunctionalExpressionIndex.java @@ -0,0 +1,349 @@ +/* + * 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; + +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.lang.LighterAST; +import com.intellij.lang.LighterASTNode; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiBinaryExpression; +import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey; +import com.intellij.psi.impl.source.Constants; +import com.intellij.psi.impl.source.JavaFileElementType; +import com.intellij.psi.impl.source.JavaLightTreeUtil; +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.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.indexing.*; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.DataInputOutputUtil; +import com.intellij.util.io.KeyDescriptor; +import gnu.trove.TIntArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.intellij.psi.impl.source.tree.JavaElementType.*; + +public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension implements PsiDependentIndex { + public static final ID INDEX_ID = ID.create("java.fun.expression"); + 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 + private static FunctionalExpressionKey.Location calcLocation(LighterAST tree, LighterASTNode funExpr) { + LighterASTNode call = getContainingCall(tree, funExpr); + List args = JavaLightTreeUtil.getArgList(tree, call); + int argIndex = args == null ? -1 : getArgIndex(args, funExpr); + String methodName = call == null ? null : getCalledMethodName(tree, call); + return methodName == null || argIndex < 0 + ? createTypedLocation(tree, funExpr) + : new FunctionalExpressionKey.CallLocation(methodName, args.size(), argIndex, StreamApiDetector.isStreamApiCall(tree, call)); + } + + @NotNull + 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 = JavaLightTreeUtil.getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, typeElement, JAVA_CODE_REFERENCE)); + if (typeText != null) { + return new FunctionalExpressionKey.TypedLocation(typeText); + } + } + return FunctionalExpressionKey.Location.UNKNOWN; + } + + 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; + } + + private static FunctionalExpressionKey.CoarseType calcType(final LighterAST tree, LighterASTNode funExpr) { + if (funExpr.getTokenType() == METHOD_REF_EXPRESSION) return FunctionalExpressionKey.CoarseType.UNKNOWN; + + LighterASTNode block = LightTreeUtil.firstChildOfType(tree, funExpr, CODE_BLOCK); + if (block == null) { + LighterASTNode expr = findExpressionChild(funExpr, tree); + return isBooleanExpression(tree, expr) ? FunctionalExpressionKey.CoarseType.BOOLEAN : FunctionalExpressionKey.CoarseType.UNKNOWN; + } + + 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)) { + return; + } + + if (type == RETURN_STATEMENT) { + LighterASTNode expr = findExpressionChild(element, tree); + returnsSomething.set(expr != null); + if (isBooleanExpression(tree, expr)) { + isBoolean.set(true); + } + return; + } + + if (type == EXPRESSION_STATEMENT) { + hasStatements.set(true); + } + + super.visitNode(element); + } + }.visitNode(block); + + if (isBoolean.get()) { + return FunctionalExpressionKey.CoarseType.BOOLEAN; + } + + if (returnsSomething.isNull()) { + return hasStatements.get() ? FunctionalExpressionKey.CoarseType.VOID : FunctionalExpressionKey.CoarseType.UNKNOWN; + } + + return returnsSomething.get() ? FunctionalExpressionKey.CoarseType.NON_VOID : FunctionalExpressionKey.CoarseType.VOID; + } + + 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 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; + } + + 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(); + } + + @Nullable + 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 ? null : JavaLightTreeUtil.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 JavaLightTreeUtil.getNameIdentifierText(tree, findClass(tree, call)); + } + + return JavaLightTreeUtil.getNameIdentifierText(tree, methodExpr); + } + + @Nullable + private static String getSuperClassName(LighterAST tree, LighterASTNode call) { + LighterASTNode aClass = findClass(tree, call); + LighterASTNode extendsList = LightTreeUtil.firstChildOfType(tree, aClass, EXTENDS_LIST); + return JavaLightTreeUtil.getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, extendsList, JAVA_CODE_REFERENCE)); + } + + @Nullable + private static LighterASTNode getContainingCall(LighterAST tree, LighterASTNode node) { + LighterASTNode expressionList = skipExpressionsUp(tree, node, TokenSet.create(EXPRESSION_LIST)); + 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 skipExpressionsUp(LighterAST tree, @NotNull LighterASTNode node, TokenSet types) { + node = tree.getParent(node); + while (node != null) { + final IElementType type = node.getTokenType(); + if (types.contains(type)) return node; + if (type != PARENTH_EXPRESSION && type != CONDITIONAL_EXPRESSION) return null; + 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; + } + + @NotNull + @Override + public KeyDescriptor getKeyDescriptor() { + return KEY_DESCRIPTOR; + } + + @Override + public int getVersion() { + return 1; + } + + @NotNull + @Override + public ID getName() { + return INDEX_ID; + } + + @NotNull + @Override + public DataIndexer getIndexer() { + return inputData -> { + Map result = new HashMap<>(); + + LighterAST tree = ((FileContentImpl)inputData).getLighterASTForPsiDependentIndex(); + new RecursiveLighterASTNodeWalkingVisitor(tree) { + @Override + public void visitNode(@NotNull LighterASTNode element) { + if (element.getTokenType() == METHOD_REF_EXPRESSION || + element.getTokenType() == LAMBDA_EXPRESSION) { + FunctionalExpressionKey key = new FunctionalExpressionKey(getFunExprParameterCount(tree, element), + calcType(tree, element), + calcLocation(tree, element)); + TIntArrayList list = result.get(key); + if (list == null) { + result.put(key, list = new TIntArrayList()); + } + list.add(element.getStartOffset()); + } + + super.visitNode(element); + } + }.visitNode(tree.getRoot()); + + return result; + }; + } + + @NotNull + @Override + public DataExternalizer getValueExternalizer() { + return new DataExternalizer() { + @Override + public void save(@NotNull DataOutput out, TIntArrayList value) throws IOException { + DataInputOutputUtil.writeINT(out, value.size()); + for (int i = 0; i < value.size(); i++) { + DataInputOutputUtil.writeINT(out, value.get(i)); + } + } + + @Override + public TIntArrayList read(@NotNull DataInput in) throws IOException { + int length = DataInputOutputUtil.readINT(in); + TIntArrayList list = new TIntArrayList(length); + for (int i = 0; i < length; i++) { + list.add(DataInputOutputUtil.readINT(in)); + } + return list; + } + }; + } + + @NotNull + @Override + public FileBasedIndex.InputFilter getInputFilter() { + return new DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE) { + @Override + public boolean acceptInput(@NotNull VirtualFile file) { + return super.acceptInput(file) && JavaFileElementType.isInSourceContent(file); + } + }; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } +} \ No newline at end of file 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 deleted file mode 100644 index 5f576c275227..000000000000 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaFunctionalExpressionIndex.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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/search/JavaFunctionalExpressionSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java index aa3a63c776d0..79fb585ea60a 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 @@ -32,12 +32,12 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.java.JavaFunctionalExpressionIndex; 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; import com.intellij.psi.search.*; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.FunctionalExpressionSearch; @@ -48,6 +48,7 @@ import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.containers.MultiMap; +import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,7 +58,7 @@ import java.util.stream.*; import static com.intellij.util.ObjectUtils.assertNotNull; public class JavaFunctionalExpressionSearcher extends QueryExecutorBase { - private static final Logger LOG = Logger.getInstance("#" + JavaFunctionalExpressionSearcher.class.getName()); + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.search.JavaFunctionalExpressionSearcher"); /** * 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 @@ -104,22 +105,37 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase queries = collectQueryKeys(descriptor, highLevelModules); - for (PsiFunctionalExpression expression : getCandidates(descriptor, queries)) { - if (!processExpression(consumer, funInterface, expression)) { + for (Map.Entry> entry : getCandidateOffsets(descriptor, queries).entrySet()) { + if (!processFile(consumer, funInterface, entry.getKey(), entry.getValue())) { return; } } } } - private static Boolean processExpression(@NotNull Processor consumer, - PsiClass aClass, - PsiFunctionalExpression expression) { + private static boolean processFile(@NotNull Processor consumer, + PsiClass samClass, + VirtualFile vFile, Collection offsets) { return ReadAction.compute(() -> { - if (expression.isValid() && - InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(expression.getFunctionalInterfaceType()), aClass, true)) { - if (!consumer.process(expression)) { - return false; + if (!samClass.isValid()) return true; + + PsiFile file = samClass.getManager().findFile(vFile); + if (!(file instanceof PsiJavaFile)) { + LOG.error("Non-java file " + file + "; " + vFile); + return true; + } + + for (Integer offset : offsets) { + PsiFunctionalExpression expression = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiFunctionalExpression.class, false); + if (expression == null || expression.getTextRange().getStartOffset() != offset) { + LOG.error("Fun expression not found in " + file + " at " + offset); + continue; + } + + if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(expression.getFunctionalInterfaceType()), samClass, true)) { + if (!consumer.process(expression)) { + return false; + } } } @@ -128,30 +144,49 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase getCandidates(SamDescriptor descriptor, MultiMap queries) { - MultiMap exprs = MultiMap.createLinked(); - for (Map.Entry> entry : queries.entrySet()) { - ReadAction.run(() -> { - ProgressManager.checkCanceled(); - GlobalSearchScope combinedScope = descriptor.useScope.intersectWith( - GlobalSearchScope.union(entry.getValue().toArray(new GlobalSearchScope[0]))); - 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; - }); - } - }); + private static MultiMap getCandidateOffsets(SamDescriptor descriptor, MultiMap queries) { + MultiMap result = MultiMap.create(); + for (Location location : queries.keySet()) { + MultiMap offsets = getOffsetsForLocation(descriptor, location, queries.get(location)); + result.putAllValues(offsets); + if (LOG.isDebugEnabled()) { + logIfLarge(offsets, location); + } } - Collection result = exprs.values(); - LOG.info("checking " + result.size() + " fun-expressions in " + exprs.size() + " files"); + if (LOG.isDebugEnabled()) { + LOG.debug("checking " + result.values().size() + " fun-expressions in " + result.keySet().size() + " files"); + } return result; } + private static void logIfLarge(MultiMap offsets, Location location) { + int delta = offsets.values().size(); + if (delta > 5) { + String sample = offsets.entrySet().stream().limit(10).map(e -> e.getKey().getName() + "->" + e.getValue()).collect(Collectors.joining(", ")); + LOG.debug(delta + " expressions for " + location + "; " + sample); + } + } + + @NotNull + private static MultiMap getOffsetsForLocation(SamDescriptor descriptor, Location location, Collection scopes) { + MultiMap map = MultiMap.create(); + ReadAction.run(() -> { + ProgressManager.checkCanceled(); + GlobalSearchScope combinedScope = descriptor.useScope.intersectWith( + GlobalSearchScope.union(scopes.toArray(new GlobalSearchScope[0]))); + for (FunctionalExpressionKey key : descriptor.generateKeys(location)) { + FileBasedIndex.getInstance().processValues(JavaFunctionalExpressionIndex.INDEX_ID, key, null, (file, offsets) -> { + for (int i : offsets.toNativeArray()) { + map.putValue(file, i); + } + return true; + }, new JavaSourceFilterScope(combinedScope)); + } + }); + return map; + } + @NotNull private static MultiMap collectQueryKeys(SamDescriptor descriptor, Set candidateModules) { MultiMap queries = MultiMap.createSet(); @@ -273,7 +308,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase collectMethodNamesCalledWithFunExpressions(SamDescriptor descriptor) { Set usedMethodNames = new HashSet<>(); - StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FUNCTIONAL_EXPRESSIONS, key -> { + FileBasedIndex.getInstance().processAllKeys(JavaFunctionalExpressionIndex.INDEX_ID, key -> { ProgressManager.checkCanceled(); if (key.canRepresent(descriptor.samParamCount, descriptor.booleanCompatible, descriptor.isVoid) && key.location instanceof CallLocation) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/StreamApiDetector.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/StreamApiDetector.java similarity index 99% rename from java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/StreamApiDetector.java rename to java/java-psi-impl/src/com/intellij/psi/impl/java/StreamApiDetector.java index 3b764dd675dd..1d9f12302442 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/StreamApiDetector.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/StreamApiDetector.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.psi.impl.java.stubs; +package com.intellij.psi.impl.java; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; 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 46658ccdcd79..e011ad1c3f4a 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 @@ -17,31 +17,14 @@ 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.PsiBinaryExpression; import com.intellij.psi.PsiFunctionalExpression; -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.JavaLightTreeUtil; -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.psi.tree.TokenSet; 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.*; public abstract class FunctionalExpressionElementType extends JavaStubElementType,T> { public FunctionalExpressionElementType(String debugName) { @@ -50,211 +33,20 @@ public abstract class FunctionalExpressionElementType 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)); + return new FunctionalExpressionStub(parentStub, this); } @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) { - return new FunctionalExpressionStub(parentStub, this, - new FunctionalExpressionKey(getFunExprParameterCount(tree, funExpr), - calcType(tree, funExpr), - calcLocation(tree, funExpr))); - } - - @NotNull - private static FunctionalExpressionKey.Location calcLocation(LighterAST tree, LighterASTNode funExpr) { - LighterASTNode call = getContainingCall(tree, funExpr); - List args = JavaLightTreeUtil.getArgList(tree, call); - int argIndex = args == null ? -1 : getArgIndex(args, funExpr); - String methodName = call == null ? null : getCalledMethodName(tree, call); - return methodName == null || argIndex < 0 - ? createTypedLocation(tree, funExpr) - : new FunctionalExpressionKey.CallLocation(methodName, args.size(), argIndex, StreamApiDetector.isStreamApiCall(tree, call)); - } - - @NotNull - 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 = JavaLightTreeUtil.getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, typeElement, JAVA_CODE_REFERENCE)); - if (typeText != null) { - return new FunctionalExpressionKey.TypedLocation(typeText); - } - } - return FunctionalExpressionKey.Location.UNKNOWN; - } - - 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; - } - - private static CoarseType calcType(final LighterAST tree, LighterASTNode funExpr) { - if (funExpr.getTokenType() == METHOD_REF_EXPRESSION) return CoarseType.UNKNOWN; - - LighterASTNode block = LightTreeUtil.firstChildOfType(tree, funExpr, CODE_BLOCK); - if (block == null) { - LighterASTNode expr = findExpressionChild(funExpr, tree); - return isBooleanExpression(tree, expr) ? CoarseType.BOOLEAN : CoarseType.UNKNOWN; - } - - 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)) { - return; - } - - if (type == RETURN_STATEMENT) { - LighterASTNode expr = findExpressionChild(element, tree); - returnsSomething.set(expr != null); - if (isBooleanExpression(tree, expr)) { - isBoolean.set(true); - } - return; - } - - if (type == EXPRESSION_STATEMENT) { - hasStatements.set(true); - } - - super.visitNode(element); - } - }.visitNode(block); - - 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 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 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; - } - - 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(); - } - - @Nullable - 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 ? null : JavaLightTreeUtil.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 JavaLightTreeUtil.getNameIdentifierText(tree, findClass(tree, call)); - } - - return JavaLightTreeUtil.getNameIdentifierText(tree, methodExpr); - } - - @Nullable - private static String getSuperClassName(LighterAST tree, LighterASTNode call) { - LighterASTNode aClass = findClass(tree, call); - LighterASTNode extendsList = LightTreeUtil.firstChildOfType(tree, aClass, EXTENDS_LIST); - return JavaLightTreeUtil.getNameIdentifierText(tree, LightTreeUtil.firstChildOfType(tree, extendsList, JAVA_CODE_REFERENCE)); - } - - @Nullable - private static LighterASTNode getContainingCall(LighterAST tree, LighterASTNode node) { - LighterASTNode expressionList = skipExpressionsUp(tree, node, TokenSet.create(EXPRESSION_LIST)); - 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 skipExpressionsUp(LighterAST tree, @NotNull LighterASTNode node, TokenSet types) { - node = tree.getParent(node); - while (node != null) { - final IElementType type = node.getTokenType(); - if (types.contains(type)) return node; - if (type != PARENTH_EXPRESSION && type != CONDITIONAL_EXPRESSION) return null; - 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; + return new FunctionalExpressionStub(parentStub, this); } } 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 99a523272a6b..dac7f807d6f4 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,19 +19,11 @@ 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, @NotNull FunctionalExpressionKey indexKey) { + protected FunctionalExpressionStub(StubElement parent, IStubElementType elementType) { 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/index/JavaStubIndexKeys.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/index/JavaStubIndexKeys.java index 46624449cb5c..5d9b78e09ed3 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,7 +16,6 @@ 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; /** @@ -32,7 +31,6 @@ 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 827a6805cdbd..05db00ecee09 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 = 34; + public static final int STUB_VERSION = 35; public JavaFileElementType() { super("java.FILE", JavaLanguage.INSTANCE); @@ -56,6 +56,10 @@ public class JavaFileElementType extends ILightStubFileElementType - +