From f4e9d9a7a0fdb3a6da402ca44d982a2a598c5b23 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 14 Sep 2015 17:15:50 +0300 Subject: [PATCH] Fix navigation to decompiled multifile class members. --- .../stubs/elements/JetFileElementType.java | 4 +- .../navigation/DecompiledNavigationUtils.java | 26 ++++++++++--- .../stubBuilder/KotlinClsStubBuilder.kt | 5 ++- .../decompiler/stubBuilder/clsStubBuilding.kt | 10 +++-- .../idea/stubindex/IdeStubIndexService.java | 38 +++++++++++++++++-- .../stubindex/JetMultifileClassPartIndex.kt | 37 ++++++++++++++++++ .../idea/stubindex/KotlinFileStubForIde.kt | 10 +++-- .../idea/stubindex/StaticFacadeIndexUtil.kt | 10 ++++- idea/src/META-INF/plugin.xml | 1 + .../DecompiledTextConsistencyTest.kt | 4 +- 10 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/JetMultifileClassPartIndex.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetFileElementType.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetFileElementType.java index 7b6d0918231..d199002387c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetFileElementType.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetFileElementType.java @@ -25,18 +25,16 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.tree.IStubFileElementType; -import com.intellij.util.io.StringRef; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.JetLanguage; import org.jetbrains.kotlin.parsing.JetParser; import org.jetbrains.kotlin.psi.stubs.KotlinFileStub; -import org.jetbrains.kotlin.psi.stubs.impl.KotlinFileStubImpl; import java.io.IOException; public class JetFileElementType extends IStubFileElementType { - public static final int STUB_VERSION = 56; + public static final int STUB_VERSION = 57; private static final String NAME = "kotlin.FILE"; diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/DecompiledNavigationUtils.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/DecompiledNavigationUtils.java index 90edc6461f7..bf16e12184d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/DecompiledNavigationUtils.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/DecompiledNavigationUtils.java @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.idea.decompiler.DecompiledUtilsKt; import org.jetbrains.kotlin.idea.decompiler.KotlinClsFileBase; import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope; +import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil; import org.jetbrains.kotlin.idea.vfilefinder.JsVirtualFileFinderFactory; import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory; import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder; @@ -35,6 +36,7 @@ import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.JetDeclaration; +import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.serialization.ProtoBuf; import org.jetbrains.kotlin.serialization.deserialization.NameResolver; @@ -44,6 +46,8 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf; import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils; +import java.util.Collection; + import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassId; import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getClassId; @@ -89,7 +93,7 @@ public final class DecompiledNavigationUtils { ) { if (ErrorUtils.isError(referencedDescriptor)) return null; - ClassId containerClassId = getContainerClassId(referencedDescriptor); + ClassId containerClassId = getContainerClassId(project, referencedDescriptor); if (containerClassId == null) return null; GlobalSearchScope scopeToSearchIn = JetSourceFilterScope.kotlinSourceAndClassFiles(GlobalSearchScope.allScope(project), project); @@ -115,11 +119,11 @@ public final class DecompiledNavigationUtils { //TODO: navigate to inner classes //TODO: should we construct proper SourceElement's for decompiled parts / facades? @Nullable - private static ClassId getContainerClassId(@NotNull DeclarationDescriptor referencedDescriptor) { + private static ClassId getContainerClassId(@NotNull Project project, @NotNull DeclarationDescriptor referencedDescriptor) { DeserializedCallableMemberDescriptor deserializedCallableContainer = DescriptorUtils.getParentOfType(referencedDescriptor, DeserializedCallableMemberDescriptor.class, true); if (deserializedCallableContainer != null) { - return getContainerClassId(deserializedCallableContainer); + return getContainerClassId(project, deserializedCallableContainer); } ClassOrPackageFragmentDescriptor @@ -132,8 +136,18 @@ public final class DecompiledNavigationUtils { ProtoBuf.Callable proto = deserializedDescriptor.getProto(); NameResolver nameResolver = deserializedDescriptor.getNameResolver(); if (proto.hasExtension(JvmProtoBuf.implClassName)) { - Name implClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName)); - return new ClassId(packageFQN, implClassName); + Name partClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName)); + FqName partFQN = packageFQN.child(partClassName); + Collection multifileFacadeJetFiles = + StaticFacadeIndexUtil.getMultifileClassForPart(partFQN, GlobalSearchScope.allScope(project), project); + if (multifileFacadeJetFiles.isEmpty()) { + return new ClassId(packageFQN, partClassName); + } + else { + JetFile multifileFacade = multifileFacadeJetFiles.iterator().next(); + String multifileFacadeName = multifileFacade.getVirtualFile().getNameWithoutExtension(); + return new ClassId(packageFQN, Name.identifier(multifileFacadeName)); + } } } @@ -142,7 +156,7 @@ public final class DecompiledNavigationUtils { if (containerDescriptor instanceof ClassDescriptor) { if (containerDescriptor.getContainingDeclaration() instanceof ClassDescriptor || ExpressionTypingUtils.isLocal(containerDescriptor.getContainingDeclaration(), containerDescriptor)) { - return getContainerClassId(containerDescriptor.getContainingDeclaration()); + return getContainerClassId(project, containerDescriptor.getContainingDeclaration()); } return getClassId((ClassDescriptor) containerDescriptor); } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt index 5f6a86c7e9b..fd066cbeb7a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt @@ -22,6 +22,7 @@ import com.intellij.psi.compiled.ClsStubBuilder import com.intellij.psi.impl.compiled.ClassFileStubBuilder import com.intellij.psi.stubs.PsiFileStub import com.intellij.util.indexing.FileContent +import org.jetbrains.kotlin.idea.decompiler.findMultifileClassParts import org.jetbrains.kotlin.idea.decompiler.isKotlinInternalCompiledFile import org.jetbrains.kotlin.idea.decompiler.readMultifileClassPartHeaders import org.jetbrains.kotlin.idea.decompiler.textBuilder.DirectoryBasedClassFinder @@ -61,8 +62,8 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() { val components = createStubBuilderComponents(file, packageFqName) if (header.isCompatibleMultifileClassKind()) { - val partHeaders = readMultifileClassPartHeaders(file, kotlinBinaryClass) - return createMultifileClassStub(partHeaders, classId.asSingleFqName(), components) + val partFiles = findMultifileClassParts(file, kotlinBinaryClass) + return createMultifileClassStub(kotlinBinaryClass, partFiles, classId.asSingleFqName(), components) } val annotationData = header.annotationData diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/clsStubBuilding.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/clsStubBuilding.kt index d66dc1edbc8..f47121f17cc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/clsStubBuilding.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/clsStubBuilding.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.stubindex.KotlinFileStubForIde import org.jetbrains.kotlin.lexer.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -74,15 +75,18 @@ fun createFileFacadeStub( } fun createMultifileClassStub( - partHeaders: List, + multifileClass: KotlinJvmBinaryClass, + partFiles: List, facadeFqName: FqName, components: ClsStubBuilderComponents ): KotlinFileStubImpl { val packageFqName = facadeFqName.parent() - val fileStub = KotlinFileStubForIde.forMultifileClassStub(facadeFqName, packageFqName.isRoot) + val partNames = multifileClass.classHeader.filePartClassNames?.asList() + val fileStub = KotlinFileStubForIde.forMultifileClassStub(facadeFqName, partNames, packageFqName.isRoot) setupFileStub(fileStub, packageFqName) val multifileClassContainer = ProtoContainer(null, packageFqName) - for (partHeader in partHeaders) { + for (partFile in partFiles) { + val partHeader = partFile.classHeader val partData = JvmProtoBufUtil.readPackageDataFrom(partHeader.annotationData!!) val partContext = components.createContext(partData.nameResolver, packageFqName) for (partMember in partData.packageProto.memberList) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java index 76f84724bb6..9eb6457f1ef 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.stubs.*; @@ -33,6 +34,8 @@ import org.jetbrains.kotlin.psi.stubs.elements.StubIndexService; import org.jetbrains.kotlin.util.TypeIndexUtilKt; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class IdeStubIndexService extends StubIndexService { @@ -55,12 +58,25 @@ public class IdeStubIndexService extends StubIndexService { sink.occurrence(JetFilePartClassIndex.INSTANCE.getKey(), partFqName.asString()); } + // TODO drop JetFile jetFile = stub.getPsi(); if (jetFile != null) { JvmFileClassInfo fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(jetFile); sink.occurrence(JetFileFacadeFqNameIndex.INSTANCE.getKey(), fileClassInfo.getFacadeClassFqName().asString()); sink.occurrence(JetFilePartClassIndex.INSTANCE.getKey(), fileClassInfo.getFileClassFqName().asString()); } + + List partNames = ((KotlinFileStubForIde) stub).getFacadePartSimpleNames(); + if (partNames != null) { + for (StringRef partName : partNames) { + String partSimpleName = StringRef.toString(partName); + if (partSimpleName == null) { + continue; + } + FqName multifileClassPartFqName = packageFqName.child(Name.identifier(partSimpleName)); + sink.occurrence(JetMultifileClassPartIndex.INSTANCE.getKey(), multifileClassPartFqName.asString()); + } + } } @Override @@ -184,9 +200,9 @@ public class IdeStubIndexService extends StubIndexService { JvmFileClassInfo fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file); StringRef facadeSimpleName = StringRef.fromString(fileClassInfo.getFacadeClassFqName().shortName().asString()); StringRef partSimpleName = StringRef.fromString(fileClassInfo.getFileClassFqName().shortName().asString()); - return new KotlinFileStubForIde(file, packageFqName, isScript, facadeSimpleName, partSimpleName); + return new KotlinFileStubForIde(file, packageFqName, isScript, facadeSimpleName, partSimpleName, null); } - return new KotlinFileStubForIde(file, packageFqName, isScript, null, null); + return new KotlinFileStubForIde(file, packageFqName, isScript, null, null, null); } @Override @@ -198,6 +214,16 @@ public class IdeStubIndexService extends StubIndexService { dataStream.writeBoolean(fileStub.isScript()); dataStream.writeName(StringRef.toString(fileStub.getFacadeSimpleName())); dataStream.writeName(StringRef.toString(fileStub.getPartSimpleName())); + List facadePartNames = fileStub.getFacadePartSimpleNames(); + if (facadePartNames == null) { + dataStream.writeInt(0); + } + else { + dataStream.writeInt(facadePartNames.size()); + for (StringRef partName : facadePartNames) { + dataStream.writeName(StringRef.toString(partName)); + } + } } @NotNull @@ -207,6 +233,12 @@ public class IdeStubIndexService extends StubIndexService { boolean isScript = dataStream.readBoolean(); StringRef facadeSimpleName = dataStream.readName(); StringRef partSimpleName = dataStream.readName(); - return new KotlinFileStubForIde(null, packageFqNameAsString, isScript, facadeSimpleName, partSimpleName); + int numPartNames = dataStream.readInt(); + List facadePartNames = new ArrayList(); + for (int i = 0; i < numPartNames; ++i) { + StringRef partNameRef = dataStream.readName(); + facadePartNames.add(partNameRef); + } + return new KotlinFileStubForIde(null, packageFqNameAsString, isScript, facadeSimpleName, partSimpleName, facadePartNames); } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/JetMultifileClassPartIndex.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/JetMultifileClassPartIndex.kt new file mode 100644 index 00000000000..58d980e356f --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/JetMultifileClassPartIndex.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.stubindex + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndexKey +import org.jetbrains.kotlin.psi.JetFile + + +public class JetMultifileClassPartIndex private constructor() : StringStubIndexExtension() { + override fun getKey(): StubIndexKey = KEY + + override fun get(key: String, project: Project, scope: GlobalSearchScope) = + super.get(key, project, JetSourceFilterScope.kotlinSourcesAndLibraries(scope, project)) + + companion object { + private val KEY = KotlinIndexUtil.createIndexKey(JetMultifileClassPartIndex::class.java) + public val INSTANCE: JetMultifileClassPartIndex = JetMultifileClassPartIndex() + public @JvmStatic fun getInstance(): JetMultifileClassPartIndex = INSTANCE + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/KotlinFileStubForIde.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/KotlinFileStubForIde.kt index f9e034ee90d..74199a4acc6 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/KotlinFileStubForIde.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/KotlinFileStubForIde.kt @@ -30,7 +30,8 @@ public class KotlinFileStubForIde( packageName: StringRef, isScript: Boolean, val facadeSimpleName: StringRef?, - val partSimpleName: StringRef? + val partSimpleName: StringRef?, + val facadePartSimpleNames: List? ) : KotlinFileStubImpl(jetFile, packageName, isScript), KotlinFileStub, PsiClassHolderFileStub { private fun StringRef.relativeToPackage() = getPackageFqName().child(Name.identifier(this.string)) @@ -42,7 +43,7 @@ public class KotlinFileStubForIde( get() = partSimpleName?.relativeToPackage() public constructor(jetFile: JetFile?, packageName: String, isScript: Boolean) - : this(jetFile, StringRef.fromString(packageName)!!, isScript, null, null) + : this(jetFile, StringRef.fromString(packageName)!!, isScript, null, null, null) companion object { public fun forFile(packageFqName: FqName, isScript: Boolean): KotlinFileStubImpl = @@ -50,6 +51,7 @@ public class KotlinFileStubForIde( packageName = StringRef.fromString(packageFqName.asString())!!, facadeSimpleName = null, partSimpleName = null, + facadePartSimpleNames = null, isScript = isScript) public fun forFileFacadeStub(facadeFqName: FqName, isScript: Boolean): KotlinFileStubImpl = @@ -57,13 +59,15 @@ public class KotlinFileStubForIde( packageName = facadeFqName.parent().stringRef(), facadeSimpleName = facadeFqName.shortName().stringRef(), partSimpleName = facadeFqName.shortName().stringRef(), + facadePartSimpleNames = null, isScript = isScript) - public fun forMultifileClassStub(facadeFqName: FqName, isScript: Boolean): KotlinFileStubImpl = + public fun forMultifileClassStub(facadeFqName: FqName, partNames: List?, isScript: Boolean): KotlinFileStubImpl = KotlinFileStubForIde(jetFile = null, packageName = facadeFqName.parent().stringRef(), facadeSimpleName = facadeFqName.shortName().stringRef(), partSimpleName = null, + facadePartSimpleNames = partNames?.map { StringRef.fromString(it) }, isScript = isScript) private fun FqName.stringRef() = StringRef.fromString(asString())!! diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt index 0f320b6c9a0..b4db214b452 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt @@ -20,16 +20,24 @@ import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.JetFile public object StaticFacadeIndexUtil { // TODO change as we introduce multi-file facades (this will require a separate index) - @jvmStatic public fun findFilesForFilePart( + @JvmStatic public fun findFilesForFilePart( partFqName: FqName, searchScope: GlobalSearchScope, project: Project ) : Collection = PackagePartClassUtils.getFilesWithCallables( JetFileFacadeFqNameIndex.INSTANCE.get(partFqName.asString(), project, searchScope)) + + @JvmStatic public fun getMultifileClassForPart( + partFqName: FqName, + searchScope: GlobalSearchScope, + project: Project + ): Collection = + JetMultifileClassPartIndex.INSTANCE.get(partFqName.asString(), project, searchScope) } \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index d4b380c325b..9e6defd3b98 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -504,6 +504,7 @@ + diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextConsistencyTest.kt b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextConsistencyTest.kt index 4305fdfb885..c0049acad54 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextConsistencyTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextConsistencyTest.kt @@ -37,9 +37,9 @@ public class DecompiledTextConsistencyTest : TextConsistencyBaseTest() { override fun getPackages(): List = emptyList() - override fun getFacades(): List = listOf(FqName("kotlin.JUtilKt")) + override fun getFacades(): List = listOf(FqName("kotlin.KotlinPackage")) - override fun getTopLevelMembers(): Map = mapOf("kotlin.JUtilKt" to "linkedListOf") + override fun getTopLevelMembers(): Map = mapOf("kotlin.KotlinPackage" to "linkedListOf") override fun getVirtualFileFinder(): VirtualFileFinder = JvmVirtualFileFinder.SERVICE.getInstance(project)