Refactoring: step 1

This commit is contained in:
Valentin Kipyatkov
2016-10-27 15:20:39 +03:00
parent 6c3d2c62b4
commit dbaaee7d0b
3 changed files with 164 additions and 111 deletions
@@ -37,19 +37,15 @@ import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.util.*
@@ -154,20 +150,6 @@ abstract class CompletionSession(
declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) })
}
protected object TopLevelExtensionsExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is CallableMemberDescriptor) return false
if (descriptor.extensionReceiverParameter == null) return false
if (descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) return false /* do not filter out synthetic extensions */
val containingPackage = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false // TODO: temporary solution for Android synthetic extensions
return true
}
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): Boolean {
if (!configuration.javaClassesNotToBeUsed && descriptor is ClassDescriptor) {
if (descriptor.importableFqName?.let { isJavaClassNotToBeUsedInKotlin(it) } == true) return false
@@ -287,99 +269,20 @@ abstract class CompletionSession(
return context
}
protected data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
protected val referenceVariants: ReferenceVariants? by lazy {
if (nameExpression != null && descriptorKindFilter != null) collectReferenceVariants(descriptorKindFilter!!, nameExpression) else null
if (nameExpression != null && descriptorKindFilter != null)
ReferenceVariantsCollector(referenceVariantsHelper, indicesHelper(true), prefixMatcher,
nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext,
importableFqNameClassifier, configuration
).collectReferenceVariants(descriptorKindFilter!!)
else
null
}
protected val referenceVariantsWithNonInitializedVarExcluded: ReferenceVariants? by lazy {
referenceVariants?.let { ReferenceVariants(referenceVariantsHelper.excludeNonInitializedVariable(it.imported, position), it.notImportedExtensions) }
}
private fun collectReferenceVariants(
descriptorKindFilter: DescriptorKindFilter,
nameExpression: KtSimpleNameExpression,
runtimeReceiver: ExpressionReceiver? = null
): ReferenceVariants {
val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0
&& callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE
@Suppress("NAME_SHADOWING")
val descriptorKindFilter = if (completeExtensionsFromIndices)
descriptorKindFilter exclude TopLevelExtensionsExclude // handled via indices
else
descriptorKindFilter
fun getReferenceVariants(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
return referenceVariantsHelper.getReferenceVariants(
nameExpression,
kindFilter,
nameFilter,
filterOutJavaGettersAndSetters = false,
filterOutShadowed = false,
excludeNonInitializedVariable = false,
useReceiverType = runtimeReceiver?.type)
}
var variants = getReferenceVariants(descriptorKindFilter, descriptorNameFilter.toNameFilter())
val getOrSetPrefix = listOf("get", "set", "ge", "se", "g", "s").firstOrNull { prefix.startsWith(it) }
val additionalPropertyNameFilter: ((String) -> Boolean)? = run {
getOrSetPrefix?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asStringNameFilter() }
}
if (additionalPropertyNameFilter != null) {
variants += getReferenceVariants(descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES),
additionalPropertyNameFilter.toNameFilter())
variants = variants.distinct()
}
var notImportedExtensions: Collection<CallableDescriptor> = emptyList()
if (completeExtensionsFromIndices) {
val indicesHelper = indicesHelper(true)
val nameFilter = if (additionalPropertyNameFilter != null)
descriptorNameFilter or additionalPropertyNameFilter
else
descriptorNameFilter
val extensions = if (runtimeReceiver != null)
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiver.type), nameFilter)
else
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression!!, bindingContext, nameFilter)
val pair = extensions.partition { isImportableDescriptorImported(it) }
variants += pair.first
notImportedExtensions = pair.second
}
val shadowedDeclarationsFilter = if (runtimeReceiver != null)
ShadowedDeclarationsFilter(bindingContext, resolutionFacade, position, runtimeReceiver)
else
ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, position, callTypeAndReceiver)
if (shadowedDeclarationsFilter != null) {
variants = shadowedDeclarationsFilter.filter(variants)
notImportedExtensions = shadowedDeclarationsFilter
.createNonImportedDeclarationsFilter<CallableDescriptor>(importedDeclarations = variants)
.invoke(notImportedExtensions)
}
if (!configuration.javaGettersAndSetters) {
variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants)
}
if (!configuration.dataClassComponentFunctions) {
variants = variants.filter { !isDataClassComponentFunction(it) }
}
return ReferenceVariants(variants, notImportedExtensions)
}
private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean {
return descriptor is FunctionDescriptor &&
descriptor.isOperator &&
DataClassDescriptorResolver.isComponentLike(descriptor.name) &&
descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
}
protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? {
val variants = referenceVariants ?: return null
val filter: (DeclarationDescriptor) -> Boolean = { it is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(it) }
@@ -398,7 +301,11 @@ abstract class CompletionSession(
if (runtimeType == null || runtimeType == type) return null
val expressionReceiver = ExpressionReceiver.create(explicitReceiver, runtimeType, bindingContext)
val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, nameExpression!!, expressionReceiver)
val (variants, notImportedExtensions) = ReferenceVariantsCollector(
referenceVariantsHelper, indicesHelper(true), prefixMatcher,
nameExpression!!, callTypeAndReceiver, resolutionFacade, bindingContext,
importableFqNameClassifier, configuration, runtimeReceiver = expressionReceiver
).collectReferenceVariants(descriptorKindFilter!!)
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported)
val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions)
@@ -475,10 +382,4 @@ abstract class CompletionSession(
return callTypeAndReceiver to receiverTypes
}
protected fun isImportableDescriptorImported(descriptor: DeclarationDescriptor): Boolean {
val classification = importableFqNameClassifier.classify(descriptor.importableFqName!!, false)
return classification != ImportableFqNameClassifier.Classification.notImported
&& classification != ImportableFqNameClassifier.Classification.siblingImported
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
@@ -401,3 +402,10 @@ fun LookupElement.decorateAsStaticMember(
}
}
}
fun ImportableFqNameClassifier.isImportableDescriptorImported(descriptor: DeclarationDescriptor): Boolean {
val classification = classify(descriptor.importableFqName!!, false)
return classification != ImportableFqNameClassifier.Classification.notImported
&& classification != ImportableFqNameClassifier.Classification.siblingImported
}
@@ -0,0 +1,144 @@
/*
* Copyright 2010-2016 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.completion
import com.intellij.codeInsight.completion.PrefixMatcher
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
internal class ReferenceVariantsCollector(
private val referenceVariantsHelper: ReferenceVariantsHelper,
private val indicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val nameExpression: KtSimpleNameExpression,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val importableFqNameClassifier: ImportableFqNameClassifier,
private val configuration: CompletionSessionConfiguration,
private val runtimeReceiver: ExpressionReceiver? = null
) {
private val prefix = prefixMatcher.prefix
private val descriptorNameFilter = prefixMatcher.asStringNameFilter()
fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter): ReferenceVariants {
val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0
&& callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE
@Suppress("NAME_SHADOWING")
val descriptorKindFilter = if (completeExtensionsFromIndices)
descriptorKindFilter exclude TopLevelExtensionsExclude // handled via indices
else
descriptorKindFilter
fun getReferenceVariants(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
return referenceVariantsHelper.getReferenceVariants(
nameExpression,
kindFilter,
nameFilter,
filterOutJavaGettersAndSetters = false,
filterOutShadowed = false,
excludeNonInitializedVariable = false,
useReceiverType = runtimeReceiver?.type)
}
var variants = getReferenceVariants(descriptorKindFilter, descriptorNameFilter.toNameFilter())
val getOrSetPrefix = listOf("get", "set", "ge", "se", "g", "s").firstOrNull { prefix.startsWith(it) }
val additionalPropertyNameFilter: ((String) -> Boolean)? = run {
getOrSetPrefix?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asStringNameFilter() }
}
if (additionalPropertyNameFilter != null) {
variants += getReferenceVariants(descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES),
additionalPropertyNameFilter.toNameFilter())
variants = variants.distinct()
}
var notImportedExtensions: Collection<CallableDescriptor> = emptyList()
if (completeExtensionsFromIndices) {
val nameFilter = if (additionalPropertyNameFilter != null)
descriptorNameFilter or additionalPropertyNameFilter
else
descriptorNameFilter
val extensions = if (runtimeReceiver != null)
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiver.type), nameFilter)
else
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, nameExpression, bindingContext, nameFilter)
val pair = extensions.partition { importableFqNameClassifier.isImportableDescriptorImported(it) }
variants += pair.first
notImportedExtensions = pair.second
}
val shadowedDeclarationsFilter = if (runtimeReceiver != null)
ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression, runtimeReceiver)
else
ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression, callTypeAndReceiver)
if (shadowedDeclarationsFilter != null) {
variants = shadowedDeclarationsFilter.filter(variants)
notImportedExtensions = shadowedDeclarationsFilter
.createNonImportedDeclarationsFilter<CallableDescriptor>(importedDeclarations = variants)
.invoke(notImportedExtensions)
}
if (!configuration.javaGettersAndSetters) {
variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants)
}
if (!configuration.dataClassComponentFunctions) {
variants = variants.filter { !isDataClassComponentFunction(it) }
}
return ReferenceVariants(variants, notImportedExtensions)
}
private object TopLevelExtensionsExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is CallableMemberDescriptor) return false
if (descriptor.extensionReceiverParameter == null) return false
if (descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) return false /* do not filter out synthetic extensions */
val containingPackage = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false // TODO: temporary solution for Android synthetic extensions
return true
}
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean {
return descriptor is FunctionDescriptor &&
descriptor.isOperator &&
DataClassDescriptorResolver.isComponentLike(descriptor.name) &&
descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
}
}