From d5a3e4eaf1dce8b66350d913e31bc153c9d4f747 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 10 Aug 2017 18:04:05 +0300 Subject: [PATCH 01/10] javac ast indices: add cast index --- .../ast/JavacReferenceCollectorListener.java | 12 +++--- .../jps/javac/ast/JavacTreeRefScanner.java | 21 ++++++++- .../jps/javac/ast/api/JavacFileData.java | 32 +++++++++++++- .../jetbrains/jps/javac/ast/api/JavacRef.java | 2 +- .../jps/javac/ast/api/JavacTypeCast.java | 40 +++++++++++++++++ .../BackwardReferenceIndexUtil.java | 29 ++++++++++--- .../BackwardReferenceRegistrar.java | 12 ++++-- .../backwardRefs/index/CompiledFileData.java | 10 ++++- .../backwardRefs/index/CompilerIndices.java | 43 +++++++++++++++++-- ...cessRefCollectorCompilerToolExtension.java | 4 +- .../ast/api/JavacFileReferencesRegistrar.java | 9 ++-- .../referencesIndex/castData/Foo.java | 5 +++ .../referencesIndex/castData/initialIndex.txt | 18 ++++++++ .../referencesIndex/castDataArrays/Foo.java | 7 +++ .../castDataArrays/initialIndex.txt | 14 ++++++ .../referencesIndex/castDataGenerics/Foo.java | 7 +++ .../castDataGenerics/initialIndex.txt | 19 ++++++++ .../references/ReferenceIndexTest.kt | 14 +++++- .../references/ReferenceIndexTestBase.kt | 21 ++++++++- 19 files changed, 289 insertions(+), 30 deletions(-) create mode 100644 jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacTypeCast.java create mode 100644 jps/jps-builders/testData/referencesIndex/castData/Foo.java create mode 100644 jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt create mode 100644 jps/jps-builders/testData/referencesIndex/castDataArrays/Foo.java create mode 100644 jps/jps-builders/testData/referencesIndex/castDataArrays/initialIndex.txt create mode 100644 jps/jps-builders/testData/referencesIndex/castDataGenerics/Foo.java create mode 100644 jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index 1a4e71ef5990..e0007ae56657 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -21,10 +21,7 @@ import com.sun.source.util.*; import com.sun.tools.javac.util.ClientCodeException; import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.javac.ast.api.JavacDef; -import org.jetbrains.jps.javac.ast.api.JavacFileData; -import org.jetbrains.jps.javac.ast.api.JavacNameTable; -import org.jetbrains.jps.javac.ast.api.JavacRef; +import org.jetbrains.jps.javac.ast.api.*; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -230,6 +227,7 @@ final class JavacReferenceCollectorListener implements TaskListener { myFileData = new JavacFileData(filePath, createReferenceHolder(), myDivideImportRefs ? createReferenceHolder() : EMPTY_T_OBJ_INT_MAP, + new ArrayList(), createDefinitionHolder()); myTreeHelper = new JavacTreeHelper(unitTree, myTreeUtility); } @@ -242,6 +240,10 @@ final class JavacReferenceCollectorListener implements TaskListener { myFileData.getDefs().add(def); } + public void sinkTypeCast(JavacTypeCast typeCast) { + myFileData.getCasts().add(typeCast); + } + @Nullable JavacRef.JavacElementRefBase asJavacRef(Element element) { return asJavacRef(element, null); diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java index b85ef0b241e8..b621691eafa4 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -21,6 +21,7 @@ import com.sun.source.util.TreeScanner; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacNameTable; import org.jetbrains.jps.javac.ast.api.JavacRef; +import org.jetbrains.jps.javac.ast.api.JavacTypeCast; import javax.lang.model.element.*; import javax.lang.model.type.ArrayType; @@ -206,6 +207,24 @@ class JavacTreeRefScanner extends TreeScanner myRefs; private final TObjectIntHashMap myImportRefs; + private final List myCasts; private final List myDefs; public JavacFileData(@NotNull String path, @NotNull TObjectIntHashMap refs, @NotNull TObjectIntHashMap importRefs, + @NotNull List casts, @NotNull List defs) { myFilePath = path; myRefs = refs; myImportRefs = importRefs; + myCasts = casts; myDefs = defs; } @@ -62,6 +65,11 @@ public class JavacFileData { return myImportRefs; } + @NotNull + public List getCasts() { + return myCasts; + } + @NotNull public List getDefs() { return myDefs; @@ -75,6 +83,7 @@ public class JavacFileData { stream.writeUTF(getFilePath()); saveRefs(stream, getRefs()); saveRefs(stream, getImportRefs()); + saveCasts(stream, getCasts()); saveDefs(stream, getDefs()); } catch (IOException e) { @@ -91,6 +100,7 @@ public class JavacFileData { return new JavacFileData(in.readUTF(), readRefs(in), readRefs(in), + readCasts(in), readDefs(in)); } catch (IOException e) { @@ -246,4 +256,24 @@ public class JavacFileData { }); return modifierList.isEmpty() ? Collections.emptySet() : EnumSet.copyOf(modifierList); } + + private static void saveCasts(@NotNull final DataOutput output, @NotNull List casts) throws IOException { + DataInputOutputUtilRt.writeSeq(output, casts, new ThrowableConsumer() { + @Override + public void consume(JavacTypeCast cast) throws IOException { + writeJavacRef(output, cast.getOperandType()); + writeJavacRef(output, cast.getCastType()); + } + }); + } + + @NotNull + private static List readCasts(@NotNull final DataInput input) throws IOException { + return DataInputOutputUtilRt.readSeq(input, new ThrowableComputable() { + @Override + public JavacTypeCast compute() throws IOException { + return new JavacTypeCast((JavacRef.JavacClass)readJavacRef(input), (JavacRef.JavacClass)readJavacRef(input)); + } + }); + } } diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacRef.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacRef.java index c5947dd5ae46..277489319aea 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacRef.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacRef.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacTypeCast.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacTypeCast.java new file mode 100644 index 000000000000..5f81fa6f9351 --- /dev/null +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacTypeCast.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 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 org.jetbrains.jps.javac.ast.api; + +import org.jetbrains.annotations.NotNull; + +public class JavacTypeCast { + @NotNull + private final JavacRef.JavacClass myOperandType; + @NotNull + private final JavacRef.JavacClass myCastType; + + public JavacTypeCast(@NotNull JavacRef.JavacClass operandType, @NotNull JavacRef.JavacClass castType) { + myOperandType = operandType; + myCastType = castType; + } + + @NotNull + public JavacRef.JavacClass getOperandType() { + return myOperandType; + } + + @NotNull + public JavacRef.JavacClass getCastType() { + return myCastType; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java index d0c83802e223..ad192ae54db4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -23,23 +23,29 @@ import gnu.trove.TObjectIntHashMap; import org.jetbrains.jps.backwardRefs.index.CompiledFileData; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacRef; +import org.jetbrains.jps.javac.ast.api.JavacTypeCast; import java.io.IOException; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; public class BackwardReferenceIndexUtil { static void registerFile(String filePath, TObjectIntHashMap refs, - List defs, + Collection defs, + Collection casts, final BackwardReferenceIndexWriter writer) { try { final int fileId = writer.enumeratePath(filePath); int funExprId = 0; - final Map definitions = new HashMap<>(defs.size()); - final Map> backwardHierarchyMap = new HashMap<>(); - final Map> signatureData = new THashMap<>(); + Map definitions = new HashMap<>(defs.size()); + Map> backwardHierarchyMap = new HashMap<>(); + Map> signatureData = new THashMap<>(); + THashMap> castMap = new THashMap<>(); + final AnonymousClassEnumerator anonymousClassEnumerator = new AnonymousClassEnumerator(); @@ -102,7 +108,16 @@ public class BackwardReferenceIndexUtil { if (exception[0] != null) { throw exception[0]; } - writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, convertedRefs, definitions, signatureData)); + + for (JavacTypeCast cast : casts) { + LightRef enumeratedCastType = writer.enumerateNames(cast.getCastType(), name -> null); + if (enumeratedCastType == null) continue; + LightRef enumeratedOperandType = writer.enumerateNames(cast.getOperandType(), name -> null); + if (enumeratedOperandType == null) continue; + castMap.computeIfAbsent(enumeratedOperandType, t -> new SmartList<>()).add(enumeratedCastType); + } + + writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData)); } catch (IOException e) { writer.setRebuildCause(e); diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java index 82163edd81f7..018659aec2fb 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,11 +16,12 @@ package org.jetbrains.jps.backwardRefs; import gnu.trove.TObjectIntHashMap; +import org.jetbrains.jps.javac.ast.api.JavacTypeCast; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar; import org.jetbrains.jps.javac.ast.api.JavacRef; -import java.util.List; +import java.util.Collection; public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar { private volatile BackwardReferenceIndexWriter myWriter; @@ -41,7 +42,10 @@ public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar } @Override - public void registerFile(String filePath, TObjectIntHashMap refs, List defs) { - BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, myWriter); + public void registerFile(String filePath, + TObjectIntHashMap refs, + Collection defs, + Collection casts) { + BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, casts, myWriter); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompiledFileData.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompiledFileData.java index f1d2ebb09aa1..af8af9e6488a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompiledFileData.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompiledFileData.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -24,15 +24,18 @@ import java.util.Map; public class CompiledFileData { private final Map> myBackwardHierarchyMap; + private final Map> myCasts; private final Map myReferences; private final Map myDefinitions; private final Map> mySignatureData; public CompiledFileData(@NotNull Map> backwardHierarchyMap, + @NotNull Map> casts, @NotNull Map references, @NotNull Map definitions, @NotNull Map> signatureData) { myBackwardHierarchyMap = backwardHierarchyMap; + myCasts = casts; myReferences = references; myDefinitions = definitions; mySignatureData = signatureData; @@ -57,4 +60,9 @@ public class CompiledFileData { public Map> getSignatureData() { return mySignatureData; } + + @NotNull + public Map> getCasts() { + return myCasts; + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java index ec8f5a618e29..835903607a86 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,7 +16,9 @@ package org.jetbrains.jps.backwardRefs.index; import com.intellij.openapi.util.io.DataInputOutputUtilRt; -import com.intellij.util.indexing.*; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.IndexExtension; +import com.intellij.util.indexing.IndexId; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.KeyDescriptor; @@ -41,12 +43,47 @@ public class CompilerIndices { public final static IndexId> BACK_HIERARCHY = IndexId.create("back.hierarchy"); public final static IndexId BACK_CLASS_DEF = IndexId.create("back.class.def"); public final static IndexId> BACK_MEMBER_SIGN = IndexId.create("back.member.sign"); + public final static IndexId> BACK_CAST = IndexId.create("back.cast"); public static List> getIndices() { return Arrays.asList(createBackwardClassDefinitionExtension(), createBackwardUsagesExtension(), createBackwardHierarchyExtension(), - createBackwardSignatureExtension()); + createBackwardSignatureExtension(), + createBackwardCastExtension()); + } + + private static IndexExtension, CompiledFileData> createBackwardCastExtension() { + return new IndexExtension, CompiledFileData>() { + @NotNull + @Override + public IndexId> getName() { + return BACK_CAST; + } + + @NotNull + @Override + public DataIndexer, CompiledFileData> getIndexer() { + return CompiledFileData::getCasts; + } + + @NotNull + @Override + public KeyDescriptor getKeyDescriptor() { + return LightRefDescriptor.INSTANCE; + } + + @NotNull + @Override + public DataExternalizer> getValueExternalizer() { + return createLightRefSeqExternalizer(); + } + + @Override + public int getVersion() { + return VERSION; + } + }; } private static IndexExtension createBackwardUsagesExtension() { diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/InProcessRefCollectorCompilerToolExtension.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/InProcessRefCollectorCompilerToolExtension.java index d776c938ef0c..a355a525d6cf 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/InProcessRefCollectorCompilerToolExtension.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/InProcessRefCollectorCompilerToolExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -78,7 +78,7 @@ public class InProcessRefCollectorCompilerToolExtension extends AbstractRefColle public void consume(JavacFileData data) { for (JavacFileReferencesRegistrar registrar : myRegistrars) { if (registrar.isEnabled()) { - registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs()); + registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs(), data.getCasts()); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java index e7fde87db14f..54f748496837 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,7 +17,7 @@ package org.jetbrains.jps.javac.ast.api; import gnu.trove.TObjectIntHashMap; -import java.util.List; +import java.util.Collection; public interface JavacFileReferencesRegistrar { void initialize(); @@ -26,5 +26,8 @@ public interface JavacFileReferencesRegistrar { boolean onlyImports(); - void registerFile(String filePath, TObjectIntHashMap refs, List defs); + void registerFile(String filePath, + TObjectIntHashMap refs, + Collection defs, + Collection casts); } diff --git a/jps/jps-builders/testData/referencesIndex/castData/Foo.java b/jps/jps-builders/testData/referencesIndex/castData/Foo.java new file mode 100644 index 000000000000..04f47d8c7f50 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castData/Foo.java @@ -0,0 +1,5 @@ +class Foo { + void m(CharSequence c) { + ((String)c).getBytes(); + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt new file mode 100644 index 000000000000..0f58e4ad8770 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt @@ -0,0 +1,18 @@ +Backward Hierarchy: +java.lang.Object -> Foo + +Backward References: +Foo in Foo occurrences = 1 +Foo.m(1) in Foo occurrences = 1 +java.lang.CharSequence in Foo occurrences = 1 +java.lang.String in Foo occurrences = 1 +java.lang.String.getBytes(0) in Foo occurrences = 1 + +Class Definitions: +Foo in Foo + +Members Signatures: + + +Type Casts: +java.lang.CharSequence <- java.lang.String \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/castDataArrays/Foo.java b/jps/jps-builders/testData/referencesIndex/castDataArrays/Foo.java new file mode 100644 index 000000000000..9f56446c850f --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castDataArrays/Foo.java @@ -0,0 +1,7 @@ +import java.util.*; + +class Foo { + void m(Object[] os) { + int length = ((String[]) os).length; + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/castDataArrays/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/castDataArrays/initialIndex.txt new file mode 100644 index 000000000000..4aa3fcc046fe --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castDataArrays/initialIndex.txt @@ -0,0 +1,14 @@ +Backward Hierarchy: +java.lang.Object -> Foo + +Backward References: +Array.length in Foo occurrences = 1 +Foo in Foo occurrences = 1 +Foo.m(1) in Foo occurrences = 1 +java.lang.Object in Foo occurrences = 1 +java.lang.String in Foo occurrences = 1 + +Class Definitions: +Foo in Foo + +Members Signatures: diff --git a/jps/jps-builders/testData/referencesIndex/castDataGenerics/Foo.java b/jps/jps-builders/testData/referencesIndex/castDataGenerics/Foo.java new file mode 100644 index 000000000000..c74ad7c3031a --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castDataGenerics/Foo.java @@ -0,0 +1,7 @@ +import java.util.*; + +class Foo { + void m(Collection c) { + ((List)c).get(1); + } +} \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt new file mode 100644 index 000000000000..e923c94a3fa5 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt @@ -0,0 +1,19 @@ +Backward Hierarchy: +java.lang.Object -> Foo + +Backward References: +Foo in Foo occurrences = 1 +Foo.m(1) in Foo occurrences = 1 +java.lang.String in Foo occurrences = 2 +java.util.Collection in Foo occurrences = 1 +java.util.List in Foo occurrences = 1 +java.util.List.get(1) in Foo occurrences = 1 + +Class Definitions: +Foo in Foo + +Members Signatures: + + +Type Casts: +java.util.Collection <- java.util.List \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt index 8840c7503136..f14ba9239ead 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -181,5 +181,17 @@ class ReferenceIndexTest : ReferenceIndexTestBase() { fun testDefaultConstructorUsage() { assertIndexOnRebuild("Foo.java") } + + fun testCastData() { + assertIndexOnRebuild("Foo.java") + } + + fun testCastDataArrays() { + assertIndexOnRebuild("Foo.java") + } + + fun testCastDataGenerics() { + assertIndexOnRebuild("Foo.java") + } } diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt index 742ae34b7423..39245dea5bc2 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -160,6 +160,25 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() { signs.sort() result.append(signs.joinToString(separator = "\n")) + val typeCasts = mutableListOf() + storage(index, CompilerIndices.BACK_CAST).processKeys {castType -> + val operands = mutableListOf() + val valueIt = index[CompilerIndices.BACK_CAST].getData(castType).valueIterator + while (valueIt.hasNext()) { + val nextRefs = valueIt.next() + nextRefs.mapTo(operands) { it.asText(nameEnumerator) } + } + if (!operands.isEmpty()) { + typeCasts.add(castType.asText(nameEnumerator) + " <- " + operands.joinToString(separator = " ")) + } + true + } + if (typeCasts.isNotEmpty()) { + result.append("\n\nType Casts:\n") + typeCasts.sort() + result.append(typeCasts.joinToString(separator = "\n")) + } + return result.toString() } finally { From f62e509001127d2da81dbd20b1971f814658f9af Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 11 Aug 2017 10:52:45 +0300 Subject: [PATCH 02/10] remove unused methods --- .../chainsSearch/MethodChainsSearchUtil.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainsSearchUtil.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainsSearchUtil.java index ae029a1702cf..42b54b329366 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainsSearchUtil.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainsSearchUtil.java @@ -21,37 +21,16 @@ import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiType; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.text.EditDistance; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Comparator; -import java.util.Set; import java.util.stream.Stream; public final class MethodChainsSearchUtil { - private final static int COMMON_PART_MIN_LENGTH = 3; - private MethodChainsSearchUtil() { } - public static boolean isSimilar(@NotNull String target, - @NotNull String candidate) { - return EditDistance.levenshtein(target, sanitizedToLowerCase(candidate), true) >= COMMON_PART_MIN_LENGTH; - } - - @NotNull - public static String sanitizedToLowerCase(@NotNull String name) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < name.length(); i++) { - char ch = name.charAt(i); - if (Character.isLetter(ch)) { - result.append(Character.toLowerCase(ch)); - } - } - return result.toString(); - } - @Nullable public static PsiMethod getMethodWithMinNotPrimitiveParameters(@NotNull PsiMethod[] methods, @NotNull PsiClass target) { From 842733f97b0e022ae5f950d727dc98f0058101e8 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 11 Aug 2017 10:53:25 +0300 Subject: [PATCH 03/10] CompilerReferenceService: try ro find suitable type cast according given threshold --- .../backwardRefs/CompilerReferenceReader.java | 19 ++++ .../CompilerReferenceServiceEx.java | 5 ++ .../CompilerReferenceServiceImpl.java | 48 ++++++++++ .../{MethodChain.java => CallChain.java} | 87 +++++++++++-------- .../compiler/chainsSearch/ChainOperation.java | 60 +++++++++++++ .../compiler/chainsSearch/ChainSearcher.java | 40 ++++----- .../MethodChainLookupRangingHelper.java | 31 ++++--- .../chainsSearch/SearchInitializer.java | 20 ++--- .../MethodChainCompletionContributor.java | 6 +- .../CastingLookupElementDecorator.java | 4 +- 10 files changed, 236 insertions(+), 84 deletions(-) rename java/compiler/impl/src/com/intellij/compiler/chainsSearch/{MethodChain.java => CallChain.java} (57%) create mode 100644 java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java index 9fb285c41ac8..1b6d39d653d9 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java @@ -30,6 +30,7 @@ import com.intellij.util.indexing.StorageException; import com.intellij.util.indexing.ValueContainer; import gnu.trove.THashSet; import gnu.trove.TIntHashSet; +import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex; @@ -178,6 +179,24 @@ class CompilerReferenceReader { return myIndex; } + TObjectIntHashMap getTypeCastsInside(@NotNull LightRef.LightClassHierarchyElementDef toType, @NotNull TIntHashSet ids) throws StorageException { + TObjectIntHashMap typeCastStats = new TObjectIntHashMap<>(); + myIndex.get(CompilerIndices.BACK_CAST).getData(toType).forEach(new ValueContainer.ContainerAction>() { + @Override + public boolean perform(int id, Collection value) { + if (!ids.contains(id)) return true; + for (LightRef ref : value) { + if (!typeCastStats.adjustValue(ref, 1)) { + typeCastStats.put(ref, 1); + } + } + return true; + } + }); + + return typeCastStats; + } + static boolean exists(Project project) { File buildDir = BuildManager.getInstance().getProjectSystemDirectory(project); if (buildDir == null || CompilerBackwardReferenceIndex.versionDiffers(buildDir)) { diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java index bf47948911c6..4cfc7c2173ee 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java @@ -19,6 +19,7 @@ import com.intellij.compiler.CompilerReferenceService; import com.intellij.compiler.chainsSearch.SignatureAndOccurrences; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.backwardRefs.LightRef; import org.jetbrains.jps.backwardRefs.SignatureData; @@ -37,6 +38,10 @@ public abstract class CompilerReferenceServiceEx extends CompilerReferenceServic @SignatureData.IteratorKind byte iteratorKind) throws ReferenceIndexUnavailableException; + @Nullable + public abstract LightRef.LightClassHierarchyElementDef mayCallOfTypeCast(@NotNull LightRef.JavaLightMethodRef method, int probabilityThreshold) + throws ReferenceIndexUnavailableException; + public abstract boolean mayHappen(@NotNull LightRef qualifier, @NotNull LightRef base, int probabilityThreshold) throws ReferenceIndexUnavailableException; diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index edce380b146c..09a91a30e325 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -59,6 +59,8 @@ import com.intellij.util.io.PersistentEnumeratorBase; import com.intellij.util.messages.MessageBusConnection; import gnu.trove.THashSet; import gnu.trove.TIntHashSet; +import gnu.trove.TObjectIntHashMap; +import gnu.trove.TObjectIntProcedure; import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -261,6 +263,52 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp } } + /** + * finds one best candidate to do a cast type before given method call (eg.: ((B) a).someMethod()). Follows given formula: + * + * #(files where method & type cast is occurred) / #(files where method is occurred) > 1 - 1 / probabilityThreshold + */ + @Nullable + @Override + public LightRef.LightClassHierarchyElementDef mayCallOfTypeCast(@NotNull LightRef.JavaLightMethodRef method, int probabilityThreshold) + throws ReferenceIndexUnavailableException { + try { + myReadDataLock.lock(); + try { + if (myReader == null) throw new ReferenceIndexUnavailableException(); + final TIntHashSet ids = myReader.getAllContainingFileIds(method); + + LightRef.LightClassHierarchyElementDef owner = method.getOwner(); + + TObjectIntHashMap typeCasts = myReader.getTypeCastsInside(owner, ids); + + LightRef[] best = {null}; + int[] bestFileCount = {0}; + + typeCasts.forEachEntry(new TObjectIntProcedure() { + @Override + public boolean execute(LightRef operandType, int matchedFileCount) { + if (ids.size() > probabilityThreshold * (matchedFileCount - ids.size())) { + if (best[0] == null || bestFileCount[0] < matchedFileCount) { + best[0] = operandType; + bestFileCount[0] = matchedFileCount; + } + } + return true; + } + }); + + return (LightRef.LightClassHierarchyElementDef)best[0]; + } + finally { + myReadDataLock.unlock(); + } + } catch (Exception e) { + onException(e, "conditional probability"); + return null; + } + } + /** * conditional probability P(ref1 | ref2) = P(ref1 * ref2) / P(ref2) > 1 - 1 / threshold * diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChain.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java similarity index 57% rename from java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChain.java rename to java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java index fbeeb34154a7..70871d9f5fba 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChain.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java @@ -16,29 +16,32 @@ package com.intellij.compiler.chainsSearch; import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; -import com.intellij.psi.*; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.Set; import java.util.stream.Collectors; -import static com.intellij.util.containers.ContainerUtil.newArrayList; -import static com.intellij.util.containers.ContainerUtil.reverse; -public class MethodChain { - private final List myRevertedPath; +public class CallChain { + @NotNull + private final ChainOperation[] myReverseOperations; private final MethodIncompleteSignature mySignature; private final int myWeight; private final PsiClass myQualifierClass; @Nullable - public static MethodChain create(@NotNull MethodIncompleteSignature signature, - int weight, - @NotNull ChainCompletionContext context) { + public static CallChain create(@NotNull MethodIncompleteSignature signature, + int weight, + @NotNull ChainCompletionContext context) { PsiClass qualifier = context.resolveQualifierClass(signature); if (qualifier == null || (!signature.isStatic() && InheritanceUtil.isInheritorOrSelf(context.getTarget().getTargetClass(), qualifier, true))) { return null; @@ -54,15 +57,15 @@ public class MethodChain { return null; } classes.add(contextClass); - return new MethodChain(qualifier, Collections.singletonList(methods), signature, weight); + return new CallChain(qualifier, new ChainOperation[] {new ChainOperation.MethodCall(methods)}, signature, weight); } - public MethodChain(@NotNull PsiClass qualifierClass, - @NotNull List revertedPath, - MethodIncompleteSignature signature, - int weight) { + public CallChain(@NotNull PsiClass qualifierClass, + @NotNull ChainOperation[] reverseOperations, + MethodIncompleteSignature signature, + int weight) { myQualifierClass = qualifierClass; - myRevertedPath = revertedPath; + myReverseOperations = reverseOperations; mySignature = signature; myWeight = weight; } @@ -73,7 +76,7 @@ public class MethodChain { } public int length() { - return myRevertedPath.size(); + return myReverseOperations.length; } public PsiClass getQualifierClass() { @@ -82,54 +85,57 @@ public class MethodChain { @NotNull public PsiMethod[] getFirst() { - return myRevertedPath.get(0); + return ((ChainOperation.MethodCall) myReverseOperations[0]).getCandidates(); } - public List getPath() { - return reverse(myRevertedPath); + public ChainOperation[] getPath() { + return ArrayUtil.reverseArray(myReverseOperations); } public int getChainWeight() { return myWeight; } - - public MethodChain continuation(@NotNull MethodIncompleteSignature signature, - int weight, - @NotNull ChainCompletionContext context) { - MethodChain head = create(signature, weight, context); + public CallChain continuation(@NotNull MethodIncompleteSignature signature, + int weight, + @NotNull ChainCompletionContext context) { + CallChain head = create(signature, weight, context); if (head == null) return null; - ArrayList newRevertedPath = newArrayList(); - newRevertedPath.addAll(myRevertedPath); - newRevertedPath.add(head.getPath().get(0)); - return new MethodChain(head.getQualifierClass(), newRevertedPath, head.getHeadSignature(), Math.min(weight, getChainWeight())); + ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; + System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); + newReverseOperations[length()] = head.getPath()[0]; + return new CallChain(head.getQualifierClass(), newReverseOperations, head.getHeadSignature(), Math.min(weight, getChainWeight())); } @Override public String toString() { - return myQualifierClass.getName() + "." + reverse(myRevertedPath).stream().map(methods -> methods[0].getName() + "()").collect(Collectors.joining(".")); + ChainOperation[] path = getPath(); + return Arrays.toString(path) + " on " + myQualifierClass.getName(); } @SuppressWarnings("ConstantConditions") - public static CompareResult compare(@NotNull MethodChain left, @NotNull MethodChain right) { + public static CompareResult compare(@NotNull CallChain left, @NotNull CallChain right) { if (left.length() == 0 || right.length() == 0) { throw new IllegalStateException("chains can't be empty"); } - Iterator leftIterator = left.myRevertedPath.iterator(); - Iterator rightIterator = right.myRevertedPath.iterator(); - while (leftIterator.hasNext() && rightIterator.hasNext()) { - PsiMethod[] thisNext = leftIterator.next(); - PsiMethod[] thatNext = rightIterator.next(); + int leftCurrentIdx = 0; + int rightCurrentIdx = 0; + + while (leftCurrentIdx < left.length() && rightCurrentIdx < right.length()) { + ChainOperation thisNext = left.myReverseOperations[leftCurrentIdx]; + ChainOperation thatNext = right.myReverseOperations[leftCurrentIdx]; if (!lookSimilar(thisNext, thatNext)) { return CompareResult.NOT_EQUAL; } + leftCurrentIdx++; + rightCurrentIdx++; } - if (leftIterator.hasNext() && !rightIterator.hasNext()) { + if (leftCurrentIdx < left.length() && rightCurrentIdx == right.length()) { return CompareResult.LEFT_CONTAINS_RIGHT; } - if (!leftIterator.hasNext() && rightIterator.hasNext()) { + if (leftCurrentIdx == left.length() && rightCurrentIdx < right.length()) { return CompareResult.RIGHT_CONTAINS_LEFT; } @@ -143,7 +149,12 @@ public class MethodChain { NOT_EQUAL } - static boolean lookSimilar(PsiMethod[] methods1, PsiMethod[] methods2) { + static boolean lookSimilar(ChainOperation op1, ChainOperation op2) { + if (op1 instanceof ChainOperation.TypeCast || op2 instanceof ChainOperation.TypeCast) return false; + + PsiMethod[] methods1 = ((ChainOperation.MethodCall)op1).getCandidates(); + PsiMethod[] methods2 = ((ChainOperation.MethodCall)op2).getCandidates(); + PsiMethod repr1 = methods1[0]; PsiMethod repr2 = methods2[0]; if (repr1.hasModifierProperty(PsiModifier.STATIC) || repr2.hasModifierProperty(PsiModifier.STATIC)) return false; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java new file mode 100644 index 000000000000..e939d0eea133 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 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.compiler.chainsSearch; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import org.jetbrains.annotations.NotNull; + +public interface ChainOperation { + class TypeCast implements ChainOperation { + // we cast only to a class + private final PsiClass myCastClass; + + public TypeCast(@NotNull PsiClass aClass) {myCastClass = aClass;} + + public PsiClass getCastClass() { + return myCastClass; + } + + @Override + public String toString() { + return "cast to " + myCastClass.getName(); + } + } + + class MethodCall implements ChainOperation { + @NotNull + private final PsiMethod[] myCandidates; + + public MethodCall(@NotNull PsiMethod[] candidates) { + if (candidates.length == 0) { + throw new IllegalStateException(); + } + myCandidates = candidates; + } + + @NotNull + public PsiMethod[] getCandidates() { + return myCandidates; + } + + @Override + public String toString() { + return myCandidates[0].getName() + "()"; + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index d2afd2168294..e849b2b36368 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -28,11 +28,11 @@ import java.util.*; public class ChainSearcher { @NotNull - public static List search(int pathMaximalLength, - ChainSearchTarget searchTarget, - int maxResultSize, - ChainCompletionContext context, - CompilerReferenceServiceEx compilerReferenceServiceEx) { + public static List search(int pathMaximalLength, + ChainSearchTarget searchTarget, + int maxResultSize, + ChainCompletionContext context, + CompilerReferenceServiceEx compilerReferenceServiceEx) { SearchInitializer initializer = createInitializer(searchTarget, compilerReferenceServiceEx, context); return search(compilerReferenceServiceEx, initializer, pathMaximalLength, maxResultSize, context); } @@ -55,18 +55,18 @@ public class ChainSearcher { } @NotNull - private static List search(CompilerReferenceServiceEx referenceServiceEx, - SearchInitializer initializer, - int chainMaxLength, - int maxResultSize, - ChainCompletionContext context) { - LinkedList q = initializer.getChainQueue(); + private static List search(CompilerReferenceServiceEx referenceServiceEx, + SearchInitializer initializer, + int chainMaxLength, + int maxResultSize, + ChainCompletionContext context) { + LinkedList q = initializer.getChainQueue(); - List result = new ArrayList<>(); + List result = new ArrayList<>(); while (!q.isEmpty()) { ProgressManager.checkCanceled(); - MethodChain currentChain = q.poll(); + CallChain currentChain = q.poll(); MethodIncompleteSignature headSignature = currentChain.getHeadSignature(); if (addChainIfTerminal(currentChain, result, chainMaxLength, context)) continue; @@ -81,7 +81,7 @@ public class ChainSearcher { MethodIncompleteSignature sign = candidate.getSignature(); if ((sign.isStatic() || !sign.getOwner().equals(context.getTarget().getClassQName())) && referenceServiceEx.mayHappen(candidate.getSignature().getRef(), headSignature.getRef(), ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD)) { - MethodChain continuation = currentChain.continuation(candidate.getSignature(), candidate.getOccurrenceCount(), context); + CallChain continuation = currentChain.continuation(candidate.getSignature(), candidate.getOccurrenceCount(), context); if (continuation != null) { boolean stopChain = candidate.getSignature().isStatic() || context.hasQualifier(context.resolveQualifierClass(candidate.getSignature())); if (stopChain) { @@ -109,8 +109,8 @@ public class ChainSearcher { /** * To reduce false-positives we add a method to result only if its qualifier can be occurred together with context variables. */ - private static void addChainIfQualifierCanBeOccurredInContext(MethodChain currentChain, - List result, + private static void addChainIfQualifierCanBeOccurredInContext(CallChain currentChain, + List result, ChainCompletionContext context, CompilerReferenceServiceEx referenceServiceEx) { if (!context.getTarget().getClassQName().equals(currentChain.getHeadSignature().getOwner())) { @@ -129,7 +129,7 @@ public class ChainSearcher { } } - private static boolean addChainIfTerminal(MethodChain currentChain, List result, int pathMaximalLength, + private static boolean addChainIfTerminal(CallChain currentChain, List result, int pathMaximalLength, ChainCompletionContext context) { if (currentChain.getHeadSignature().isStatic() || context.hasQualifier(context.resolveQualifierClass(currentChain.getHeadSignature())) || @@ -140,7 +140,7 @@ public class ChainSearcher { return false; } - private static void addChainIfNotPresent(MethodChain newChain, List result) { + private static void addChainIfNotPresent(CallChain newChain, List result) { if (result.isEmpty()) { result.add(newChain); return; @@ -148,8 +148,8 @@ public class ChainSearcher { boolean doAdd = true; IntStack indicesToRemove = new IntStack(); for (int i = 0; i < result.size(); i++) { - MethodChain chain = result.get(i); - MethodChain.CompareResult r = MethodChain.compare(chain, newChain); + CallChain chain = result.get(i); + CallChain.CompareResult r = CallChain.compare(chain, newChain); switch (r) { case LEFT_CONTAINS_RIGHT: indicesToRemove.push(i); diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java index fde155f88049..840e218b9261 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java @@ -16,6 +16,7 @@ package com.intellij.compiler.chainsSearch; import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.codeInsight.completion.CastingLookupElementDecorator; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.JavaChainLookupElement; import com.intellij.codeInsight.completion.JavaMethodCallElement; @@ -41,24 +42,32 @@ import java.util.stream.Collectors; public class MethodChainLookupRangingHelper { @NotNull - public static LookupElement toLookupElement(MethodChain chain, + public static LookupElement toLookupElement(CallChain chain, ChainCompletionContext context) { int unreachableParametersCount = 0; int matchedParametersInContext = 0; LookupElement chainLookupElement = null; - for (PsiMethod[] psiMethods : chain.getPath()) { - PsiMethod method = ObjectUtils.notNull(MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, context.getTarget().getTargetClass())); - Couple info = calculateParameterInfo(method, context); - unreachableParametersCount += info.getFirst(); - matchedParametersInContext += info.getSecond(); + for (ChainOperation op : chain.getPath()) { + if (op instanceof ChainOperation.MethodCall) { + PsiMethod method = ObjectUtils.notNull(MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(((ChainOperation.MethodCall)op).getCandidates(), + context.getTarget().getTargetClass())); + Couple info = calculateParameterInfo(method, context); + unreachableParametersCount += info.getFirst(); + matchedParametersInContext += info.getSecond(); - if (chainLookupElement == null) { - LookupElement qualifierLookupElement = createQualifierLookupElement(method, chain.getQualifierClass(), context); - LookupElement headLookupElement = createMethodLookupElement(method); - chainLookupElement = qualifierLookupElement == null ? headLookupElement : new JavaChainLookupElement(qualifierLookupElement, headLookupElement); + if (chainLookupElement == null) { + LookupElement qualifierLookupElement = createQualifierLookupElement(method, chain.getQualifierClass(), context); + LookupElement headLookupElement = createMethodLookupElement(method); + chainLookupElement = qualifierLookupElement == null ? headLookupElement : new JavaChainLookupElement(qualifierLookupElement, headLookupElement); + } else { + chainLookupElement = new JavaChainLookupElement(chainLookupElement, new JavaMethodCallElement(method)); + } } else { - chainLookupElement = new JavaChainLookupElement(chainLookupElement, new JavaMethodCallElement(method)); + if (chainLookupElement == null) throw new IllegalStateException(); + PsiClass castClass = ((ChainOperation.TypeCast)op).getCastClass(); + PsiClassType type = JavaPsiFacade.getElementFactory(castClass.getProject()).createType(castClass); + chainLookupElement = CastingLookupElementDecorator.createCastingElement(chainLookupElement, type); } } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java index 02bb2e51a938..c0670867f6fd 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java @@ -21,39 +21,39 @@ import java.util.*; public class SearchInitializer { private final ChainCompletionContext myContext; - private final LinkedList myQueue; - private final LinkedHashMap myChains; + private final LinkedList myQueue; + private final LinkedHashMap myChains; public SearchInitializer(SortedSet indexValues, ChainCompletionContext context) { myContext = context; int size = indexValues.size(); - List chains = new ArrayList<>(size); + List chains = new ArrayList<>(size); populateFrequentlyUsedMethod(indexValues, chains); myQueue = new LinkedList<>(); myChains = new LinkedHashMap<>(chains.size()); - for (MethodChain chain : chains) { + for (CallChain chain : chains) { MethodIncompleteSignature signature = chain.getHeadSignature(); myQueue.add(chain); myChains.put(signature, chain); } } - public LinkedList getChainQueue() { + public LinkedList getChainQueue() { return myQueue; } - public LinkedHashMap getChains() { + public LinkedHashMap getChains() { return myChains; } private void populateFrequentlyUsedMethod(SortedSet signatures, - List chains) { + List chains) { int bestOccurrences = -1; for (SignatureAndOccurrences indexValue : signatures) { - MethodChain methodChain = MethodChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext); - if (methodChain != null) { - chains.add(methodChain); + CallChain callChain = CallChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext); + if (callChain != null) { + chains.add(callChain); int occurrences = indexValue.getOccurrenceCount(); if (bestOccurrences == -1) { bestOccurrences = occurrences; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java index f97dfb21e8b9..95a4631cc877 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java @@ -22,7 +22,7 @@ import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx; import com.intellij.compiler.backwardRefs.ReferenceIndexUnavailableException; import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants; import com.intellij.compiler.chainsSearch.ChainSearcher; -import com.intellij.compiler.chainsSearch.MethodChain; +import com.intellij.compiler.chainsSearch.CallChain; import com.intellij.compiler.chainsSearch.MethodChainLookupRangingHelper; import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; import com.intellij.compiler.chainsSearch.context.ChainSearchTarget; @@ -92,13 +92,13 @@ public class MethodChainCompletionContributor extends CompletionContributor { private static List searchForLookups(ChainCompletionContext context) { CompilerReferenceServiceEx methodsUsageIndexReader = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(context.getProject()); ChainSearchTarget target = context.getTarget(); - List searchResult = + List searchResult = ChainSearcher.search(ChainSearchMagicConstants.MAX_CHAIN_SIZE, target, ChainSearchMagicConstants.MAX_SEARCH_RESULT_SIZE, context, methodsUsageIndexReader); - int maxWeight = searchResult.stream().mapToInt(MethodChain::getChainWeight).max().orElse(0); + int maxWeight = searchResult.stream().mapToInt(CallChain::getChainWeight).max().orElse(0); return searchResult .stream() diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java b/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java index fc17c3c6fa2e..a6f4abfca45b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -79,7 +79,7 @@ public class CastingLookupElementDecorator extends LookupElementDecorator Date: Fri, 8 Sep 2017 16:37:54 +0300 Subject: [PATCH 04/10] increment index version --- .../org/jetbrains/jps/backwardRefs/index/CompilerIndices.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java index 835903607a86..06908429ca47 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java @@ -37,7 +37,7 @@ import java.util.List; public class CompilerIndices { //TODO manage version separately - public final static int VERSION = 5; + public final static int VERSION = 6; public final static IndexId BACK_USAGES = IndexId.create("back.refs"); public final static IndexId> BACK_HIERARCHY = IndexId.create("back.hierarchy"); From 8e8f8f6d48098bbc9b812b08847abfb79316d5ad Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 11 Sep 2017 16:17:31 +0300 Subject: [PATCH 05/10] reverse cast index --- .../jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java | 2 +- .../testData/referencesIndex/castData/initialIndex.txt | 2 +- .../testData/referencesIndex/castDataGenerics/initialIndex.txt | 2 +- .../testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java index ad192ae54db4..22a70409f658 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java @@ -114,7 +114,7 @@ public class BackwardReferenceIndexUtil { if (enumeratedCastType == null) continue; LightRef enumeratedOperandType = writer.enumerateNames(cast.getOperandType(), name -> null); if (enumeratedOperandType == null) continue; - castMap.computeIfAbsent(enumeratedOperandType, t -> new SmartList<>()).add(enumeratedCastType); + castMap.computeIfAbsent(enumeratedCastType, t -> new SmartList<>()).add(enumeratedOperandType); } writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData)); diff --git a/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt index 0f58e4ad8770..026a7ce3f392 100644 --- a/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt +++ b/jps/jps-builders/testData/referencesIndex/castData/initialIndex.txt @@ -15,4 +15,4 @@ Members Signatures: Type Casts: -java.lang.CharSequence <- java.lang.String \ No newline at end of file +java.lang.String -> java.lang.CharSequence \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt index e923c94a3fa5..56806a7e8026 100644 --- a/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt +++ b/jps/jps-builders/testData/referencesIndex/castDataGenerics/initialIndex.txt @@ -16,4 +16,4 @@ Members Signatures: Type Casts: -java.util.Collection <- java.util.List \ No newline at end of file +java.util.List -> java.util.Collection \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt index 39245dea5bc2..56bb70ed6315 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt @@ -169,7 +169,7 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() { nextRefs.mapTo(operands) { it.asText(nameEnumerator) } } if (!operands.isEmpty()) { - typeCasts.add(castType.asText(nameEnumerator) + " <- " + operands.joinToString(separator = " ")) + typeCasts.add(castType.asText(nameEnumerator) + " -> " + operands.joinToString(separator = " ")) } true } From b7dac06526ed96fdb1c466356d1c263d11748fe4 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 11 Sep 2017 17:12:04 +0300 Subject: [PATCH 06/10] search for suitable type casts in relevant method chain completion; draft --- .../compiler/chainsSearch/CallChain.java | 19 +++++++-- .../compiler/chainsSearch/ChainOperation.java | 8 ++-- .../compiler/chainsSearch/ChainSearcher.java | 37 +++++++++++++---- .../chainsSearch/MethodIncompleteSignature.kt | 15 +++++-- .../chainsSearch/RefChainOperation.java | 24 +++++++++++ .../chainsSearch/SearchInitializer.java | 2 +- .../compiler/chainsSearch/TypeCast.kt | 40 +++++++++++++++++++ .../context/ChainCompletionContext.java | 27 ++++++++++--- 8 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java index 70871d9f5fba..ca0d762c17ab 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java @@ -34,7 +34,7 @@ import java.util.stream.Collectors; public class CallChain { @NotNull private final ChainOperation[] myReverseOperations; - private final MethodIncompleteSignature mySignature; + private final RefChainOperation mySignature; private final int myWeight; private final PsiClass myQualifierClass; @@ -42,7 +42,7 @@ public class CallChain { public static CallChain create(@NotNull MethodIncompleteSignature signature, int weight, @NotNull ChainCompletionContext context) { - PsiClass qualifier = context.resolveQualifierClass(signature); + PsiClass qualifier = context.resolvePsiClass(signature.getOwnerRef()); if (qualifier == null || (!signature.isStatic() && InheritanceUtil.isInheritorOrSelf(context.getTarget().getTargetClass(), qualifier, true))) { return null; } @@ -62,7 +62,7 @@ public class CallChain { public CallChain(@NotNull PsiClass qualifierClass, @NotNull ChainOperation[] reverseOperations, - MethodIncompleteSignature signature, + RefChainOperation signature, int weight) { myQualifierClass = qualifierClass; myReverseOperations = reverseOperations; @@ -71,7 +71,7 @@ public class CallChain { } @NotNull - public MethodIncompleteSignature getHeadSignature() { + public RefChainOperation getHeadSignature() { return mySignature; } @@ -108,6 +108,17 @@ public class CallChain { return new CallChain(head.getQualifierClass(), newReverseOperations, head.getHeadSignature(), Math.min(weight, getChainWeight())); } + public CallChain continuationWithCast(@NotNull TypeCast cast, + @NotNull ChainCompletionContext context) { + PsiClass operand = context.resolvePsiClass(cast.getOperandRef()); + if (operand == null) return null; + + ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; + System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); + newReverseOperations[length()] = new ChainOperation.TypeCast(operand); + return new CallChain(operand, newReverseOperations, cast, getChainWeight()); + } + @Override public String toString() { ChainOperation[] path = getPath(); diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java index e939d0eea133..16db37bc423e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java @@ -22,17 +22,17 @@ import org.jetbrains.annotations.NotNull; public interface ChainOperation { class TypeCast implements ChainOperation { // we cast only to a class - private final PsiClass myCastClass; + private final PsiClass myOperandClass; - public TypeCast(@NotNull PsiClass aClass) {myCastClass = aClass;} + public TypeCast(@NotNull PsiClass aClass) {myOperandClass= aClass;} public PsiClass getCastClass() { - return myCastClass; + return myOperandClass; } @Override public String toString() { - return "cast to " + myCastClass.getName(); + return "cast of " + myOperandClass.getName(); } } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index e849b2b36368..09c764ddd6ed 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -67,23 +67,28 @@ public class ChainSearcher { ProgressManager.checkCanceled(); CallChain currentChain = q.poll(); - MethodIncompleteSignature headSignature = currentChain.getHeadSignature(); + RefChainOperation headSignature = currentChain.getHeadSignature(); if (addChainIfTerminal(currentChain, result, chainMaxLength, context)) continue; // otherwise try to find chain continuation boolean updated = false; - SortedSet candidates = referenceServiceEx.findMethodReferenceOccurrences(headSignature.getOwner(), SignatureData.ZERO_DIM); + SortedSet candidates = referenceServiceEx.findMethodReferenceOccurrences(headSignature.getOwner1(), SignatureData.ZERO_DIM); + LightRef ref1 = headSignature.getRef1(); for (SignatureAndOccurrences candidate : candidates) { if (candidate.getOccurrenceCount() * ChainSearchMagicConstants.FILTER_RATIO < currentChain.getChainWeight()) { break; } MethodIncompleteSignature sign = candidate.getSignature(); if ((sign.isStatic() || !sign.getOwner().equals(context.getTarget().getClassQName())) && - referenceServiceEx.mayHappen(candidate.getSignature().getRef(), headSignature.getRef(), ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD)) { + (!(ref1 instanceof LightRef.JavaLightMethodRef) || + referenceServiceEx + .mayHappen(candidate.getSignature().getRef(), ref1, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD))) { + CallChain continuation = currentChain.continuation(candidate.getSignature(), candidate.getOccurrenceCount(), context); if (continuation != null) { - boolean stopChain = candidate.getSignature().isStatic() || context.hasQualifier(context.resolveQualifierClass(candidate.getSignature())); + boolean stopChain = + candidate.getSignature().isStatic() || context.hasQualifier(context.resolvePsiClass(candidate.getSignature().getOwnerRef())); if (stopChain) { addChainIfNotPresent(continuation, result); } @@ -95,6 +100,18 @@ public class ChainSearcher { } } + if (ref1 instanceof LightRef.JavaLightMethodRef) { + LightRef.LightClassHierarchyElementDef def = + referenceServiceEx.mayCallOfTypeCast((LightRef.JavaLightMethodRef)ref1, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD); + if (def != null) { + CallChain continuation = currentChain.continuationWithCast(new TypeCast(def, ((MethodIncompleteSignature)headSignature).getOwnerRef(), referenceServiceEx, 0), context); + if (continuation != null) { + q.addFirst(continuation); + updated = true; + } + } + } + if (!updated) { addChainIfQualifierCanBeOccurredInContext(currentChain, result, context, referenceServiceEx); } @@ -113,11 +130,13 @@ public class ChainSearcher { List result, ChainCompletionContext context, CompilerReferenceServiceEx referenceServiceEx) { - if (!context.getTarget().getClassQName().equals(currentChain.getHeadSignature().getOwner())) { + RefChainOperation signature = currentChain.getHeadSignature(); + if (!(signature instanceof MethodIncompleteSignature)) return; + if (!context.getTarget().getClassQName().equals(((MethodIncompleteSignature)signature).getOwner())) { Set references = context.getContextClassReferences(); boolean isRelevantQualifier = false; for (LightRef ref: references) { - if (referenceServiceEx.mayHappen(currentChain.getHeadSignature().getOwnerRef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) { + if (referenceServiceEx.mayHappen(((MethodIncompleteSignature)signature).getOwnerRef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) { isRelevantQualifier = true; break; } @@ -131,8 +150,10 @@ public class ChainSearcher { private static boolean addChainIfTerminal(CallChain currentChain, List result, int pathMaximalLength, ChainCompletionContext context) { - if (currentChain.getHeadSignature().isStatic() || - context.hasQualifier(context.resolveQualifierClass(currentChain.getHeadSignature())) || + RefChainOperation signature = currentChain.getHeadSignature(); + if (!(signature instanceof MethodIncompleteSignature)) return false; + if (((MethodIncompleteSignature)signature).isStatic() || + context.hasQualifier(context.resolvePsiClass(((MethodIncompleteSignature)signature).getOwnerRef())) || currentChain.length() >= pathMaximalLength) { addChainIfNotPresent(currentChain, result); return true; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt index d976f7a1aae2..a8a56dccd0a8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt @@ -27,7 +27,15 @@ import java.util.function.Predicate class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, private val signatureData: SignatureData, - private val refService: CompilerReferenceServiceEx) { + private val refService: CompilerReferenceServiceEx): RefChainOperation { + override fun getOwner1(): String { + return owner + } + + override fun getRef1(): LightRef { + return ref + } + companion object { val CONSTRUCTOR_METHOD_NAME = "" } @@ -42,16 +50,17 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, refService.getName(ref.owner.name) } - val rawReturnType: String by lazy(LazyThreadSafetyMode.NONE) { + private val rawReturnType: String by lazy(LazyThreadSafetyMode.NONE) { refService.getName(signatureData.rawReturnType) } - val parameterCount: Int + private val parameterCount: Int get() = ref.parameterCount val isStatic: Boolean get() = signatureData.isStatic + //TODO remove fun resolveQualifier(project: Project, resolveScope: GlobalSearchScope, accessValidator: Predicate): PsiClass? { diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java new file mode 100644 index 000000000000..4338ac3d3579 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2017 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.compiler.chainsSearch; + +import org.jetbrains.jps.backwardRefs.LightRef; + +public interface RefChainOperation { + String getOwner1(); + + LightRef getRef1(); +} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java index c0670867f6fd..8c074b7f5c5f 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java @@ -33,7 +33,7 @@ public class SearchInitializer { myQueue = new LinkedList<>(); myChains = new LinkedHashMap<>(chains.size()); for (CallChain chain : chains) { - MethodIncompleteSignature signature = chain.getHeadSignature(); + MethodIncompleteSignature signature = (MethodIncompleteSignature)chain.getHeadSignature(); myQueue.add(chain); myChains.put(signature, chain); } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt new file mode 100644 index 000000000000..bd3709623b1c --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 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.compiler.chainsSearch + +import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx +import org.jetbrains.jps.backwardRefs.LightRef + +class TypeCast(val operandRef: LightRef.LightClassHierarchyElementDef, + val castTypeRef: LightRef.LightClassHierarchyElementDef, + refService: CompilerReferenceServiceEx, + val occurrences: Int): RefChainOperation { + override fun getOwner1(): String { + return operandName + } + + override fun getRef1(): LightRef { + return operandRef + } + + val operandName: String by lazy(LazyThreadSafetyMode.NONE) { + refService.getName(operandRef.name) + } + + val castTypeName: String by lazy(LazyThreadSafetyMode.NONE) { + refService.getName(castTypeRef.name) + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java index 43b66f10df3a..b480da460e2d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java @@ -34,6 +34,7 @@ import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FactoryMap; import gnu.trove.THashSet; +import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.backwardRefs.LightRef; @@ -65,22 +66,23 @@ public class ChainCompletionContext { @NotNull private final PsiResolveHelper myResolveHelper; @NotNull - private final Map myQualifierClassResolver; + private final TIntObjectHashMap myQualifierClassResolver; @NotNull private final Map myResolver; + @NotNull + private final CompilerReferenceServiceEx myRefServiceEx; private final NotNullLazyValue> myContextClassReferences = new NotNullLazyValue>() { @NotNull @Override protected Set compute() { - CompilerReferenceServiceEx referenceServiceEx = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(myProject); return getContextTypes() .stream() .map(PsiUtil::resolveClassInType) .filter(Objects::nonNull) .map(c -> ClassUtil.getJVMClassName(c)) .filter(Objects::nonNull) - .mapToInt(c -> referenceServiceEx.getNameId(c)) + .mapToInt(c -> myRefServiceEx.getNameId(c)) .filter(n -> n != 0) .mapToObj(n -> new LightRef.JavaLightClassRef(n)).collect(Collectors.toSet()); } @@ -95,8 +97,9 @@ public class ChainCompletionContext { myResolveScope = context.getResolveScope(); myProject = context.getProject(); myResolveHelper = PsiResolveHelper.SERVICE.getInstance(myProject); - myQualifierClassResolver = FactoryMap.createMap(sign -> sign.resolveQualifier(myProject, myResolveScope, accessValidator())); + myQualifierClassResolver = new TIntObjectHashMap<>(); myResolver = FactoryMap.createMap(sign -> sign.resolve(myProject, myResolveScope, accessValidator())); + myRefServiceEx = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(myProject); } @NotNull @@ -162,8 +165,20 @@ public class ChainCompletionContext { } @Nullable - public PsiClass resolveQualifierClass(MethodIncompleteSignature sign) { - return myQualifierClassResolver.get(sign); + public PsiClass resolvePsiClass(LightRef.LightClassHierarchyElementDef aClass) { + int nameId = aClass.getName(); + if (myQualifierClassResolver.contains(nameId)) { + return myQualifierClassResolver.get(nameId); + } else { + PsiClass psiClass = null; + String name = myRefServiceEx.getName(nameId); + PsiClass resolvedClass = JavaPsiFacade.getInstance(getProject()).findClass(name, myResolveScope); + if (resolvedClass != null && accessValidator().test(resolvedClass)) { + psiClass = resolvedClass; + } + myQualifierClassResolver.put(nameId, psiClass); + return psiClass; + } } @NotNull From 9a0734c88bb57d1c8cf9ca0aa97f9f186e55364e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 11 Sep 2017 17:23:33 +0300 Subject: [PATCH 07/10] do not create very complex chains with casts --- .../src/com/intellij/compiler/chainsSearch/CallChain.java | 4 ++++ .../src/com/intellij/compiler/chainsSearch/ChainSearcher.java | 1 + 2 files changed, 5 insertions(+) diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java index ca0d762c17ab..827656fb6d6e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java @@ -70,6 +70,10 @@ public class CallChain { myWeight = weight; } + public boolean hasCast() { + return Arrays.stream(myReverseOperations).anyMatch(op -> op instanceof ChainOperation.TypeCast); + } + @NotNull public RefChainOperation getHeadSignature() { return mySignature; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index 09c764ddd6ed..9229e5bf3458 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -132,6 +132,7 @@ public class ChainSearcher { CompilerReferenceServiceEx referenceServiceEx) { RefChainOperation signature = currentChain.getHeadSignature(); if (!(signature instanceof MethodIncompleteSignature)) return; + if (currentChain.hasCast()) return; if (!context.getTarget().getClassQName().equals(((MethodIncompleteSignature)signature).getOwner())) { Set references = context.getContextClassReferences(); boolean isRelevantQualifier = false; From a46a3263cb1c89a37016e8d48195da06249c0c4a Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 12 Sep 2017 15:54:31 +0300 Subject: [PATCH 08/10] casts in relevant method chain search: fix cast type, fix suitable cast search --- .../CompilerReferenceServiceImpl.java | 2 +- .../compiler/chainsSearch/CallChain.java | 18 +++++++++++++----- .../compiler/chainsSearch/ChainOperation.java | 8 ++++++-- .../compiler/chainsSearch/ChainSearcher.java | 3 +-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index 09a91a30e325..e768b8cc5b49 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -288,7 +288,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp typeCasts.forEachEntry(new TObjectIntProcedure() { @Override public boolean execute(LightRef operandType, int matchedFileCount) { - if (ids.size() > probabilityThreshold * (matchedFileCount - ids.size())) { + if (ids.size() > probabilityThreshold * (ids.size() - matchedFileCount)) { if (best[0] == null || bestFileCount[0] < matchedFileCount) { best[0] = operandType; bestFileCount[0] = matchedFileCount; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java index 827656fb6d6e..5a6ed859d099 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/CallChain.java @@ -35,6 +35,7 @@ public class CallChain { @NotNull private final ChainOperation[] myReverseOperations; private final RefChainOperation mySignature; + private final MethodIncompleteSignature myLastMethodSign; private final int myWeight; private final PsiClass myQualifierClass; @@ -57,16 +58,18 @@ public class CallChain { return null; } classes.add(contextClass); - return new CallChain(qualifier, new ChainOperation[] {new ChainOperation.MethodCall(methods)}, signature, weight); + return new CallChain(qualifier, new ChainOperation[] {new ChainOperation.MethodCall(methods)}, signature, signature, weight); } public CallChain(@NotNull PsiClass qualifierClass, @NotNull ChainOperation[] reverseOperations, RefChainOperation signature, + MethodIncompleteSignature lastMethodSign, int weight) { myQualifierClass = qualifierClass; myReverseOperations = reverseOperations; mySignature = signature; + myLastMethodSign = lastMethodSign; myWeight = weight; } @@ -74,6 +77,10 @@ public class CallChain { return Arrays.stream(myReverseOperations).anyMatch(op -> op instanceof ChainOperation.TypeCast); } + public MethodIncompleteSignature getLastMethodSign() { + return myLastMethodSign; + } + @NotNull public RefChainOperation getHeadSignature() { return mySignature; @@ -109,18 +116,19 @@ public class CallChain { ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); newReverseOperations[length()] = head.getPath()[0]; - return new CallChain(head.getQualifierClass(), newReverseOperations, head.getHeadSignature(), Math.min(weight, getChainWeight())); + return new CallChain(head.getQualifierClass(), newReverseOperations, head.getHeadSignature(), signature, Math.min(weight, getChainWeight())); } public CallChain continuationWithCast(@NotNull TypeCast cast, @NotNull ChainCompletionContext context) { PsiClass operand = context.resolvePsiClass(cast.getOperandRef()); - if (operand == null) return null; + PsiClass castType = context.resolvePsiClass(cast.getCastTypeRef()); + if (operand == null || castType == null) return null; ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); - newReverseOperations[length()] = new ChainOperation.TypeCast(operand); - return new CallChain(operand, newReverseOperations, cast, getChainWeight()); + newReverseOperations[length()] = new ChainOperation.TypeCast(operand, castType); + return new CallChain(operand, newReverseOperations, cast, myLastMethodSign, getChainWeight()); } @Override diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java index 16db37bc423e..97cc33468e36 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainOperation.java @@ -23,11 +23,15 @@ public interface ChainOperation { class TypeCast implements ChainOperation { // we cast only to a class private final PsiClass myOperandClass; + @NotNull private final PsiClass myCastClass; - public TypeCast(@NotNull PsiClass aClass) {myOperandClass= aClass;} + public TypeCast(@NotNull PsiClass operandClass, @NotNull PsiClass castClass) { + myOperandClass= operandClass; + myCastClass = castClass; + } public PsiClass getCastClass() { - return myOperandClass; + return myCastClass; } @Override diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index 9229e5bf3458..70940eced821 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -130,8 +130,7 @@ public class ChainSearcher { List result, ChainCompletionContext context, CompilerReferenceServiceEx referenceServiceEx) { - RefChainOperation signature = currentChain.getHeadSignature(); - if (!(signature instanceof MethodIncompleteSignature)) return; + RefChainOperation signature = currentChain.getLastMethodSign(); if (currentChain.hasCast()) return; if (!context.getTarget().getClassQName().equals(((MethodIncompleteSignature)signature).getOwner())) { Set references = context.getContextClassReferences(); From eb9fd79648b2aaaf71849796b6d5ff4ac5142ad0 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 12 Sep 2017 16:53:38 +0300 Subject: [PATCH 09/10] add tests for completion with casts --- .../compiler/chainsSearch/ChainSearcher.java | 6 ++--- .../MethodChainLookupRangingHelper.java | 13 +++++----- .../chainsSearch/MethodIncompleteSignature.kt | 4 ++++ .../chainsSearch/RefChainOperation.java | 1 + .../compiler/chainsSearch/TypeCast.kt | 4 ++++ .../TestCompletion.java | 11 +++++++++ .../TestIndex.java | 19 +++++++++++++++ .../TestCompletion.java | 15 ++++++++++++ .../TestIndex.java | 24 +++++++++++++++++++ .../TestCompletion.java | 11 +++++++++ .../TestIndex.java | 19 +++++++++++++++ .../MethodChainsCompletionTest.java | 17 +++++++++++++ 12 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestCompletion.java create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestIndex.java create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestCompletion.java create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestIndex.java create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestCompletion.java create mode 100644 java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestIndex.java diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index 70940eced821..cb9c793b9667 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -150,10 +150,10 @@ public class ChainSearcher { private static boolean addChainIfTerminal(CallChain currentChain, List result, int pathMaximalLength, ChainCompletionContext context) { - RefChainOperation signature = currentChain.getHeadSignature(); - if (!(signature instanceof MethodIncompleteSignature)) return false; + RefChainOperation signature = currentChain.getLastMethodSign(); + RefChainOperation head = currentChain.getHeadSignature(); if (((MethodIncompleteSignature)signature).isStatic() || - context.hasQualifier(context.resolvePsiClass(((MethodIncompleteSignature)signature).getOwnerRef())) || + context.hasQualifier(context.resolvePsiClass(head.getOwnerRef1())) || currentChain.length() >= pathMaximalLength) { addChainIfNotPresent(currentChain, result); return true; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java index 840e218b9261..a23e3246b0f3 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java @@ -35,7 +35,6 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.stream.Collectors; @@ -57,14 +56,16 @@ public class MethodChainLookupRangingHelper { matchedParametersInContext += info.getSecond(); if (chainLookupElement == null) { - LookupElement qualifierLookupElement = createQualifierLookupElement(method, chain.getQualifierClass(), context); + LookupElement qualifierLookupElement = method.hasModifierProperty(PsiModifier.STATIC) ? null : createQualifierLookupElement(chain.getQualifierClass(), context); LookupElement headLookupElement = createMethodLookupElement(method); chainLookupElement = qualifierLookupElement == null ? headLookupElement : new JavaChainLookupElement(qualifierLookupElement, headLookupElement); } else { chainLookupElement = new JavaChainLookupElement(chainLookupElement, new JavaMethodCallElement(method)); } } else { - if (chainLookupElement == null) throw new IllegalStateException(); + if (chainLookupElement == null) { + chainLookupElement = createQualifierLookupElement(chain.getQualifierClass(), context); + } PsiClass castClass = ((ChainOperation.TypeCast)op).getCastClass(); PsiClassType type = JavaPsiFacade.getElementFactory(castClass.getProject()).createType(castClass); chainLookupElement = CastingLookupElementDecorator.createCastingElement(chainLookupElement, type); @@ -109,9 +110,9 @@ public class MethodChainLookupRangingHelper { }; } - @Nullable - private static LookupElement createQualifierLookupElement(@NotNull PsiMethod method, @NotNull PsiClass qualifierClass, @NotNull ChainCompletionContext context) { - if (method.hasModifierProperty(PsiModifier.STATIC)) return null; + @NotNull + private static LookupElement createQualifierLookupElement(@NotNull PsiClass qualifierClass, + @NotNull ChainCompletionContext context) { PsiNamedElement element = context.getQualifiers(qualifierClass).findFirst().orElse(null); if (element == null) { return new ChainCompletionNewVariableLookupElement(qualifierClass, context); diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt index a8a56dccd0a8..14762a99f1ef 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt @@ -28,6 +28,10 @@ import java.util.function.Predicate class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, private val signatureData: SignatureData, private val refService: CompilerReferenceServiceEx): RefChainOperation { + override fun getOwnerRef1(): LightRef.LightClassHierarchyElementDef { + return ownerRef + } + override fun getOwner1(): String { return owner } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java index 4338ac3d3579..2f9b381b9f77 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java @@ -19,6 +19,7 @@ import org.jetbrains.jps.backwardRefs.LightRef; public interface RefChainOperation { String getOwner1(); + LightRef.LightClassHierarchyElementDef getOwnerRef1(); LightRef getRef1(); } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt index bd3709623b1c..9fa09601eeae 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt @@ -22,6 +22,10 @@ class TypeCast(val operandRef: LightRef.LightClassHierarchyElementDef, val castTypeRef: LightRef.LightClassHierarchyElementDef, refService: CompilerReferenceServiceEx, val occurrences: Int): RefChainOperation { + override fun getOwnerRef1(): LightRef.LightClassHierarchyElementDef { + return operandRef + } + override fun getOwner1(): String { return operandName } diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestCompletion.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestCompletion.java new file mode 100644 index 000000000000..bea43a5d5b31 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestCompletion.java @@ -0,0 +1,11 @@ +interface Editor {} +interface EditorEx extends Editor { + MarkupModelEx getMarkupModel(); +} +interface MarkupModelEx {} + +class Test { + void m(Editor editor) { + MarkupModelEx m = + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestIndex.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestIndex.java new file mode 100644 index 000000000000..9e02d9826597 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnContextVariable/TestIndex.java @@ -0,0 +1,19 @@ +interface Editor {} +interface EditorEx extends Editor { + MarkupModelEx getMarkupModel(); +} +interface MarkupModelEx {} + +class Test { + void m(Editor editor) { + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestCompletion.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestCompletion.java new file mode 100644 index 000000000000..762e124f58e4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestCompletion.java @@ -0,0 +1,15 @@ +interface InspectionManager { + static InspectionManager getInstance() { + return null; + } +} +interface InspectionManagerEx extends InspectionManager { + GlobalInspectionContext createContext(); +} +interface GlobalInspectionContext {} + +class Test { + void m() { + GlobalInspectionContext c = + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestIndex.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestIndex.java new file mode 100644 index 000000000000..81fd6179e38c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnStaticMethod/TestIndex.java @@ -0,0 +1,24 @@ +interface InspectionManager { + static InspectionManager getInstance() { + return null; + } +} +interface InspectionManagerEx extends InspectionManager { + GlobalInspectionContext createContext(); +} +interface GlobalInspectionContext {} + +class Test { + void m() { + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + ((InspectionManagerEx)InspectionManager.getInstance()).createContext(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestCompletion.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestCompletion.java new file mode 100644 index 000000000000..ffbe7a3383b8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestCompletion.java @@ -0,0 +1,11 @@ +interface Editor {} +interface EditorEx extends Editor { + MarkupModelEx getMarkupModel(); +} +interface MarkupModelEx {} + +class Test { + void m() { + MarkupModelEx m = + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestIndex.java b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestIndex.java new file mode 100644 index 000000000000..9e02d9826597 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testChainWithCastOnVariableOutsideContext/TestIndex.java @@ -0,0 +1,19 @@ +interface Editor {} +interface EditorEx extends Editor { + MarkupModelEx getMarkupModel(); +} +interface MarkupModelEx {} + +class Test { + void m(Editor editor) { + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + ((EditorEx)editor).getMarkupModel(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java index bd4b99d71002..97e8bc884a2f 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java @@ -23,7 +23,9 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.compiler.chainsSearch.ChainRelevance; import com.intellij.compiler.chainsSearch.completion.MethodChainCompletionContributor; import com.intellij.compiler.chainsSearch.completion.lookup.JavaRelevantChainLookupElement; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.registry.Registry; +import com.intellij.pom.java.LanguageLevel; import com.intellij.testFramework.SkipSlowTestLocally; import com.intellij.util.SmartList; @@ -44,6 +46,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest { Registry.get(MethodChainCompletionContributor.REGISTRY_KEY).setValue(true, myFixture.getTestRootDisposable()); myDefaultAutoCompleteOnCodeCompletion = CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_CODE_COMPLETION; CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = false; + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8); } @Override @@ -240,6 +243,20 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest { assertEquals("psiElement.getProject", element.getLookupString()); } + public void testChainWithCastOnContextVariable() { + JavaRelevantChainLookupElement element = assertOneElement(doCompletion()); + assertEquals("(EditorEx)editor.getMarkupModel", element.toString()); + } + + public void testChainWithCastOnVariableOutsideContext() { + assertEmpty(doCompletion()); + } + + public void testChainWithCastOnStaticMethod() { + JavaRelevantChainLookupElement element = assertOneElement(doCompletion()); + assertEquals("(InspectionManagerEx)getInstance().createContext", element.toString()); + } + public void assertAdvisorLookupElementEquals(String lookupText, int unreachableParametersCount, int chainSize, From 614810f8cd65710598dec6c5ffe8189a3ee1fb6b Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 15 Sep 2017 12:12:38 +0300 Subject: [PATCH 10/10] type casts in relevant method chain completion: various renames --- .../backwardRefs/CompilerReferenceReader.java | 10 +- .../CompilerReferenceServiceEx.java | 8 +- .../CompilerReferenceServiceImpl.java | 18 +-- .../compiler/chainsSearch/ChainSearcher.java | 93 ++++++++------- .../MethodChainLookupRangingHelper.java | 2 +- ...nces.java => MethodRefAndOccurrences.java} | 10 +- .../{CallChain.java => OperationChain.java} | 61 +++++----- .../chainsSearch/RefChainOperation.java | 25 ---- ...pleteSignature.kt => RefChainOperation.kt} | 107 +++++++++--------- .../chainsSearch/SearchInitializer.java | 28 ++--- .../compiler/chainsSearch/TypeCast.kt | 44 ------- .../MethodChainCompletionContributor.java | 6 +- .../context/ChainCompletionContext.java | 15 ++- 13 files changed, 184 insertions(+), 243 deletions(-) rename java/compiler/impl/src/com/intellij/compiler/chainsSearch/{SignatureAndOccurrences.java => MethodRefAndOccurrences.java} (76%) rename java/compiler/impl/src/com/intellij/compiler/chainsSearch/{CallChain.java => OperationChain.java} (76%) delete mode 100644 java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java rename java/compiler/impl/src/com/intellij/compiler/chainsSearch/{MethodIncompleteSignature.kt => RefChainOperation.kt} (52%) delete mode 100644 java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java index 1b6d39d653d9..79650074d237 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java @@ -179,13 +179,13 @@ class CompilerReferenceReader { return myIndex; } - TObjectIntHashMap getTypeCastsInside(@NotNull LightRef.LightClassHierarchyElementDef toType, @NotNull TIntHashSet ids) throws StorageException { + TObjectIntHashMap getTypeCasts(@NotNull LightRef.LightClassHierarchyElementDef castType, @NotNull TIntHashSet fileIds) throws StorageException { TObjectIntHashMap typeCastStats = new TObjectIntHashMap<>(); - myIndex.get(CompilerIndices.BACK_CAST).getData(toType).forEach(new ValueContainer.ContainerAction>() { + myIndex.get(CompilerIndices.BACK_CAST).getData(castType).forEach(new ValueContainer.ContainerAction>() { @Override - public boolean perform(int id, Collection value) { - if (!ids.contains(id)) return true; - for (LightRef ref : value) { + public boolean perform(int id, Collection values) { + if (!fileIds.contains(id)) return true; + for (LightRef ref : values) { if (!typeCastStats.adjustValue(ref, 1)) { typeCastStats.put(ref, 1); } diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java index 4cfc7c2173ee..b69e08d824e1 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceEx.java @@ -16,7 +16,8 @@ package com.intellij.compiler.backwardRefs; import com.intellij.compiler.CompilerReferenceService; -import com.intellij.compiler.chainsSearch.SignatureAndOccurrences; +import com.intellij.compiler.chainsSearch.MethodRefAndOccurrences; +import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,8 +35,9 @@ public abstract class CompilerReferenceServiceEx extends CompilerReferenceServic } @NotNull - public abstract SortedSet findMethodReferenceOccurrences(@NotNull String rawReturnType, - @SignatureData.IteratorKind byte iteratorKind) + public abstract SortedSet findMethodReferenceOccurrences(@NotNull String rawReturnType, + @SignatureData.IteratorKind byte iteratorKind, + @NotNull ChainCompletionContext context) throws ReferenceIndexUnavailableException; @Nullable diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index e768b8cc5b49..16a6e7961459 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -20,8 +20,9 @@ import com.intellij.compiler.backwardRefs.view.CompilerReferenceFindUsagesTestIn import com.intellij.compiler.backwardRefs.view.CompilerReferenceHierarchyTestInfo; import com.intellij.compiler.backwardRefs.view.DirtyScopeTestInfo; import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants; -import com.intellij.compiler.chainsSearch.MethodIncompleteSignature; -import com.intellij.compiler.chainsSearch.SignatureAndOccurrences; +import com.intellij.compiler.chainsSearch.MethodCall; +import com.intellij.compiler.chainsSearch.MethodRefAndOccurrences; +import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; import com.intellij.compiler.server.BuildManager; import com.intellij.compiler.server.BuildManagerListener; import com.intellij.lang.injection.InjectedLanguageManager; @@ -222,8 +223,9 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp @NotNull @Override - public SortedSet findMethodReferenceOccurrences(@NotNull String rawReturnType, - @SignatureData.IteratorKind byte iteratorKind) { + public SortedSet findMethodReferenceOccurrences(@NotNull String rawReturnType, + @SignatureData.IteratorKind byte iteratorKind, + @NotNull ChainCompletionContext context) { try { myReadDataLock.lock(); try { @@ -242,15 +244,15 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp .distinct() .map(r -> { int count = myReader.getOccurrenceCount(r); - return count <= 1 ? null : new SignatureAndOccurrences( - new MethodIncompleteSignature((LightRef.JavaLightMethodRef)r, sd, this), + return count <= 1 ? null : new MethodRefAndOccurrences( + new MethodCall((LightRef.JavaLightMethodRef)r, sd, context), count); })) .filter(Objects::nonNull) .collect(Collectors.groupingBy(x -> x.getSignature(), Collectors.summarizingInt(x -> x.getOccurrenceCount()))) .entrySet() .stream() - .map(e -> new SignatureAndOccurrences(e.getKey(), (int)e.getValue().getSum())) + .map(e -> new MethodRefAndOccurrences(e.getKey(), (int)e.getValue().getSum())) .collect(Collectors.toCollection(TreeSet::new)); } finally { @@ -280,7 +282,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp LightRef.LightClassHierarchyElementDef owner = method.getOwner(); - TObjectIntHashMap typeCasts = myReader.getTypeCastsInside(owner, ids); + TObjectIntHashMap typeCasts = myReader.getTypeCasts(owner, ids); LightRef[] best = {null}; int[] bestFileCount = {0}; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index cb9c793b9667..9163bb2716d6 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -28,11 +28,11 @@ import java.util.*; public class ChainSearcher { @NotNull - public static List search(int pathMaximalLength, - ChainSearchTarget searchTarget, - int maxResultSize, - ChainCompletionContext context, - CompilerReferenceServiceEx compilerReferenceServiceEx) { + public static List search(int pathMaximalLength, + ChainSearchTarget searchTarget, + int maxResultSize, + ChainCompletionContext context, + CompilerReferenceServiceEx compilerReferenceServiceEx) { SearchInitializer initializer = createInitializer(searchTarget, compilerReferenceServiceEx, context); return search(compilerReferenceServiceEx, initializer, pathMaximalLength, maxResultSize, context); } @@ -41,54 +41,49 @@ public class ChainSearcher { private static SearchInitializer createInitializer(ChainSearchTarget target, CompilerReferenceServiceEx referenceServiceEx, ChainCompletionContext context) { - SortedSet methods = null; + SortedSet methods = Collections.emptySortedSet(); for (byte kind : target.getArrayKind()) { - SortedSet currentMethods = - referenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), kind); - if (methods == null) { - methods = currentMethods; - } else { - methods = unionSortedSet(currentMethods, methods); - } + SortedSet currentMethods = referenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), kind, context); + methods = methods == null ? currentMethods : unionSortedSet(currentMethods, methods); } return new SearchInitializer(methods, context); } @NotNull - private static List search(CompilerReferenceServiceEx referenceServiceEx, - SearchInitializer initializer, - int chainMaxLength, - int maxResultSize, - ChainCompletionContext context) { - LinkedList q = initializer.getChainQueue(); + private static List search(CompilerReferenceServiceEx referenceServiceEx, + SearchInitializer initializer, + int chainMaxLength, + int maxResultSize, + ChainCompletionContext context) { + LinkedList q = initializer.getChainQueue(); - List result = new ArrayList<>(); + List result = new ArrayList<>(); while (!q.isEmpty()) { ProgressManager.checkCanceled(); - CallChain currentChain = q.poll(); - RefChainOperation headSignature = currentChain.getHeadSignature(); + OperationChain currentChain = q.poll(); + RefChainOperation head = currentChain.getHead(); if (addChainIfTerminal(currentChain, result, chainMaxLength, context)) continue; // otherwise try to find chain continuation boolean updated = false; - SortedSet candidates = referenceServiceEx.findMethodReferenceOccurrences(headSignature.getOwner1(), SignatureData.ZERO_DIM); - LightRef ref1 = headSignature.getRef1(); - for (SignatureAndOccurrences candidate : candidates) { + SortedSet candidates = referenceServiceEx.findMethodReferenceOccurrences(head.getQualifierRawName(), SignatureData.ZERO_DIM, context); + LightRef ref = head.getLightRef(); + for (MethodRefAndOccurrences candidate : candidates) { if (candidate.getOccurrenceCount() * ChainSearchMagicConstants.FILTER_RATIO < currentChain.getChainWeight()) { break; } - MethodIncompleteSignature sign = candidate.getSignature(); - if ((sign.isStatic() || !sign.getOwner().equals(context.getTarget().getClassQName())) && - (!(ref1 instanceof LightRef.JavaLightMethodRef) || - referenceServiceEx - .mayHappen(candidate.getSignature().getRef(), ref1, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD))) { + MethodCall sign = candidate.getSignature(); + if ((sign.isStatic() || !sign.getQualifierRawName().equals(context.getTarget().getClassQName())) && + (!(ref instanceof LightRef.JavaLightMethodRef) || + referenceServiceEx.mayHappen(candidate.getSignature().getLightRef(), ref, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD))) { - CallChain continuation = currentChain.continuation(candidate.getSignature(), candidate.getOccurrenceCount(), context); + OperationChain + continuation = currentChain.continuationWithMethod(candidate.getSignature(), candidate.getOccurrenceCount(), context); if (continuation != null) { boolean stopChain = - candidate.getSignature().isStatic() || context.hasQualifier(context.resolvePsiClass(candidate.getSignature().getOwnerRef())); + candidate.getSignature().isStatic() || context.hasQualifier(context.resolvePsiClass(candidate.getSignature().getQualifierDef())); if (stopChain) { addChainIfNotPresent(continuation, result); } @@ -100,11 +95,12 @@ public class ChainSearcher { } } - if (ref1 instanceof LightRef.JavaLightMethodRef) { + if (ref instanceof LightRef.JavaLightMethodRef) { LightRef.LightClassHierarchyElementDef def = - referenceServiceEx.mayCallOfTypeCast((LightRef.JavaLightMethodRef)ref1, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD); + referenceServiceEx.mayCallOfTypeCast((LightRef.JavaLightMethodRef)ref, ChainSearchMagicConstants.METHOD_PROBABILITY_THRESHOLD); if (def != null) { - CallChain continuation = currentChain.continuationWithCast(new TypeCast(def, ((MethodIncompleteSignature)headSignature).getOwnerRef(), referenceServiceEx, 0), context); + OperationChain + continuation = currentChain.continuationWithCast(new TypeCast(def, head.getQualifierDef(), referenceServiceEx), context); if (continuation != null) { q.addFirst(continuation); updated = true; @@ -126,17 +122,18 @@ public class ChainSearcher { /** * To reduce false-positives we add a method to result only if its qualifier can be occurred together with context variables. */ - private static void addChainIfQualifierCanBeOccurredInContext(CallChain currentChain, - List result, + private static void addChainIfQualifierCanBeOccurredInContext(OperationChain currentChain, + List result, ChainCompletionContext context, CompilerReferenceServiceEx referenceServiceEx) { - RefChainOperation signature = currentChain.getLastMethodSign(); + RefChainOperation signature = currentChain.getHeadMethodCall(); + // type cast + introduced qualifier: it's too complex chain if (currentChain.hasCast()) return; - if (!context.getTarget().getClassQName().equals(((MethodIncompleteSignature)signature).getOwner())) { + if (!context.getTarget().getClassQName().equals(signature.getQualifierRawName())) { Set references = context.getContextClassReferences(); boolean isRelevantQualifier = false; for (LightRef ref: references) { - if (referenceServiceEx.mayHappen(((MethodIncompleteSignature)signature).getOwnerRef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) { + if (referenceServiceEx.mayHappen(signature.getQualifierDef(), ref, ChainSearchMagicConstants.VAR_PROBABILITY_THRESHOLD)) { isRelevantQualifier = true; break; } @@ -148,12 +145,12 @@ public class ChainSearcher { } } - private static boolean addChainIfTerminal(CallChain currentChain, List result, int pathMaximalLength, + private static boolean addChainIfTerminal(OperationChain currentChain, List result, int pathMaximalLength, ChainCompletionContext context) { - RefChainOperation signature = currentChain.getLastMethodSign(); - RefChainOperation head = currentChain.getHeadSignature(); - if (((MethodIncompleteSignature)signature).isStatic() || - context.hasQualifier(context.resolvePsiClass(head.getOwnerRef1())) || + RefChainOperation signature = currentChain.getHeadMethodCall(); + RefChainOperation head = currentChain.getHead(); + if (((MethodCall)signature).isStatic() || + context.hasQualifier(context.resolvePsiClass(head.getQualifierDef())) || currentChain.length() >= pathMaximalLength) { addChainIfNotPresent(currentChain, result); return true; @@ -161,7 +158,7 @@ public class ChainSearcher { return false; } - private static void addChainIfNotPresent(CallChain newChain, List result) { + private static void addChainIfNotPresent(OperationChain newChain, List result) { if (result.isEmpty()) { result.add(newChain); return; @@ -169,8 +166,8 @@ public class ChainSearcher { boolean doAdd = true; IntStack indicesToRemove = new IntStack(); for (int i = 0; i < result.size(); i++) { - CallChain chain = result.get(i); - CallChain.CompareResult r = CallChain.compare(chain, newChain); + OperationChain chain = result.get(i); + OperationChain.CompareResult r = OperationChain.compare(chain, newChain); switch (r) { case LEFT_CONTAINS_RIGHT: indicesToRemove.push(i); diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java index a23e3246b0f3..256e8c16ca40 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodChainLookupRangingHelper.java @@ -41,7 +41,7 @@ import java.util.stream.Collectors; public class MethodChainLookupRangingHelper { @NotNull - public static LookupElement toLookupElement(CallChain chain, + public static LookupElement toLookupElement(OperationChain chain, ChainCompletionContext context) { int unreachableParametersCount = 0; int matchedParametersInContext = 0; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SignatureAndOccurrences.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodRefAndOccurrences.java similarity index 76% rename from java/compiler/impl/src/com/intellij/compiler/chainsSearch/SignatureAndOccurrences.java rename to java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodRefAndOccurrences.java index 69e15ef02ee1..239503978f39 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SignatureAndOccurrences.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodRefAndOccurrences.java @@ -17,16 +17,16 @@ package com.intellij.compiler.chainsSearch; import org.jetbrains.annotations.NotNull; -public class SignatureAndOccurrences implements Comparable { - private final MethodIncompleteSignature mySignature; +public class MethodRefAndOccurrences implements Comparable { + private final MethodCall mySignature; private final int myOccurrences; - public SignatureAndOccurrences(final MethodIncompleteSignature signature, final int occurrences) { + public MethodRefAndOccurrences(final MethodCall signature, final int occurrences) { mySignature = signature; myOccurrences = occurrences; } - public MethodIncompleteSignature getSignature() { + public MethodCall getSignature() { return mySignature; } @@ -35,7 +35,7 @@ public class SignatureAndOccurrences implements Comparable op instanceof ChainOperation.TypeCast); } - public MethodIncompleteSignature getLastMethodSign() { - return myLastMethodSign; + @NotNull + public MethodCall getHeadMethodCall() { + return myHeadMethodCall; } @NotNull - public RefChainOperation getHeadSignature() { - return mySignature; + public RefChainOperation getHead() { + return myHeadOperation; } public int length() { @@ -107,28 +108,30 @@ public class CallChain { return myWeight; } - public CallChain continuation(@NotNull MethodIncompleteSignature signature, - int weight, - @NotNull ChainCompletionContext context) { - CallChain head = create(signature, weight, context); + @Nullable + OperationChain continuationWithMethod(@NotNull MethodCall signature, + int weight, + @NotNull ChainCompletionContext context) { + OperationChain head = create(signature, weight, context); if (head == null) return null; ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); newReverseOperations[length()] = head.getPath()[0]; - return new CallChain(head.getQualifierClass(), newReverseOperations, head.getHeadSignature(), signature, Math.min(weight, getChainWeight())); + return new OperationChain(head.getQualifierClass(), newReverseOperations, head.getHead(), signature, Math.min(weight, getChainWeight())); } - public CallChain continuationWithCast(@NotNull TypeCast cast, - @NotNull ChainCompletionContext context) { - PsiClass operand = context.resolvePsiClass(cast.getOperandRef()); + @Nullable + OperationChain continuationWithCast(@NotNull TypeCast cast, + @NotNull ChainCompletionContext context) { + PsiClass operand = context.resolvePsiClass(cast.getLightRef()); PsiClass castType = context.resolvePsiClass(cast.getCastTypeRef()); if (operand == null || castType == null) return null; ChainOperation[] newReverseOperations = new ChainOperation[length() + 1]; System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, myReverseOperations.length); newReverseOperations[length()] = new ChainOperation.TypeCast(operand, castType); - return new CallChain(operand, newReverseOperations, cast, myLastMethodSign, getChainWeight()); + return new OperationChain(operand, newReverseOperations, cast, myHeadMethodCall, getChainWeight()); } @Override @@ -138,7 +141,7 @@ public class CallChain { } @SuppressWarnings("ConstantConditions") - public static CompareResult compare(@NotNull CallChain left, @NotNull CallChain right) { + public static CompareResult compare(@NotNull OperationChain left, @NotNull OperationChain right) { if (left.length() == 0 || right.length() == 0) { throw new IllegalStateException("chains can't be empty"); } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java deleted file mode 100644 index 2f9b381b9f77..000000000000 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2000-2017 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.compiler.chainsSearch; - -import org.jetbrains.jps.backwardRefs.LightRef; - -public interface RefChainOperation { - String getOwner1(); - LightRef.LightClassHierarchyElementDef getOwnerRef1(); - - LightRef getRef1(); -} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.kt similarity index 52% rename from java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt rename to java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.kt index 14762a99f1ef..b12ace2190a6 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/MethodIncompleteSignature.kt +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/RefChainOperation.kt @@ -16,73 +16,74 @@ package com.intellij.compiler.chainsSearch import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx +import com.intellij.compiler.chainsSearch.context.ChainCompletionContext import com.intellij.compiler.chainsSearch.context.ChainSearchTarget -import com.intellij.openapi.project.Project -import com.intellij.psi.* -import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.PsiArrayType +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiModifier import com.intellij.psi.util.PsiUtil import org.jetbrains.jps.backwardRefs.LightRef import org.jetbrains.jps.backwardRefs.SignatureData -import java.util.function.Predicate -class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, - private val signatureData: SignatureData, - private val refService: CompilerReferenceServiceEx): RefChainOperation { - override fun getOwnerRef1(): LightRef.LightClassHierarchyElementDef { - return ownerRef +sealed class RefChainOperation { + abstract val qualifierRawName: String + + abstract val qualifierDef: LightRef.LightClassHierarchyElementDef + + abstract val lightRef: LightRef +} + +class TypeCast(override val lightRef: LightRef.LightClassHierarchyElementDef, + val castTypeRef: LightRef.LightClassHierarchyElementDef, + refService: CompilerReferenceServiceEx): RefChainOperation() { + override val qualifierRawName + get() = operandName.value + override val qualifierDef + get() = lightRef + + private val operandName = lazy(LazyThreadSafetyMode.NONE) { + refService.getName(lightRef.name) } +} - override fun getOwner1(): String { - return owner - } +class MethodCall(override val lightRef: LightRef.JavaLightMethodRef, + private val signatureData: SignatureData, + private val context: ChainCompletionContext): RefChainOperation() { - override fun getRef1(): LightRef { - return ref - } - - companion object { + private companion object { val CONSTRUCTOR_METHOD_NAME = "" } - val name: String by lazy(LazyThreadSafetyMode.NONE) { - refService.getName(ref.name) + override val qualifierRawName: String + get() = owner.value + override val qualifierDef: LightRef.LightClassHierarchyElementDef + get() = lightRef.owner + + private val name = lazy(LazyThreadSafetyMode.NONE) { + context.refService.getName(lightRef.name) } - val ownerRef = ref.owner - - val owner: String by lazy(LazyThreadSafetyMode.NONE) { - refService.getName(ref.owner.name) + private val owner = lazy(LazyThreadSafetyMode.NONE) { + context.refService.getName(lightRef.owner.name) } - private val rawReturnType: String by lazy(LazyThreadSafetyMode.NONE) { - refService.getName(signatureData.rawReturnType) + private val rawReturnType = lazy(LazyThreadSafetyMode.NONE) { + context.refService.getName(signatureData.rawReturnType) } - private val parameterCount: Int - get() = ref.parameterCount - val isStatic: Boolean get() = signatureData.isStatic - //TODO remove - fun resolveQualifier(project: Project, - resolveScope: GlobalSearchScope, - accessValidator: Predicate): PsiClass? { - val clazz = JavaPsiFacade.getInstance(project).findClass(owner, resolveScope) - return if (clazz != null && accessValidator.test(clazz)) clazz else null - } - - fun resolve(project: Project, - resolveScope: GlobalSearchScope, - accessValidator: Predicate): Array { - if (CONSTRUCTOR_METHOD_NAME == name) { + fun resolve(): Array { + if (CONSTRUCTOR_METHOD_NAME == name.value) { return PsiMethod.EMPTY_ARRAY } - val aClass = resolveQualifier(project, resolveScope, accessValidator) ?: return PsiMethod.EMPTY_ARRAY - return aClass.findMethodsByName(name, true) + val aClass = context.resolvePsiClass(qualifierDef) ?: return PsiMethod.EMPTY_ARRAY + return aClass.findMethodsByName(name.value, true) .filter { it.hasModifierProperty(PsiModifier.STATIC) == isStatic } .filter { !it.isDeprecated } - .filter { accessValidator.test(it) } + .filter { context.accessValidator().test(it) } .filter { val returnType = it.returnType when (signatureData.iteratorKind) { @@ -90,7 +91,7 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, when (returnType) { is PsiArrayType -> { val componentType = returnType.componentType - componentType is PsiClassType && componentType.resolve()?.qualifiedName == rawReturnType + componentType is PsiClassType && componentType.resolve()?.qualifiedName == rawReturnType.value } else -> false } @@ -98,11 +99,11 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, SignatureData.ITERATOR_ONE_DIM -> { val iteratorKind = ChainSearchTarget.getIteratorKind(PsiUtil.resolveClassInClassTypeOnly(returnType)) when { - iteratorKind != null -> PsiUtil.resolveClassInClassTypeOnly(PsiUtil.substituteTypeParameter(returnType, iteratorKind, 0, false))?.qualifiedName == rawReturnType + iteratorKind != null -> PsiUtil.resolveClassInClassTypeOnly(PsiUtil.substituteTypeParameter(returnType, iteratorKind, 0, false))?.qualifiedName == rawReturnType.value else -> false } } - SignatureData.ZERO_DIM -> returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType + SignatureData.ZERO_DIM -> returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType.value else -> throw IllegalStateException("kind is unsupported ${signatureData.iteratorKind}") } } @@ -114,23 +115,23 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef, if (this === other) return true if (other?.javaClass != javaClass) return false - other as MethodIncompleteSignature + other as MethodCall - if (ref.owner != other.ref.owner) return false - if (ref.name != other.ref.name) return false + if (lightRef.owner != other.lightRef.owner) return false + if (lightRef.name != other.lightRef.name) return false if (signatureData != other.signatureData) return false return true } override fun hashCode(): Int { - var result = ref.owner.hashCode() - result = 31 * result + ref.name.hashCode() + var result = lightRef.owner.hashCode() + result = 31 * result + lightRef.name.hashCode() result = 31 * result + signatureData.hashCode() return result } override fun toString(): String { - return owner + (if (isStatic) "" else "#") + name + "(" + parameterCount + ")" + return qualifierRawName + (if (isStatic) "." else "#") + name + "(" + lightRef.parameterCount + ")" } -} \ No newline at end of file +} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java index 8c074b7f5c5f..0dd1efe69859 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/SearchInitializer.java @@ -21,39 +21,39 @@ import java.util.*; public class SearchInitializer { private final ChainCompletionContext myContext; - private final LinkedList myQueue; - private final LinkedHashMap myChains; + private final LinkedList myQueue; + private final LinkedHashMap myChains; - public SearchInitializer(SortedSet indexValues, + public SearchInitializer(SortedSet indexValues, ChainCompletionContext context) { myContext = context; int size = indexValues.size(); - List chains = new ArrayList<>(size); + List chains = new ArrayList<>(size); populateFrequentlyUsedMethod(indexValues, chains); myQueue = new LinkedList<>(); myChains = new LinkedHashMap<>(chains.size()); - for (CallChain chain : chains) { - MethodIncompleteSignature signature = (MethodIncompleteSignature)chain.getHeadSignature(); + for (OperationChain chain : chains) { + MethodCall signature = (MethodCall)chain.getHead(); myQueue.add(chain); myChains.put(signature, chain); } } - public LinkedList getChainQueue() { + public LinkedList getChainQueue() { return myQueue; } - public LinkedHashMap getChains() { + public LinkedHashMap getChains() { return myChains; } - private void populateFrequentlyUsedMethod(SortedSet signatures, - List chains) { + private void populateFrequentlyUsedMethod(SortedSet signatures, + List chains) { int bestOccurrences = -1; - for (SignatureAndOccurrences indexValue : signatures) { - CallChain callChain = CallChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext); - if (callChain != null) { - chains.add(callChain); + for (MethodRefAndOccurrences indexValue : signatures) { + OperationChain operationChain = OperationChain.create(indexValue.getSignature(), indexValue.getOccurrenceCount(), myContext); + if (operationChain != null) { + chains.add(operationChain); int occurrences = indexValue.getOccurrenceCount(); if (bestOccurrences == -1) { bestOccurrences = occurrences; diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt deleted file mode 100644 index 9fa09601eeae..000000000000 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/TypeCast.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2000-2017 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.compiler.chainsSearch - -import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx -import org.jetbrains.jps.backwardRefs.LightRef - -class TypeCast(val operandRef: LightRef.LightClassHierarchyElementDef, - val castTypeRef: LightRef.LightClassHierarchyElementDef, - refService: CompilerReferenceServiceEx, - val occurrences: Int): RefChainOperation { - override fun getOwnerRef1(): LightRef.LightClassHierarchyElementDef { - return operandRef - } - - override fun getOwner1(): String { - return operandName - } - - override fun getRef1(): LightRef { - return operandRef - } - - val operandName: String by lazy(LazyThreadSafetyMode.NONE) { - refService.getName(operandRef.name) - } - - val castTypeName: String by lazy(LazyThreadSafetyMode.NONE) { - refService.getName(castTypeRef.name) - } -} diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java index 95a4631cc877..6dcc2f11fb54 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/completion/MethodChainCompletionContributor.java @@ -22,7 +22,7 @@ import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx; import com.intellij.compiler.backwardRefs.ReferenceIndexUnavailableException; import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants; import com.intellij.compiler.chainsSearch.ChainSearcher; -import com.intellij.compiler.chainsSearch.CallChain; +import com.intellij.compiler.chainsSearch.OperationChain; import com.intellij.compiler.chainsSearch.MethodChainLookupRangingHelper; import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; import com.intellij.compiler.chainsSearch.context.ChainSearchTarget; @@ -92,13 +92,13 @@ public class MethodChainCompletionContributor extends CompletionContributor { private static List searchForLookups(ChainCompletionContext context) { CompilerReferenceServiceEx methodsUsageIndexReader = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(context.getProject()); ChainSearchTarget target = context.getTarget(); - List searchResult = + List searchResult = ChainSearcher.search(ChainSearchMagicConstants.MAX_CHAIN_SIZE, target, ChainSearchMagicConstants.MAX_SEARCH_RESULT_SIZE, context, methodsUsageIndexReader); - int maxWeight = searchResult.stream().mapToInt(CallChain::getChainWeight).max().orElse(0); + int maxWeight = searchResult.stream().mapToInt(OperationChain::getChainWeight).max().orElse(0); return searchResult .stream() diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java index b480da460e2d..b67bcdfcb083 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/context/ChainCompletionContext.java @@ -17,7 +17,7 @@ package com.intellij.compiler.chainsSearch.context; import com.intellij.compiler.CompilerReferenceService; import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx; -import com.intellij.compiler.chainsSearch.MethodIncompleteSignature; +import com.intellij.compiler.chainsSearch.MethodCall; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyValue; @@ -68,7 +68,7 @@ public class ChainCompletionContext { @NotNull private final TIntObjectHashMap myQualifierClassResolver; @NotNull - private final Map myResolver; + private final Map myResolver; @NotNull private final CompilerReferenceServiceEx myRefServiceEx; @@ -98,7 +98,7 @@ public class ChainCompletionContext { myProject = context.getProject(); myResolveHelper = PsiResolveHelper.SERVICE.getInstance(myProject); myQualifierClassResolver = new TIntObjectHashMap<>(); - myResolver = FactoryMap.createMap(sign -> sign.resolve(myProject, myResolveScope, accessValidator())); + myResolver = FactoryMap.createMap(sign -> sign.resolve()); myRefServiceEx = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(myProject); } @@ -119,6 +119,11 @@ public class ChainCompletionContext { return false; } + @NotNull + public CompilerReferenceServiceEx getRefService() { + return myRefServiceEx; + } + @NotNull public PsiElement getContextPsi() { return myContext; @@ -182,11 +187,11 @@ public class ChainCompletionContext { } @NotNull - public PsiMethod[] resolve(MethodIncompleteSignature sign) { + public PsiMethod[] resolve(MethodCall sign) { return myResolver.get(sign); } - private Predicate accessValidator() { + public Predicate accessValidator() { return m -> myResolveHelper.isAccessible(m, myContext, null); }