From e52c3b4c814e993e35ecc3ce5cfa82d46f630cad Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 8 Aug 2017 09:57:47 +0300 Subject: [PATCH] Rewritten KDoc-link resolve and completion to work more reasonably --- .../kotlin/idea/kdoc/resolveKDocLink.kt | 190 ++++++++++++------ .../completion/KDocCompletionContributor.kt | 73 ++----- .../basic/java/importAliases/KDocExtension.kt | 8 + .../testData/kdoc/AfterPackageName.kt | 7 + .../testData/kdoc/ExtensionsFQLink.kt | 19 +- ...k.kt => ExtensionsForNestedClassFQLink.kt} | 0 idea/idea-completion/testData/kdoc/Link.kt | 16 +- .../testData/kdoc/MemberLink.kt | 9 +- .../testData/kdoc/NoCompletionAfterFunName.kt | 10 + .../testData/kdoc/NoTopLevelForQualified.kt | 9 + .../test/JvmBasicCompletionTestGenerated.java | 6 + .../test/KDocCompletionTestGenerated.java | 24 ++- .../kdoc/resolve/CheckExtensionReceiver.kt | 13 ++ .../kdoc/resolve/ExtensionFromImports.kt | 10 + idea/testData/kdoc/resolve/ExtensionFun.kt | 14 ++ .../kdoc/resolve/ExtensionNonQualified.kt | 10 + idea/testData/kdoc/resolve/ExtensionVal.kt | 14 ++ .../kdoc/resolve/OnlyMembersFromClass.kt | 8 + .../idea/kdoc/KdocResolveTestGenerated.java | 36 ++++ 19 files changed, 333 insertions(+), 143 deletions(-) create mode 100644 idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt create mode 100644 idea/idea-completion/testData/kdoc/AfterPackageName.kt rename idea/idea-completion/testData/kdoc/{ExtensionsForSubClassFQLink.kt => ExtensionsForNestedClassFQLink.kt} (100%) create mode 100644 idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt create mode 100644 idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt create mode 100644 idea/testData/kdoc/resolve/CheckExtensionReceiver.kt create mode 100644 idea/testData/kdoc/resolve/ExtensionFromImports.kt create mode 100644 idea/testData/kdoc/resolve/ExtensionFun.kt create mode 100644 idea/testData/kdoc/resolve/ExtensionNonQualified.kt create mode 100644 idea/testData/kdoc/resolve/ExtensionVal.kt create mode 100644 idea/testData/kdoc/resolve/OnlyMembersFromClass.kt diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/kdoc/resolveKDocLink.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/kdoc/resolveKDocLink.kt index d8db6ca8ec4..67912426e92 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/kdoc/resolveKDocLink.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/kdoc/resolveKDocLink.kt @@ -20,11 +20,13 @@ import com.intellij.openapi.components.ServiceManager import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.getFileResolutionScope +import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable +import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPsiFactory @@ -34,18 +36,22 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.* import org.jetbrains.kotlin.resolve.scopes.utils.* import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addIfNotNull -fun resolveKDocLink(context: BindingContext, - resolutionFacade: ResolutionFacade, - fromDescriptor: DeclarationDescriptor, - fromSubjectOfTag: KDocTag?, - qualifiedName: List): Collection = +fun resolveKDocLink( + context: BindingContext, + resolutionFacade: ResolutionFacade, + fromDescriptor: DeclarationDescriptor, + fromSubjectOfTag: KDocTag?, + qualifiedName: List +): Collection = when (fromSubjectOfTag?.knownTag) { KDocKnownTag.PARAM -> resolveParamLink(fromDescriptor, qualifiedName) KDocKnownTag.SAMPLE -> resolveKDocSampleLink(context, resolutionFacade, fromDescriptor, qualifiedName) @@ -56,8 +62,10 @@ fun resolveKDocLink(context: BindingContext, fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List { // TODO resolve parameters of functions passed as parameters when (fromDescriptor) { - is CallableDescriptor -> + is CallableDescriptor -> { return fromDescriptor.valueParameters + fromDescriptor.typeParameters + } + is ClassifierDescriptor -> { val typeParams = fromDescriptor.typeConstructor.parameters if (fromDescriptor is ClassDescriptor) { @@ -68,20 +76,24 @@ fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List { + return emptyList() + } + } } private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List): List { - val name = qualifiedName.singleOrNull() ?: return listOf() + val name = qualifiedName.singleOrNull() ?: return emptyList() return getParamDescriptors(fromDescriptor).filter { it.name.asString() == name } } -fun resolveKDocSampleLink(context: BindingContext, - resolutionFacade: ResolutionFacade, - fromDescriptor: DeclarationDescriptor, - qualifiedName: List): Collection { +fun resolveKDocSampleLink( + context: BindingContext, + resolutionFacade: ResolutionFacade, + fromDescriptor: DeclarationDescriptor, + qualifiedName: List +): Collection { val resolvedViaService = SampleResolutionService.resolveSample(context, fromDescriptor, resolutionFacade, qualifiedName) if (resolvedViaService.isNotEmpty()) return resolvedViaService @@ -96,19 +108,21 @@ private fun resolveDefaultKDocLink( qualifiedName: List ): Collection { - val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor) + val contextScope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor) if (qualifiedName.size == 1) { val shortName = Name.identifier(qualifiedName.single()) + val descriptorsByName = SmartList() - scope.collectAllByName(shortName, descriptorsByName) - + descriptorsByName.addIfNotNull(contextScope.findClassifier(shortName, NoLookupLocation.FROM_IDE)) + descriptorsByName.addIfNotNull(contextScope.findPackage(shortName)) + descriptorsByName.addAll(contextScope.collectFunctions(shortName, NoLookupLocation.FROM_IDE)) + descriptorsByName.addAll(contextScope.collectVariables(shortName, NoLookupLocation.FROM_IDE)) + // Try to find a matching local descriptor (parameter or type parameter) first val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor } if (localDescriptors.isNotEmpty()) return localDescriptors - descriptorsByName.addIfNotNull(fromDescriptor.module.getPackage(FqName.topLevel(shortName))) - return descriptorsByName } @@ -120,23 +134,17 @@ private fun resolveDefaultKDocLink( // TODO escape identifiers val codeFragment = factory.createExpressionCodeFragment(qualifiedName.joinToString("."), contextElement) val qualifiedExpression = codeFragment.findElementAt(codeFragment.textLength - 1)?.getStrictParentOfType() ?: return emptyList() - val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(qualifiedExpression, scope, context) + val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(qualifiedExpression, contextScope, context) if (descriptor == null) return emptyList() if (memberName != null) { - val memberScope = getKDocLinkResolutionScope(resolutionFacade, descriptor) - return memberScope.collectAllByName(memberName) + val memberScope = getKDocLinkMemberScope(descriptor, contextScope) + return memberScope.getContributedFunctions(memberName, NoLookupLocation.FROM_IDE) + + memberScope.getContributedVariables(memberName, NoLookupLocation.FROM_IDE) + + listOfNotNull(memberScope.getContributedClassifier(memberName, NoLookupLocation.FROM_IDE)) } return listOf(descriptor) } -private fun LexicalScope.collectAllByName(shortName: Name, toCollection: MutableCollection = SmartList()): Collection { - toCollection.addIfNotNull(findClassifier(shortName, NoLookupLocation.FROM_IDE)) - toCollection.addIfNotNull(findPackage(shortName)) - toCollection.addAll(collectFunctions(shortName, NoLookupLocation.FROM_IDE)) - toCollection.addAll(collectVariables(shortName, NoLookupLocation.FROM_IDE)) - return toCollection -} - private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope { return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope } @@ -145,60 +153,108 @@ private fun getClassInnerScope(outerScope: LexicalScope, descriptor: ClassDescri val headerScope = LexicalScopeImpl(outerScope, descriptor, false, descriptor.thisAsReceiverParameter, LexicalScopeKind.SYNTHETIC) { - for (typeParameter in descriptor.declaredTypeParameters) { - addClassifierDescriptor(typeParameter) - } - for (constructor in descriptor.constructors) { - addFunctionDescriptor(constructor) - } + descriptor.declaredTypeParameters.forEach { addClassifierDescriptor(it) } + descriptor.constructors.forEach { addFunctionDescriptor(it) } } - val scopeChain = arrayListOf(descriptor.defaultType.memberScope, - descriptor.staticScope) - - descriptor.companionObjectDescriptor?.let { - scopeChain.add(it.defaultType.memberScope) - } - - return LexicalChainedScope(headerScope, descriptor, false, null, - LexicalScopeKind.SYNTHETIC, - scopeChain) + val scopeChain = listOfNotNull( + descriptor.defaultType.memberScope, + descriptor.staticScope, + descriptor.companionObjectDescriptor?.defaultType?.memberScope + ) + return LexicalChainedScope(headerScope, descriptor, false, null, LexicalScopeKind.SYNTHETIC, scopeChain) } -fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, descriptor: DeclarationDescriptor): LexicalScope { - return when (descriptor) { - is PackageFragmentDescriptor -> - LexicalScope.Base(getPackageInnerScope(descriptor).memberScopeAsImportingScope(), descriptor) - - is PackageViewDescriptor -> - LexicalScope.Base(descriptor.memberScope.memberScopeAsImportingScope(), descriptor) - +fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, contextDescriptor: DeclarationDescriptor): LexicalScope { + return when (contextDescriptor) { is ClassDescriptor -> - getClassInnerScope(getOuterScope(descriptor, resolutionFacade), descriptor) + getClassInnerScope(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor) is FunctionDescriptor -> - FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, resolutionFacade), - descriptor, LocalRedeclarationChecker.DO_NOTHING) + FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(contextDescriptor, resolutionFacade), + contextDescriptor, LocalRedeclarationChecker.DO_NOTHING) is PropertyDescriptor -> - ScopeUtils.makeScopeForPropertyHeader(getOuterScope(descriptor, resolutionFacade), descriptor) + ScopeUtils.makeScopeForPropertyHeader(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor) is DeclarationDescriptorNonRoot -> - getOuterScope(descriptor, resolutionFacade) + getOuterScope(contextDescriptor, resolutionFacade) - else -> throw IllegalArgumentException("Cannot find resolution scope for root $descriptor") + else -> throw IllegalArgumentException("Cannot find resolution scope for root $contextDescriptor") } } private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, resolutionFacade: ResolutionFacade): LexicalScope { - val parent = descriptor.containingDeclaration + val parent = descriptor.containingDeclaration!! if (parent is PackageFragmentDescriptor) { val containingFile = (descriptor.source as? PsiSourceElement)?.psi?.containingFile as? KtFile - if (containingFile != null) { - val kotlinCacheService = ServiceManager.getService(containingFile.project, KotlinCacheService::class.java) - val facadeToUse = kotlinCacheService?.getResolutionFacade(listOf(containingFile)) ?: resolutionFacade - return facadeToUse.getFileResolutionScope(containingFile) - } + ?: return LexicalScope.Base(ImportingScope.Empty, parent) + val kotlinCacheService = ServiceManager.getService(containingFile.project, KotlinCacheService::class.java) + val facadeToUse = kotlinCacheService?.getResolutionFacade(listOf(containingFile)) ?: resolutionFacade + return facadeToUse.getFileResolutionScope(containingFile) + } + else { + return getKDocLinkResolutionScope(resolutionFacade, parent) + } +} + +fun getKDocLinkMemberScope(descriptor: DeclarationDescriptor, contextScope: LexicalScope): MemberScope { + return when (descriptor) { + is PackageFragmentDescriptor -> getPackageInnerScope(descriptor) + + is PackageViewDescriptor -> descriptor.memberScope + + is ClassDescriptor -> { + ChainedMemberScope("Member scope for KDoc resolve", listOfNotNull( + descriptor.unsubstitutedMemberScope, + descriptor.staticScope, + descriptor.companionObjectDescriptor?.unsubstitutedMemberScope, + ExtensionsScope(descriptor, contextScope) + )) + } + + else -> MemberScope.Empty + } +} + +private class ExtensionsScope( + private val receiverClass: ClassDescriptor, + private val contextScope: LexicalScope +) : MemberScope { + private val receiverTypes = listOf(receiverClass.defaultType) + + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection { + return contextScope.collectFunctions(name, location) + .flatMap { if (it is SimpleFunctionDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() } + } + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection { + return contextScope.collectVariables(name, location) + .flatMap { if (it is PropertyDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() } + } + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null + + override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection { + if (DescriptorKindExclude.Extensions in kindFilter.excludes) return emptyList() + return contextScope.collectDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.NonExtensions, nameFilter, changeNamesForAliased = true) + .flatMap { if (it is CallableDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() } + } + + override fun getFunctionNames(): Set { + return getContributedDescriptors(kindFilter = DescriptorKindFilter.FUNCTIONS) + .map { it.name } + .toSet() + } + + override fun getVariableNames(): Set { + return getContributedDescriptors(kindFilter = DescriptorKindFilter.VARIABLES) + .map { it.name } + .toSet() + } + + override fun printScopeStructure(p: Printer) { + p.println("Extensions for ${receiverClass.name} in:") + contextScope.printStructure(p) } - return getKDocLinkResolutionScope(resolutionFacade, parent!!) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt index a450a3c5871..83c6604c781 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt @@ -23,22 +23,17 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.patterns.PlatformPatterns.psiElement import com.intellij.patterns.StandardPatterns import com.intellij.util.ProcessingContext -import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor import org.jetbrains.kotlin.idea.core.ExpectedInfo +import org.jetbrains.kotlin.idea.kdoc.getKDocLinkMemberScope import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope import org.jetbrains.kotlin.idea.kdoc.getParamDescriptors import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.idea.util.CallType -import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink import org.jetbrains.kotlin.kdoc.psi.impl.KDocName -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction @@ -46,12 +41,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered -import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy class KDocCompletionContributor : CompletionContributor() { init { @@ -79,6 +70,7 @@ class KDocNameCompletionSession( toFromOriginalFileMapper: ToFromOriginalFileMapper, resultSet: CompletionResultSet ) : CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) { + override val descriptorKindFilter: DescriptorKindFilter? get() = null override val expectedInfos: Collection get() = emptyList() @@ -100,63 +92,28 @@ class KDocNameCompletionSession( declarationDescriptor: DeclarationDescriptor) { val section = position.getContainingSection() val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet() - val descriptors = getParamDescriptors(declarationDescriptor) + getParamDescriptors(declarationDescriptor) .filter { it.name.asString() !in documentedParameters } - - descriptors.forEach { - collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)) - } - } - - private fun collectPackageViewDescriptors(qualifiedLink: List, nameFilter: (Name) -> Boolean): Sequence { - val fqName = FqName.fromSegments(qualifiedLink) - return moduleDescriptor.getSubPackagesOf(fqName, nameFilter).asSequence() - .map { moduleDescriptor.getPackage(it) } - } - - private fun collectDescriptorsFromScope(scope: LexicalScope, nameFilter: (Name) -> Boolean, collectFromParentScopes: Boolean): Sequence { - val implicitReceivers = scope.getImplicitReceiversHierarchy().map { it.value } - - fun isApplicable(descriptor: DeclarationDescriptor): Boolean { - if (descriptor is CallableDescriptor) { - val extensionReceiver = descriptor.extensionReceiverParameter - if (extensionReceiver != null) { - val substituted = descriptor.substituteExtensionIfCallable(implicitReceivers, bindingContext, DataFlowInfo.EMPTY, - CallType.DEFAULT, moduleDescriptor) - return substituted.isNotEmpty() + .forEach { + collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)) } - } - return true - } - - val sequence = when { - collectFromParentScopes -> scope.collectDescriptorsFiltered(nameFilter = nameFilter, changeNamesForAliased = true).asSequence() - - scope is LexicalScope.Base -> scope.parent.getContributedDescriptors(nameFilter = nameFilter).asSequence() - - else -> scope.getContributedDescriptors(nameFilter = nameFilter).asSequence() + - scope.parent.collectDescriptorsFiltered(nameFilter = nameFilter).asSequence().filter { it.isExtension } - } - return sequence.filter(::isApplicable) } + private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Collection { + val contextScope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor) - private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Sequence { val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1) val nameFilter = descriptorNameFilter.toNameFilter() - return if (qualifiedLink.isNotEmpty()) { + if (qualifiedLink.isNotEmpty()) { val parentDescriptors = resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, kDocLink.getTagIfSubject(), qualifiedLink) - val childDescriptorsOfPartialLink = parentDescriptors.asSequence().flatMap { - val scope = getKDocLinkResolutionScope(resolutionFacade, it) - collectDescriptorsFromScope(scope, nameFilter, false) - } - - (collectPackageViewDescriptors(qualifiedLink, nameFilter) + childDescriptorsOfPartialLink) + return parentDescriptors + .flatMap { + val scope = getKDocLinkMemberScope(it, contextScope) + scope.getContributedDescriptors(nameFilter = nameFilter) + } } else { - val scope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor) - (collectDescriptorsFromScope(scope, nameFilter, true) - + collectPackageViewDescriptors(qualifiedLink, nameFilter)) + return contextScope.collectDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter, changeNamesForAliased = true) } } @@ -164,7 +121,7 @@ class KDocNameCompletionSession( collectDescriptorsForLinkCompletion(declarationDescriptor, kDocLink).forEach { val element = basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true) collector.addElement(object : LookupElementDecorator(element) { - override fun handleInsert(context: InsertionContext?) { + override fun handleInsert(context: InsertionContext) { // insert only plain name here, no qualifier/parentheses/etc. } }) diff --git a/idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt b/idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt new file mode 100644 index 00000000000..e78fc93682a --- /dev/null +++ b/idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt @@ -0,0 +1,8 @@ +import kotlin.text.capitalize as xxx + +/** + * [String.x] + */ +fun foo(){} + +// EXIST: { lookupString: "xxx", itemText: "xxx" } diff --git a/idea/idea-completion/testData/kdoc/AfterPackageName.kt b/idea/idea-completion/testData/kdoc/AfterPackageName.kt new file mode 100644 index 00000000000..9ca05071fa5 --- /dev/null +++ b/idea/idea-completion/testData/kdoc/AfterPackageName.kt @@ -0,0 +1,7 @@ +/** + * [kotlin.] + */ +fun foo(){} + +// EXIST: io +// EXIST: Int diff --git a/idea/idea-completion/testData/kdoc/ExtensionsFQLink.kt b/idea/idea-completion/testData/kdoc/ExtensionsFQLink.kt index dea43293ae8..92f558d6197 100644 --- a/idea/idea-completion/testData/kdoc/ExtensionsFQLink.kt +++ b/idea/idea-completion/testData/kdoc/ExtensionsFQLink.kt @@ -1,6 +1,10 @@ package a -class B { +interface I + +class A : I + +class B : I { /** * [a.B.] */ @@ -10,11 +14,20 @@ class B { } fun B.ext() { - } val B.extVal: String get() = "" +fun A.wrongExt(){} + +val A.wrongExtVal: String + get() = "" + +fun I.extForSuper(){} + // EXIST: ext -// EXIST: extVal \ No newline at end of file +// EXIST: extVal +// ABSENT: wrongExt +// ABSENT: wrongExtVal +// EXIST: extForSuper diff --git a/idea/idea-completion/testData/kdoc/ExtensionsForSubClassFQLink.kt b/idea/idea-completion/testData/kdoc/ExtensionsForNestedClassFQLink.kt similarity index 100% rename from idea/idea-completion/testData/kdoc/ExtensionsForSubClassFQLink.kt rename to idea/idea-completion/testData/kdoc/ExtensionsForNestedClassFQLink.kt diff --git a/idea/idea-completion/testData/kdoc/Link.kt b/idea/idea-completion/testData/kdoc/Link.kt index 1bbb50aab98..41da1a10fce 100644 --- a/idea/idea-completion/testData/kdoc/Link.kt +++ b/idea/idea-completion/testData/kdoc/Link.kt @@ -1,19 +1,19 @@ package z /** - * [] + * [a] */ -fun f(xyzzy: String) { +fun az(ap: String) { } -fun bar() { +fun aaa() { } -class Z +class aZ -// EXIST: z -// EXIST: Z -// EXIST: xyzzy -// EXIST: bar +// EXIST: az +// EXIST: aZ +// EXIST: ap +// EXIST: aaa diff --git a/idea/idea-completion/testData/kdoc/MemberLink.kt b/idea/idea-completion/testData/kdoc/MemberLink.kt index debe6b6faf1..d0eb4fbebd1 100644 --- a/idea/idea-completion/testData/kdoc/MemberLink.kt +++ b/idea/idea-completion/testData/kdoc/MemberLink.kt @@ -1,8 +1,8 @@ class Foo { /** - * [] + * [b] */ - fun xyzzy() { + fun baz() { } @@ -11,8 +11,9 @@ class Foo { } } -fun Foo.quux() { +fun Foo.boo() { } // EXIST: bar -// EXIST: quux +// EXIST: baz +// EXIST: boo diff --git a/idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt b/idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt new file mode 100644 index 00000000000..98a091b2bba --- /dev/null +++ b/idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt @@ -0,0 +1,10 @@ +class Foo +fun Foo.ext() = "" + + +/** + * [Foo.ext.] + */ +fun test() {} + +// NOTHING_ELSE \ No newline at end of file diff --git a/idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt b/idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt new file mode 100644 index 00000000000..4d091d21429 --- /dev/null +++ b/idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt @@ -0,0 +1,9 @@ +class Foo + +/** + * [Foo.] + */ +fun test() {} + + +// ABSENT: test \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 6a7b8baec4f..9908bba50ae 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -3046,6 +3046,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("KDocExtension.kt") + public void testKDocExtension() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt"); + doTest(fileName); + } + @TestMetadata("PrefixUsed.kt") public void testPrefixUsed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/PrefixUsed.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java index 9a4c1d1a031..40e48035969 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java @@ -32,6 +32,12 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest { + @TestMetadata("AfterPackageName.kt") + public void testAfterPackageName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/AfterPackageName.kt"); + doTest(fileName); + } + public void testAllFilesPresentInKdoc() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/kdoc"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } @@ -42,9 +48,9 @@ public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest doTest(fileName); } - @TestMetadata("ExtensionsForSubClassFQLink.kt") - public void testExtensionsForSubClassFQLink() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/ExtensionsForSubClassFQLink.kt"); + @TestMetadata("ExtensionsForNestedClassFQLink.kt") + public void testExtensionsForNestedClassFQLink() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/ExtensionsForNestedClassFQLink.kt"); doTest(fileName); } @@ -90,6 +96,18 @@ public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest doTest(fileName); } + @TestMetadata("NoCompletionAfterFunName.kt") + public void testNoCompletionAfterFunName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt"); + doTest(fileName); + } + + @TestMetadata("NoTopLevelForQualified.kt") + public void testNoTopLevelForQualified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt"); + doTest(fileName); + } + @TestMetadata("NotTagName.kt") public void testNotTagName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NotTagName.kt"); diff --git a/idea/testData/kdoc/resolve/CheckExtensionReceiver.kt b/idea/testData/kdoc/resolve/CheckExtensionReceiver.kt new file mode 100644 index 00000000000..a7d997faddc --- /dev/null +++ b/idea/testData/kdoc/resolve/CheckExtensionReceiver.kt @@ -0,0 +1,13 @@ +class Foo +class Bar + +fun Foo.foo(){} + +fun foo(){} + +/** + * [Bar.foo] + */ +fun test() {} + +// REF_EMPTY diff --git a/idea/testData/kdoc/resolve/ExtensionFromImports.kt b/idea/testData/kdoc/resolve/ExtensionFromImports.kt new file mode 100644 index 00000000000..779c582a23c --- /dev/null +++ b/idea/testData/kdoc/resolve/ExtensionFromImports.kt @@ -0,0 +1,10 @@ +import kotlin.io.extension +import java.io.File + +/** + * [File.extension] + */ +fun f() { +} + +// REF: (for java.io.File in kotlin.io).extension diff --git a/idea/testData/kdoc/resolve/ExtensionFun.kt b/idea/testData/kdoc/resolve/ExtensionFun.kt new file mode 100644 index 00000000000..15983b1ec75 --- /dev/null +++ b/idea/testData/kdoc/resolve/ExtensionFun.kt @@ -0,0 +1,14 @@ +package a + +class B { + /** + * [a.B.ext] + */ + fun member() { + } +} + +fun B.ext() { +} + +// REF: (for B in a).ext() diff --git a/idea/testData/kdoc/resolve/ExtensionNonQualified.kt b/idea/testData/kdoc/resolve/ExtensionNonQualified.kt new file mode 100644 index 00000000000..59fd47eb63d --- /dev/null +++ b/idea/testData/kdoc/resolve/ExtensionNonQualified.kt @@ -0,0 +1,10 @@ +class Foo + +fun Foo.foo(){} + +/** + * [foo] + */ +fun test() {} + +// REF: (for Foo in ).foo() diff --git a/idea/testData/kdoc/resolve/ExtensionVal.kt b/idea/testData/kdoc/resolve/ExtensionVal.kt new file mode 100644 index 00000000000..8f0210b9e92 --- /dev/null +++ b/idea/testData/kdoc/resolve/ExtensionVal.kt @@ -0,0 +1,14 @@ +package a + +class B { + /** + * [a.B.extVal] + */ + fun member() { + } +} + +val B.extVal: String + get() = "" + +// REF: (for B in a).extVal diff --git a/idea/testData/kdoc/resolve/OnlyMembersFromClass.kt b/idea/testData/kdoc/resolve/OnlyMembersFromClass.kt new file mode 100644 index 00000000000..333a258c12f --- /dev/null +++ b/idea/testData/kdoc/resolve/OnlyMembersFromClass.kt @@ -0,0 +1,8 @@ +class Foo + +/** + * [Foo.test] + */ +fun test() {} + +// REF_EMPTY diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java index eef22202367..91dc288a2d2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java @@ -49,6 +49,12 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest { doTest(fileName); } + @TestMetadata("CheckExtensionReceiver.kt") + public void testCheckExtensionReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/CheckExtensionReceiver.kt"); + doTest(fileName); + } + @TestMetadata("ClassSelfReference.kt") public void testClassSelfReference() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ClassSelfReference.kt"); @@ -73,6 +79,30 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest { doTest(fileName); } + @TestMetadata("ExtensionFromImports.kt") + public void testExtensionFromImports() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionFromImports.kt"); + doTest(fileName); + } + + @TestMetadata("ExtensionFun.kt") + public void testExtensionFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionFun.kt"); + doTest(fileName); + } + + @TestMetadata("ExtensionNonQualified.kt") + public void testExtensionNonQualified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionNonQualified.kt"); + doTest(fileName); + } + + @TestMetadata("ExtensionVal.kt") + public void testExtensionVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionVal.kt"); + doTest(fileName); + } + @TestMetadata("ImportAliasClass.kt") public void testImportAliasClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportAliasClass.kt"); @@ -85,6 +115,12 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest { doTest(fileName); } + @TestMetadata("OnlyMembersFromClass.kt") + public void testOnlyMembersFromClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/OnlyMembersFromClass.kt"); + doTest(fileName); + } + @TestMetadata("Overloads.kt") public void testOverloads() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/Overloads.kt");