Rewritten KDoc-link resolve and completion to work more reasonably
This commit is contained in:
@@ -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<String>): Collection<DeclarationDescriptor> =
|
||||
fun resolveKDocLink(
|
||||
context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
fromSubjectOfTag: KDocTag?,
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> =
|
||||
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<DeclarationDescriptor> {
|
||||
// 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<Declaration
|
||||
}
|
||||
return typeParams
|
||||
}
|
||||
}
|
||||
|
||||
return listOf()
|
||||
else -> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List<String>): List<DeclarationDescriptor> {
|
||||
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<String>): Collection<DeclarationDescriptor> {
|
||||
fun resolveKDocSampleLink(
|
||||
context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> {
|
||||
|
||||
val resolvedViaService = SampleResolutionService.resolveSample(context, fromDescriptor, resolutionFacade, qualifiedName)
|
||||
if (resolvedViaService.isNotEmpty()) return resolvedViaService
|
||||
@@ -96,19 +108,21 @@ private fun resolveDefaultKDocLink(
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> {
|
||||
|
||||
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
||||
val contextScope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
||||
|
||||
if (qualifiedName.size == 1) {
|
||||
val shortName = Name.identifier(qualifiedName.single())
|
||||
|
||||
val descriptorsByName = SmartList<DeclarationDescriptor>()
|
||||
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<KtQualifiedExpression>() ?: 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<DeclarationDescriptor> = SmartList()): Collection<DeclarationDescriptor> {
|
||||
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<SimpleFunctionDescriptor> {
|
||||
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<PropertyDescriptor> {
|
||||
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<DeclarationDescriptor> {
|
||||
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<Name> {
|
||||
return getContributedDescriptors(kindFilter = DescriptorKindFilter.FUNCTIONS)
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun getVariableNames(): Set<Name> {
|
||||
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!!)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user