use QualifiedNameResolver for resolving links in KDoc

#KT-10398 Fixed
This commit is contained in:
Dmitry Jemerov
2016-07-06 18:02:02 +02:00
parent 60523fb93f
commit 88fe6680fa
7 changed files with 107 additions and 32 deletions
@@ -22,44 +22,54 @@ import org.jetbrains.kotlin.idea.util.getFileResolutionScope
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
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.module
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
fun resolveKDocLink(resolutionFacade: ResolutionFacade,
fun resolveKDocLink(context: BindingContext,
resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
fromSubjectOfTag: KDocTag?,
qualifiedName: List<String>): Collection<DeclarationDescriptor> {
if (fromSubjectOfTag?.knownTag == KDocKnownTag.PARAM) {
return resolveParamLink(fromDescriptor, qualifiedName)
}
// Try to find a matching local descriptor (parameter or type parameter) first.
if (qualifiedName.size == 1) {
val localResult = resolveInLocalScope(fromDescriptor, qualifiedName.single(), resolutionFacade)
if (!localResult.isEmpty()) {
return localResult
}
}
var result: Collection<DeclarationDescriptor> = listOf(fromDescriptor)
qualifiedName.forEach { nameComponent ->
val scope = getKDocLinkResolutionScope(resolutionFacade, result.singleOrNull() ?: return emptyList())
result = scope.collectDescriptorsFiltered(nameFilter = { it.asString() == nameComponent})
}
return result
}
private fun resolveInLocalScope(fromDescriptor: DeclarationDescriptor,
name: String,
resolutionFacade: ResolutionFacade): List<DeclarationDescriptor> {
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
return scope.collectDescriptorsFiltered(nameFilter = { it.asString() == name }).filter {
it.containingDeclaration == fromDescriptor
if (qualifiedName.size == 1) {
val descriptorsByName = scope.collectDescriptorsFiltered(nameFilter = { it.asString() == qualifiedName.single() })
// Try to find a matching local descriptor (parameter or type parameter) first.
val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor }
if (localDescriptors.isNotEmpty()) return localDescriptors
return descriptorsByName
}
val moduleDescriptor = fromDescriptor.module
val qualifiedExpressionResolver = resolutionFacade.getFrontendService(moduleDescriptor, QualifiedExpressionResolver::class.java)
val contextElement = DescriptorToSourceUtils.descriptorToDeclaration(fromDescriptor)
val factory = KtPsiFactory(resolutionFacade.project)
// TODO escape identifiers
val codeFragment = factory.createExpressionCodeFragment(qualifiedName.joinToString("."), contextElement)
val qualifiedExpression = codeFragment.findElementAt(codeFragment.textLength - 1)?.getStrictParentOfType<KtQualifiedExpression>() ?: return emptyList()
val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(qualifiedExpression, scope, context)
if (descriptor == null) return emptyList()
if (memberName != null) {
val memberScope = getKDocLinkResolutionScope(resolutionFacade, descriptor)
return memberScope.collectDescriptorsFiltered(nameFilter = { it == memberName })
}
return listOf(descriptor)
}
fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<DeclarationDescriptor> {
@@ -32,7 +32,8 @@ class KDocReference(element: KDocName): KtMultiReference<KDocName>(element) {
val declarationDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return arrayListOf()
val kdocLink = element.getStrictParentOfType<KDocLink>()!!
return resolveKDocLink(element.getResolutionFacade(),
return resolveKDocLink(context,
element.getResolutionFacade(),
declarationDescriptor,
kdocLink.getTagIfSubject(),
element.getQualifiedName())
@@ -59,11 +59,9 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
}
val bindingContext = context.analyze(BodyResolveMode.PARTIAL)
val contextDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, context]
if (contextDescriptor == null) {
return null
}
val descriptors = resolveKDocLink(context.getResolutionFacade(), contextDescriptor, null, StringUtil.split(link, ","))
val contextDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, context] ?: return null
val descriptors = resolveKDocLink(bindingContext, context.getResolutionFacade(),
contextDescriptor, null, StringUtil.split(link, ","))
val target = descriptors.firstOrNull()
if (target is DeclarationDescriptorWithSource) {
val source = target.source
@@ -0,0 +1,11 @@
// WITH_RUNTIME
/**
* Contains [kotlin.requi<caret>re]
*/
class C {
}
// REF: (kotlin).require(kotlin.Boolean)
+11
View File
@@ -0,0 +1,11 @@
// WITH_RUNTIME
/**
* Contains [kotlin.collections.Collecti<caret>on]
*/
class C {
}
// REF: (kotlin.collections).Collection
@@ -102,6 +102,18 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest {
doTest(fileName);
}
@TestMetadata("QualifiedNameFunctionReference.kt")
public void testQualifiedNameFunctionReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/QualifiedNameFunctionReference.kt");
doTest(fileName);
}
@TestMetadata("QualifiedNameReference.kt")
public void testQualifiedNameReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/QualifiedNameReference.kt");
doTest(fileName);
}
@TestMetadata("SeeReference.kt")
public void testSeeReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/SeeReference.kt");