Reformat the rest of 'frontend'-module according to new codestyle

This commit is contained in:
Dmitry Savvinov
2018-01-16 18:24:10 +03:00
parent e5f0ffb0c2
commit c2b532b167
132 changed files with 4906 additions and 4081 deletions
@@ -48,8 +48,8 @@ import java.util.*
import kotlin.coroutines.experimental.buildSequence import kotlin.coroutines.experimental.buildSequence
class ResolverForModule( class ResolverForModule(
val packageFragmentProvider: PackageFragmentProvider, val packageFragmentProvider: PackageFragmentProvider,
val componentProvider: ComponentProvider val componentProvider: ComponentProvider
) )
abstract class ResolverForProject<M : ModuleInfo> { abstract class ResolverForProject<M : ModuleInfo> {
@@ -70,34 +70,36 @@ class EmptyResolverForProject<M : ModuleInfo> : ResolverForProject<M>() {
get() = "Empty resolver" get() = "Empty resolver"
override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? = null override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? = null
override fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule = throw IllegalStateException("$descriptor is not contained in this resolver") override fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule =
throw IllegalStateException("$descriptor is not contained in this resolver")
override fun descriptorForModule(moduleInfo: M) = diagnoseUnknownModuleInfo(listOf(moduleInfo)) override fun descriptorForModule(moduleInfo: M) = diagnoseUnknownModuleInfo(listOf(moduleInfo))
override val allModules: Collection<M> = listOf() override val allModules: Collection<M> = listOf()
override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>) = throw IllegalStateException("Should not be called for $infos") override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>) = throw IllegalStateException("Should not be called for $infos")
} }
class ResolverForProjectImpl<M : ModuleInfo>( class ResolverForProjectImpl<M : ModuleInfo>(
private val debugName: String, private val debugName: String,
private val projectContext: ProjectContext, private val projectContext: ProjectContext,
modules: Collection<M>, modules: Collection<M>,
private val analyzerFacade: (M) -> AnalyzerFacade, private val analyzerFacade: (M) -> AnalyzerFacade,
private val modulesContent: (M) -> ModuleContent, private val modulesContent: (M) -> ModuleContent,
private val platformParameters: PlatformAnalysisParameters, private val platformParameters: PlatformAnalysisParameters,
private val targetEnvironment: TargetEnvironment = CompilerEnvironment, private val targetEnvironment: TargetEnvironment = CompilerEnvironment,
private val builtIns: KotlinBuiltIns = DefaultBuiltIns.Instance, private val builtIns: KotlinBuiltIns = DefaultBuiltIns.Instance,
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(), private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
private val packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty }, private val packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty },
private val firstDependency: M? = null, private val firstDependency: M? = null,
private val modulePlatforms: (M) -> MultiTargetPlatform?, private val modulePlatforms: (M) -> MultiTargetPlatform?,
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory, private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
private val languageSettingsProvider: LanguageSettingsProvider = LanguageSettingsProvider.Default, private val languageSettingsProvider: LanguageSettingsProvider = LanguageSettingsProvider.Default,
private val invalidateOnOOCB: Boolean = true private val invalidateOnOOCB: Boolean = true
) : ResolverForProject<M>() { ) : ResolverForProject<M>() {
private class ModuleData( private class ModuleData(
val moduleDescriptor: ModuleDescriptorImpl, val moduleDescriptor: ModuleDescriptorImpl,
val modificationTracker: ModificationTracker?, val modificationTracker: ModificationTracker?,
val modificationCount: Long? val modificationCount: Long?
) { ) {
fun isOutOfDate(): Boolean { fun isOutOfDate(): Boolean {
val currentModCount = modificationTracker?.modificationCount val currentModCount = modificationTracker?.modificationCount
@@ -117,16 +119,22 @@ class ResolverForProjectImpl<M : ModuleInfo>(
} }
private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) { private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) {
moduleDescriptor.setDependencies(LazyModuleDependencies( moduleDescriptor.setDependencies(
LazyModuleDependencies(
projectContext.storageManager, projectContext.storageManager,
module, module,
firstDependency, firstDependency,
this)) this
)
)
val content = modulesContent(module) val content = modulesContent(module)
moduleDescriptor.initialize( moduleDescriptor.initialize(
DelegatingPackageFragmentProvider(this, moduleDescriptor, content, DelegatingPackageFragmentProvider(
packageOracleFactory.createOracle(module))) this, moduleDescriptor, content,
packageOracleFactory.createOracle(module)
)
)
} }
private val resolverByModuleDescriptor = mutableMapOf<ModuleDescriptor, ResolverForModule>() private val resolverByModuleDescriptor = mutableMapOf<ModuleDescriptor, ResolverForModule>()
@@ -153,16 +161,17 @@ class ResolverForProjectImpl<M : ModuleInfo>(
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module) ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
analyzerFacade(module).createResolverForModule( analyzerFacade(module).createResolverForModule(
module, descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), modulesContent(module), module, descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), modulesContent(module),
platformParameters, targetEnvironment, this@ResolverForProjectImpl, platformParameters, targetEnvironment, this@ResolverForProjectImpl,
languageSettingsProvider, languageSettingsProvider,
packagePartProviderFactory(module, modulesContent(module))) packagePartProviderFactory(module, modulesContent(module))
)
} }
} }
} }
internal fun isResolverForModuleDescriptorComputed(descriptor: ModuleDescriptor) = internal fun isResolverForModuleDescriptorComputed(descriptor: ModuleDescriptor) =
descriptor in resolverByModuleDescriptor descriptor in resolverByModuleDescriptor
override fun descriptorForModule(moduleInfo: M): ModuleDescriptorImpl { override fun descriptorForModule(moduleInfo: M): ModuleDescriptorImpl {
if (!isCorrectModuleInfo(moduleInfo)) { if (!isCorrectModuleInfo(moduleInfo)) {
@@ -172,7 +181,7 @@ class ResolverForProjectImpl<M : ModuleInfo>(
} }
override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>) = override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>) =
throw AssertionError("$name does not know how to resolve $infos") throw AssertionError("$name does not know how to resolve $infos")
private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl { private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl {
if (module in modules) { if (module in modules) {
@@ -204,19 +213,21 @@ class ResolverForProjectImpl<M : ModuleInfo>(
} }
private fun createModuleDescriptor(module: M): ModuleData { private fun createModuleDescriptor(module: M): ModuleData {
val moduleDescriptor = ModuleDescriptorImpl(module.name, val moduleDescriptor = ModuleDescriptorImpl(
projectContext.storageManager, builtIns, modulePlatforms(module), module.capabilities) module.name,
projectContext.storageManager, builtIns, modulePlatforms(module), module.capabilities
)
moduleInfoByDescriptor[moduleDescriptor] = module moduleInfoByDescriptor[moduleDescriptor] = module
setupModuleDescriptor(module, moduleDescriptor) setupModuleDescriptor(module, moduleDescriptor)
val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker() ?: val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker()
(PsiModificationTracker.SERVICE.getInstance(projectContext.project).outOfCodeBlockModificationTracker.takeIf { invalidateOnOOCB }) ?: (PsiModificationTracker.SERVICE.getInstance(projectContext.project).outOfCodeBlockModificationTracker.takeIf { invalidateOnOOCB })
return ModuleData(moduleDescriptor, modificationTracker, modificationTracker?.modificationCount) return ModuleData(moduleDescriptor, modificationTracker, modificationTracker?.modificationCount)
} }
} }
data class ModuleContent( data class ModuleContent(
val syntheticFiles: Collection<KtFile>, val syntheticFiles: Collection<KtFile>,
val moduleContentScope: GlobalSearchScope val moduleContentScope: GlobalSearchScope
) )
interface PlatformAnalysisParameters interface PlatformAnalysisParameters
@@ -238,10 +249,10 @@ interface ModuleInfo {
// but if they are present, they should come after JVM built-ins in the dependencies list, because JVM built-ins contain // but if they are present, they should come after JVM built-ins in the dependencies list, because JVM built-ins contain
// additional members dependent on the JDK // additional members dependent on the JDK
fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns = fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns =
if (platform == TargetPlatform.Common) if (platform == TargetPlatform.Common)
ModuleInfo.DependencyOnBuiltIns.AFTER_SDK ModuleInfo.DependencyOnBuiltIns.AFTER_SDK
else else
ModuleInfo.DependencyOnBuiltIns.LAST ModuleInfo.DependencyOnBuiltIns.LAST
//TODO: (module refactoring) provide dependency on builtins after runtime in IDEA //TODO: (module refactoring) provide dependency on builtins after runtime in IDEA
enum class DependencyOnBuiltIns { NONE, AFTER_SDK, LAST } enum class DependencyOnBuiltIns { NONE, AFTER_SDK, LAST }
@@ -257,25 +268,25 @@ interface TrackableModuleInfo : ModuleInfo {
abstract class AnalyzerFacade { abstract class AnalyzerFacade {
abstract fun <M : ModuleInfo> createResolverForModule( abstract fun <M : ModuleInfo> createResolverForModule(
moduleInfo: M, moduleInfo: M,
moduleDescriptor: ModuleDescriptorImpl, moduleDescriptor: ModuleDescriptorImpl,
moduleContext: ModuleContext, moduleContext: ModuleContext,
moduleContent: ModuleContent, moduleContent: ModuleContent,
platformParameters: PlatformAnalysisParameters, platformParameters: PlatformAnalysisParameters,
targetEnvironment: TargetEnvironment, targetEnvironment: TargetEnvironment,
resolverForProject: ResolverForProject<M>, resolverForProject: ResolverForProject<M>,
languageSettingsProvider: LanguageSettingsProvider, languageSettingsProvider: LanguageSettingsProvider,
packagePartProvider: PackagePartProvider packagePartProvider: PackagePartProvider
): ResolverForModule ): ResolverForModule
abstract val targetPlatform: TargetPlatform abstract val targetPlatform: TargetPlatform
} }
class LazyModuleDependencies<M: ModuleInfo>( class LazyModuleDependencies<M : ModuleInfo>(
storageManager: StorageManager, storageManager: StorageManager,
private val module: M, private val module: M,
firstDependency: M? = null, firstDependency: M? = null,
private val resolverForProject: ResolverForProjectImpl<M> private val resolverForProject: ResolverForProjectImpl<M>
) : ModuleDependencies { ) : ModuleDependencies {
private val dependencies = storageManager.createLazyValue { private val dependencies = storageManager.createLazyValue {
val moduleDescriptor = resolverForProject.descriptorForModule(module) val moduleDescriptor = resolverForProject.descriptorForModule(module)
@@ -307,10 +318,10 @@ class LazyModuleDependencies<M: ModuleInfo>(
private class DelegatingPackageFragmentProvider<M : ModuleInfo>( private class DelegatingPackageFragmentProvider<M : ModuleInfo>(
private val resolverForProject: ResolverForProjectImpl<M>, private val resolverForProject: ResolverForProjectImpl<M>,
private val module: ModuleDescriptor, private val module: ModuleDescriptor,
moduleContent: ModuleContent, moduleContent: ModuleContent,
private val packageOracle: PackageOracle private val packageOracle: PackageOracle
) : PackageFragmentProvider { ) : PackageFragmentProvider {
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet() private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
@@ -366,6 +377,7 @@ interface ResolverForModuleComputationTracker {
fun onResolverComputed(moduleInfo: ModuleInfo) fun onResolverComputed(moduleInfo: ModuleInfo)
companion object { companion object {
fun getInstance(project: Project): ResolverForModuleComputationTracker? = ServiceManager.getService(project, ResolverForModuleComputationTracker::class.java) ?: null fun getInstance(project: Project): ResolverForModuleComputationTracker? =
ServiceManager.getService(project, ResolverForModuleComputationTracker::class.java) ?: null
} }
} }
@@ -51,42 +51,42 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen
*/ */
object CommonAnalyzerFacade : AnalyzerFacade() { object CommonAnalyzerFacade : AnalyzerFacade() {
private class SourceModuleInfo( private class SourceModuleInfo(
override val name: Name, override val name: Name,
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>, override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>,
private val dependOnOldBuiltIns: Boolean private val dependOnOldBuiltIns: Boolean
) : ModuleInfo { ) : ModuleInfo {
override fun dependencies() = listOf(this) override fun dependencies() = listOf(this)
override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns = override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns =
if (dependOnOldBuiltIns) ModuleInfo.DependencyOnBuiltIns.LAST else ModuleInfo.DependencyOnBuiltIns.NONE if (dependOnOldBuiltIns) ModuleInfo.DependencyOnBuiltIns.LAST else ModuleInfo.DependencyOnBuiltIns.NONE
} }
fun analyzeFiles( fun analyzeFiles(
files: Collection<KtFile>, moduleName: Name, dependOnBuiltIns: Boolean, languageVersionSettings: LanguageVersionSettings, files: Collection<KtFile>, moduleName: Name, dependOnBuiltIns: Boolean, languageVersionSettings: LanguageVersionSettings,
capabilities: Map<ModuleDescriptor.Capability<*>, Any?> = mapOf(MultiTargetPlatform.CAPABILITY to MultiTargetPlatform.Common), capabilities: Map<ModuleDescriptor.Capability<*>, Any?> = mapOf(MultiTargetPlatform.CAPABILITY to MultiTargetPlatform.Common),
packagePartProviderFactory: (ModuleInfo, ModuleContent) -> PackagePartProvider packagePartProviderFactory: (ModuleInfo, ModuleContent) -> PackagePartProvider
): AnalysisResult { ): AnalysisResult {
val moduleInfo = SourceModuleInfo(moduleName, capabilities, dependOnBuiltIns) val moduleInfo = SourceModuleInfo(moduleName, capabilities, dependOnBuiltIns)
val project = files.firstOrNull()?.project ?: throw AssertionError("No files to analyze") val project = files.firstOrNull()?.project ?: throw AssertionError("No files to analyze")
val multiplatformLanguageSettings = object : LanguageVersionSettings by languageVersionSettings { val multiplatformLanguageSettings = object : LanguageVersionSettings by languageVersionSettings {
override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State = override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State =
if (feature == LanguageFeature.MultiPlatformProjects) LanguageFeature.State.ENABLED if (feature == LanguageFeature.MultiPlatformProjects) LanguageFeature.State.ENABLED
else languageVersionSettings.getFeatureSupport(feature) else languageVersionSettings.getFeatureSupport(feature)
} }
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val resolver = ResolverForProjectImpl ( val resolver = ResolverForProjectImpl(
"sources for metadata serializer", "sources for metadata serializer",
ProjectContext(project), listOf(moduleInfo), { CommonAnalyzerFacade }, ProjectContext(project), listOf(moduleInfo), { CommonAnalyzerFacade },
{ ModuleContent(files, GlobalSearchScope.allScope(project)) }, { ModuleContent(files, GlobalSearchScope.allScope(project)) },
object : PlatformAnalysisParameters {}, object : PlatformAnalysisParameters {},
languageSettingsProvider = object : LanguageSettingsProvider { languageSettingsProvider = object : LanguageSettingsProvider {
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project) = multiplatformLanguageSettings override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project) = multiplatformLanguageSettings
override fun getTargetPlatform(moduleInfo: ModuleInfo) = TargetPlatformVersion.NoVersion override fun getTargetPlatform(moduleInfo: ModuleInfo) = TargetPlatformVersion.NoVersion
}, },
packagePartProviderFactory = packagePartProviderFactory, packagePartProviderFactory = packagePartProviderFactory,
modulePlatforms = { MultiTargetPlatform.Common } modulePlatforms = { MultiTargetPlatform.Common }
) )
val moduleDescriptor = resolver.descriptorForModule(moduleInfo) val moduleDescriptor = resolver.descriptorForModule(moduleInfo)
@@ -98,46 +98,46 @@ object CommonAnalyzerFacade : AnalyzerFacade() {
} }
override fun <M : ModuleInfo> createResolverForModule( override fun <M : ModuleInfo> createResolverForModule(
moduleInfo: M, moduleInfo: M,
moduleDescriptor: ModuleDescriptorImpl, moduleDescriptor: ModuleDescriptorImpl,
moduleContext: ModuleContext, moduleContext: ModuleContext,
moduleContent: ModuleContent, moduleContent: ModuleContent,
platformParameters: PlatformAnalysisParameters, platformParameters: PlatformAnalysisParameters,
targetEnvironment: TargetEnvironment, targetEnvironment: TargetEnvironment,
resolverForProject: ResolverForProject<M>, resolverForProject: ResolverForProject<M>,
languageSettingsProvider: LanguageSettingsProvider, languageSettingsProvider: LanguageSettingsProvider,
packagePartProvider: PackagePartProvider packagePartProvider: PackagePartProvider
): ResolverForModule { ): ResolverForModule {
val (syntheticFiles, moduleContentScope) = moduleContent val (syntheticFiles, moduleContentScope) = moduleContent
val project = moduleContext.project val project = moduleContext.project
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory( val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project, moduleContext.storageManager, syntheticFiles, project, moduleContext.storageManager, syntheticFiles,
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope, if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope,
moduleInfo moduleInfo
) )
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace() val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
val container = createContainerToResolveCommonCode( val container = createContainerToResolveCommonCode(
moduleContext, trace, declarationProviderFactory, moduleContentScope, targetEnvironment, packagePartProvider, moduleContext, trace, declarationProviderFactory, moduleContentScope, targetEnvironment, packagePartProvider,
languageSettingsProvider.getLanguageVersionSettings(moduleInfo, project) languageSettingsProvider.getLanguageVersionSettings(moduleInfo, project)
) )
val packageFragmentProviders = listOf( val packageFragmentProviders = listOf(
container.get<ResolveSession>().packageFragmentProvider, container.get<ResolveSession>().packageFragmentProvider,
container.get<MetadataPackageFragmentProvider>() container.get<MetadataPackageFragmentProvider>()
) )
return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container) return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container)
} }
private fun createContainerToResolveCommonCode( private fun createContainerToResolveCommonCode(
moduleContext: ModuleContext, moduleContext: ModuleContext,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory, declarationProviderFactory: DeclarationProviderFactory,
moduleContentScope: GlobalSearchScope, moduleContentScope: GlobalSearchScope,
targetEnvironment: TargetEnvironment, targetEnvironment: TargetEnvironment,
packagePartProvider: PackagePartProvider, packagePartProvider: PackagePartProvider,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): StorageComponentContainer = createContainer("ResolveCommonCode", targetPlatform) { ): StorageComponentContainer = createContainer("ResolveCommonCode", targetPlatform) {
configureModule(moduleContext, targetPlatform, TargetPlatformVersion.NoVersion, bindingTrace) configureModule(moduleContext, targetPlatform, TargetPlatformVersion.NoVersion, bindingTrace)
@@ -154,7 +154,7 @@ object CommonAnalyzerFacade : AnalyzerFacade() {
useImpl<MetadataPackageFragmentProvider>() useImpl<MetadataPackageFragmentProvider>()
val metadataFinderFactory = ServiceManager.getService(moduleContext.project, MetadataFinderFactory::class.java) val metadataFinderFactory = ServiceManager.getService(moduleContext.project, MetadataFinderFactory::class.java)
?: error("No MetadataFinderFactory in project") ?: error("No MetadataFinderFactory in project")
useInstance(metadataFinderFactory.create(moduleContentScope)) useInstance(metadataFinderFactory.create(moduleContentScope))
targetEnvironment.configure(this) targetEnvironment.configure(this)
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
interface ControlFlowBuilder { interface ControlFlowBuilder {
// Subroutines // Subroutines
fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null) fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null)
fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode
val currentSubroutine: KtElement val currentSubroutine: KtElement
@@ -624,7 +624,7 @@ class ControlFlowInformationProvider private constructor(
if (!VariableUseState.isUsed(variableUseState)) { if (!VariableUseState.isUsed(variableUseState)) {
if (element.isSingleUnderscore) return if (element.isSingleUnderscore) return
when { when {
// KtDestructuringDeclarationEntry -> KtDestructuringDeclaration -> KtParameter -> KtParameterList // KtDestructuringDeclarationEntry -> KtDestructuringDeclaration -> KtParameter -> KtParameterList
element is KtDestructuringDeclarationEntry && element.parent.parent?.parent is KtParameterList -> element is KtDestructuringDeclarationEntry && element.parent.parent?.parent is KtParameterList ->
report(Errors.UNUSED_DESTRUCTURED_PARAMETER_ENTRY.on(element, variableDescriptor), ctxt) report(Errors.UNUSED_DESTRUCTURED_PARAMETER_ENTRY.on(element, variableDescriptor), ctxt)
@@ -866,7 +866,7 @@ class ControlFlowInformationProvider private constructor(
class KindAndCall(var kind: TailRecursionKind, internal val call: ResolvedCall<*>) class KindAndCall(var kind: TailRecursionKind, internal val call: ResolvedCall<*>)
val calls = HashMap<KtElement, KindAndCall>() val calls = HashMap<KtElement, KindAndCall>()
traverseCalls traverse@{ instruction, resolvedCall -> traverseCalls traverse@ { instruction, resolvedCall ->
// is this a recursive call? // is this a recursive call?
val functionDescriptor = resolvedCall.resultingDescriptor val functionDescriptor = resolvedCall.resultingDescriptor
if (functionDescriptor.original != subroutineDescriptor) return@traverse if (functionDescriptor.original != subroutineDescriptor) return@traverse
@@ -1021,23 +1021,23 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
private fun visitInlinedFunction(lambdaFunctionLiteral: KtFunction, invocationKind: InvocationKind) { private fun visitInlinedFunction(lambdaFunctionLiteral: KtFunction, invocationKind: InvocationKind) {
// Defer emitting of inlined declaration // Defer emitting of inlined declaration
deferredGeneratorsStack.peek().add({ builder -> deferredGeneratorsStack.peek().add({ builder ->
val beforeDeclaration = builder.createUnboundLabel("before inlined declaration") val beforeDeclaration = builder.createUnboundLabel("before inlined declaration")
val afterDeclaration = builder.createUnboundLabel("after inlined declaration") val afterDeclaration = builder.createUnboundLabel("after inlined declaration")
builder.bindLabel(beforeDeclaration) builder.bindLabel(beforeDeclaration)
if (!invocationKind.isDefinitelyVisited()) { if (!invocationKind.isDefinitelyVisited()) {
builder.nondeterministicJump(afterDeclaration, lambdaFunctionLiteral, null) builder.nondeterministicJump(afterDeclaration, lambdaFunctionLiteral, null)
} }
generate(lambdaFunctionLiteral, invocationKind) generate(lambdaFunctionLiteral, invocationKind)
if (invocationKind.canBeRevisited()) { if (invocationKind.canBeRevisited()) {
builder.nondeterministicJump(beforeDeclaration, lambdaFunctionLiteral, null) builder.nondeterministicJump(beforeDeclaration, lambdaFunctionLiteral, null)
} }
builder.bindLabel(afterDeclaration) builder.bindLabel(afterDeclaration)
}) })
} }
override fun visitNamedFunction(function: KtNamedFunction) { override fun visitNamedFunction(function: KtNamedFunction) {
@@ -1544,8 +1544,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
//noinspection EnumSwitchStatementWhichMissesCases //noinspection EnumSwitchStatementWhichMissesCases
when (kind) { when (kind) {
ExplicitReceiverKind.DISPATCH_RECEIVER -> explicitReceiver = resolvedCall.dispatchReceiver ExplicitReceiverKind.DISPATCH_RECEIVER -> explicitReceiver = resolvedCall.dispatchReceiver
ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> explicitReceiver = resolvedCall.extensionReceiver ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> explicitReceiver =
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> {} resolvedCall.extensionReceiver
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> {
}
} }
} }
@@ -29,28 +29,28 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.BindingContextUtils
class PseudocodeVariableDataCollector( class PseudocodeVariableDataCollector(
private val bindingContext: BindingContext, private val bindingContext: BindingContext,
private val pseudocode: Pseudocode private val pseudocode: Pseudocode
) { ) {
val blockScopeVariableInfo = computeBlockScopeVariableInfo(pseudocode) val blockScopeVariableInfo = computeBlockScopeVariableInfo(pseudocode)
fun <I : ControlFlowInfo<*, *>> collectData( fun <I : ControlFlowInfo<*, *>> collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
initialInfo: I, initialInfo: I,
instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I> instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I>
): Map<Instruction, Edges<I>> { ): Map<Instruction, Edges<I>> {
return pseudocode.collectData( return pseudocode.collectData(
traversalOrder, traversalOrder,
instructionDataMergeStrategy, instructionDataMergeStrategy,
{ from, to, info -> filterOutVariablesOutOfScope(from, to, info) }, { from, to, info -> filterOutVariablesOutOfScope(from, to, info) },
initialInfo initialInfo
) )
} }
private fun <I : ControlFlowInfo<*, *>> filterOutVariablesOutOfScope( private fun <I : ControlFlowInfo<*, *>> filterOutVariablesOutOfScope(
from: Instruction, from: Instruction,
to: Instruction, to: Instruction,
info: I info: I
): I { ): I {
// If an edge goes from deeper scope to a less deep one, this means that it points outside of the deeper scope. // If an edge goes from deeper scope to a less deep one, this means that it points outside of the deeper scope.
val toDepth = to.blockScope.depth val toDepth = to.blockScope.depth
@@ -74,9 +74,11 @@ class PseudocodeVariableDataCollector(
val variableDeclarationElement = instruction.variableDeclarationElement val variableDeclarationElement = instruction.variableDeclarationElement
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) ?: return@traverse val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) ?: return@traverse
val variableDescriptor = BindingContextUtils.variableDescriptorForDeclaration(descriptor) val variableDescriptor = BindingContextUtils.variableDescriptorForDeclaration(descriptor)
?: throw AssertionError("Variable or class descriptor should correspond to " + ?: throw AssertionError(
"the instruction for ${instruction.element.text}.\n" + "Variable or class descriptor should correspond to " +
"Descriptor: $descriptor") "the instruction for ${instruction.element.text}.\n" +
"Descriptor: $descriptor"
)
blockScopeVariableInfo.registerVariableDeclaredInScope(variableDescriptor, instruction.blockScope) blockScopeVariableInfo.registerVariableDeclaredInScope(variableDescriptor, instruction.blockScope)
} }
}) })
@@ -85,8 +87,8 @@ class PseudocodeVariableDataCollector(
} }
interface BlockScopeVariableInfo { interface BlockScopeVariableInfo {
val declaredIn : Map<VariableDescriptor, BlockScope> val declaredIn: Map<VariableDescriptor, BlockScope>
val scopeVariables : Map<BlockScope, Collection<VariableDescriptor>> val scopeVariables: Map<BlockScope, Collection<VariableDescriptor>>
} }
class BlockScopeVariableInfoImpl : BlockScopeVariableInfo { class BlockScopeVariableInfoImpl : BlockScopeVariableInfo {
@@ -42,14 +42,14 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
private val pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode) private val pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode)
private class VariablesForDeclaration( private class VariablesForDeclaration(
val valsWithTrivialInitializer: Set<VariableDescriptor>, val valsWithTrivialInitializer: Set<VariableDescriptor>,
val nonTrivialVariables: Set<VariableDescriptor> val nonTrivialVariables: Set<VariableDescriptor>
) { ) {
val allVars = val allVars =
if (nonTrivialVariables.isEmpty()) if (nonTrivialVariables.isEmpty())
valsWithTrivialInitializer valsWithTrivialInitializer
else else
LinkedHashSet(valsWithTrivialInitializer).also { it.addAll(nonTrivialVariables) } LinkedHashSet(valsWithTrivialInitializer).also { it.addAll(nonTrivialVariables) }
} }
private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, VariablesForDeclaration>() private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, VariablesForDeclaration>()
@@ -65,10 +65,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
get() = pseudocodeVariableDataCollector.blockScopeVariableInfo get() = pseudocodeVariableDataCollector.blockScopeVariableInfo
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> = fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> =
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations).allVars getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations).allVars
fun isVariableWithTrivialInitializer(variableDescriptor: VariableDescriptor) = fun isVariableWithTrivialInitializer(variableDescriptor: VariableDescriptor) =
variableDescriptor in rootVariables.valsWithTrivialInitializer variableDescriptor in rootVariables.valsWithTrivialInitializer
private fun getAllDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): VariablesForDeclaration { private fun getAllDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): VariablesForDeclaration {
if (!includeInsideLocalDeclarations) { if (!includeInsideLocalDeclarations) {
@@ -86,9 +86,9 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
private fun addVariablesFromPseudocode( private fun addVariablesFromPseudocode(
pseudocode: Pseudocode, pseudocode: Pseudocode,
nonTrivialVariables: MutableSet<VariableDescriptor>, nonTrivialVariables: MutableSet<VariableDescriptor>,
valsWithTrivialInitializer: MutableSet<VariableDescriptor> valsWithTrivialInitializer: MutableSet<VariableDescriptor>
) { ) {
getUpperLevelDeclaredVariables(pseudocode).let { getUpperLevelDeclaredVariables(pseudocode).let {
nonTrivialVariables.addAll(it.nonTrivialVariables) nonTrivialVariables.addAll(it.nonTrivialVariables)
@@ -107,14 +107,13 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
if (instruction is VariableDeclarationInstruction) { if (instruction is VariableDeclarationInstruction) {
val variableDeclarationElement = instruction.variableDeclarationElement val variableDeclarationElement = instruction.variableDeclarationElement
val descriptor = val descriptor =
variableDescriptorForDeclaration( variableDescriptorForDeclaration(
bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
) ?: continue ) ?: continue
if (!containsDoWhile && isValWithTrivialInitializer(variableDeclarationElement, descriptor)) { if (!containsDoWhile && isValWithTrivialInitializer(variableDeclarationElement, descriptor)) {
valsWithTrivialInitializer.add(descriptor) valsWithTrivialInitializer.add(descriptor)
} } else {
else {
nonTrivialVariables.add(descriptor) nonTrivialVariables.add(descriptor)
} }
} }
@@ -124,8 +123,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
private fun isValWithTrivialInitializer(variableDeclarationElement: KtDeclaration, descriptor: VariableDescriptor) = private fun isValWithTrivialInitializer(variableDeclarationElement: KtDeclaration, descriptor: VariableDescriptor) =
variableDeclarationElement is KtParameter || variableDeclarationElement is KtObjectDeclaration || variableDeclarationElement is KtParameter || variableDeclarationElement is KtObjectDeclaration ||
variableDeclarationElement.safeAs<KtVariableDeclaration>()?.isVariableWithTrivialInitializer(descriptor) == true variableDeclarationElement.safeAs<KtVariableDeclaration>()?.isVariableWithTrivialInitializer(descriptor) == true
private fun KtVariableDeclaration.isVariableWithTrivialInitializer(descriptor: VariableDescriptor): Boolean { private fun KtVariableDeclaration.isVariableWithTrivialInitializer(descriptor: VariableDescriptor): Boolean {
if (descriptor.isPropertyWithoutBackingField()) return true if (descriptor.isPropertyWithoutBackingField()) return true
@@ -148,18 +147,20 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForValsWithTrivialInitializer if (rootVariables.nonTrivialVariables.isEmpty()) return resultForValsWithTrivialInitializer
return pseudocodeVariableDataCollector.collectData(TraversalOrder.FORWARD, InitControlFlowInfo()) { return pseudocodeVariableDataCollector.collectData(
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> -> TraversalOrder.FORWARD,
InitControlFlowInfo()
) { instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
val enterInstructionData = mergeIncomingEdgesDataForInitializers(instruction, incomingEdgesData, blockScopeVariableInfo) val enterInstructionData = mergeIncomingEdgesDataForInitializers(instruction, incomingEdgesData, blockScopeVariableInfo)
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny( val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
instruction, enterInstructionData, blockScopeVariableInfo) instruction, enterInstructionData, blockScopeVariableInfo
)
Edges(enterInstructionData, exitInstructionData) Edges(enterInstructionData, exitInstructionData)
}.mapValues { }.mapValues { (instruction, edges) ->
(instruction, edges) -> val trivialEdges = resultForValsWithTrivialInitializer[instruction]!!
val trivialEdges = resultForValsWithTrivialInitializer[instruction]!! Edges(trivialEdges.incoming.replaceDelegate(edges.incoming), trivialEdges.outgoing.replaceDelegate(edges.outgoing))
Edges(trivialEdges.incoming.replaceDelegate(edges.incoming), trivialEdges.outgoing.replaceDelegate(edges.outgoing)) }
}
} }
private fun computeInitInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>> { private fun computeInitInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>> {
@@ -170,8 +171,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
val enterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null) val enterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null)
when (instruction) { when (instruction) {
is VariableDeclarationInstruction -> is VariableDeclarationInstruction ->
extractValWithTrivialInitializer(instruction)?.let { extractValWithTrivialInitializer(instruction)?.let { variableDescriptor ->
variableDescriptor ->
declaredSet = declaredSet.add(variableDescriptor) declaredSet = declaredSet.add(variableDescriptor)
} }
is WriteValueInstruction -> { is WriteValueInstruction -> {
@@ -193,12 +193,12 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
// WriteValueInstruction having KtDeclaration as an element means // WriteValueInstruction having KtDeclaration as an element means
// it must be a write happened at the same time when // it must be a write happened at the same time when
// the variable (common variable/parameter/object) has been declared // the variable (common variable/parameter/object) has been declared
element is KtDeclaration element is KtDeclaration
private inner class ReadOnlyInitControlFlowInfoImpl( private inner class ReadOnlyInitControlFlowInfoImpl(
val declaredSet: ImmutableSet<VariableDescriptor>, val declaredSet: ImmutableSet<VariableDescriptor>,
val initSet: ImmutableSet<VariableDescriptor>, val initSet: ImmutableSet<VariableDescriptor>,
private val delegate: ReadOnlyInitControlFlowInfo? private val delegate: ReadOnlyInitControlFlowInfo?
) : ReadOnlyInitControlFlowInfo { ) : ReadOnlyInitControlFlowInfo {
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableControlFlowState? { override fun getOrNull(variableDescriptor: VariableDescriptor): VariableControlFlowState? {
if (variableDescriptor in declaredSet) { if (variableDescriptor in declaredSet) {
@@ -208,16 +208,15 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean = override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean =
delegate?.checkDefiniteInitializationInWhen(merge) ?: false delegate?.checkDefiniteInitializationInWhen(merge) ?: false
fun replaceDelegate(newDelegate: ReadOnlyInitControlFlowInfo): ReadOnlyInitControlFlowInfo = fun replaceDelegate(newDelegate: ReadOnlyInitControlFlowInfo): ReadOnlyInitControlFlowInfo =
ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, newDelegate) ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, newDelegate)
override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> { override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> {
val initial = delegate?.asMap() ?: ImmutableHashMap.empty() val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
return declaredSet.fold(initial) { return declaredSet.fold(initial) { acc, variableDescriptor ->
acc, variableDescriptor ->
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!) acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
} }
} }
@@ -244,17 +243,16 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
private fun addVariableInitStateFromCurrentInstructionIfAny( private fun addVariableInitStateFromCurrentInstructionIfAny(
instruction: Instruction, instruction: Instruction,
enterInstructionData: InitControlFlowInfo, enterInstructionData: InitControlFlowInfo,
blockScopeVariableInfo: BlockScopeVariableInfo): InitControlFlowInfo { blockScopeVariableInfo: BlockScopeVariableInfo
): InitControlFlowInfo {
if (instruction is MagicInstruction) { if (instruction is MagicInstruction) {
if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) { if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
return enterInstructionData.iterator().fold(enterInstructionData) { return enterInstructionData.iterator().fold(enterInstructionData) { result, (key, value) ->
result, (key, value) ->
if (!value.definitelyInitialized()) { if (!value.definitelyInitialized()) {
result.put(key, VariableControlFlowState.createInitializedExhaustively(value.isDeclared)) result.put(key, VariableControlFlowState.createInitializedExhaustively(value.isDeclared))
} } else result
else result
} }
} }
} }
@@ -262,9 +260,9 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return enterInstructionData return enterInstructionData
} }
val variable = val variable =
PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext) PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext)
?.takeIf { it in rootVariables.nonTrivialVariables } ?.takeIf { it in rootVariables.nonTrivialVariables }
?: return enterInstructionData ?: return enterInstructionData
var exitInstructionData = enterInstructionData var exitInstructionData = enterInstructionData
if (instruction is WriteValueInstruction) { if (instruction is WriteValueInstruction) {
// if writing to already initialized object // if writing to already initialized object
@@ -275,12 +273,11 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
val enterInitState = enterInstructionData.getOrNull(variable) val enterInitState = enterInstructionData.getOrNull(variable)
val initializationAtThisElement = VariableControlFlowState.create(instruction.element is KtProperty, enterInitState) val initializationAtThisElement = VariableControlFlowState.create(instruction.element is KtProperty, enterInitState)
exitInstructionData = exitInstructionData.put(variable, initializationAtThisElement, enterInitState) exitInstructionData = exitInstructionData.put(variable, initializationAtThisElement, enterInitState)
} } else {
else {
// instruction instanceof VariableDeclarationInstruction // instruction instanceof VariableDeclarationInstruction
val enterInitState = val enterInitState =
enterInstructionData.getOrNull(variable) enterInstructionData.getOrNull(variable)
?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo) ?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) { if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true) val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true)
@@ -303,12 +300,14 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
} }
return pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> -> return pseudocodeVariableDataCollector.collectData(
TraversalOrder.BACKWARD,
UseControlFlowInfo()
) { instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) { val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
incomingEdgesData.single() incomingEdgesData.single()
} } else {
else {
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData -> incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) -> edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor))) subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
@@ -317,38 +316,35 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
val variableDescriptor = val variableDescriptor =
PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext) PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
?.takeIf { it in rootVariables.nonTrivialVariables } ?.takeIf { it in rootVariables.nonTrivialVariables }
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) { if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
Edges(enterResult, enterResult) Edges(enterResult, enterResult)
} } else {
else {
val exitResult = val exitResult =
if (instruction is ReadValueInstruction) { if (instruction is ReadValueInstruction) {
enterResult.put(variableDescriptor, VariableUseState.READ) enterResult.put(variableDescriptor, VariableUseState.READ)
} else {
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
if (variableUseState == null) {
variableUseState = VariableUseState.UNUSED
} }
else { when (variableUseState) {
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor) VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
if (variableUseState == null) { enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
variableUseState = VariableUseState.UNUSED VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
} enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
when (variableUseState) {
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
}
} }
}
Edges(enterResult, exitResult) Edges(enterResult, exitResult)
} }
}.mapValues { }.mapValues { (_, edges) ->
(_, edges) ->
Edges( Edges(
edgesForTrivialVals.incoming.replaceDelegate(edges.incoming), edgesForTrivialVals.incoming.replaceDelegate(edges.incoming),
edgesForTrivialVals.outgoing.replaceDelegate(edges.outgoing) edgesForTrivialVals.outgoing.replaceDelegate(edges.outgoing)
) )
} }
} }
private fun computeUseInfoForTrivialVals(): Edges<ReadOnlyUseControlFlowInfoImpl> { private fun computeUseInfoForTrivialVals(): Edges<ReadOnlyUseControlFlowInfoImpl> {
@@ -373,8 +369,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
private inner class ReadOnlyUseControlFlowInfoImpl( private inner class ReadOnlyUseControlFlowInfoImpl(
val used: Set<VariableDescriptor>, val used: Set<VariableDescriptor>,
val delegate: ReadOnlyUseControlFlowInfo? val delegate: ReadOnlyUseControlFlowInfo?
) : ReadOnlyUseControlFlowInfo { ) : ReadOnlyUseControlFlowInfo {
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableUseState? { override fun getOrNull(variableDescriptor: VariableDescriptor): VariableUseState? {
if (variableDescriptor in used) return VariableUseState.READ if (variableDescriptor in used) return VariableUseState.READ
@@ -382,13 +378,12 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
fun replaceDelegate(newDelegate: ReadOnlyUseControlFlowInfo): ReadOnlyUseControlFlowInfo = fun replaceDelegate(newDelegate: ReadOnlyUseControlFlowInfo): ReadOnlyUseControlFlowInfo =
ReadOnlyUseControlFlowInfoImpl(used, newDelegate) ReadOnlyUseControlFlowInfoImpl(used, newDelegate)
override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> { override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> {
val initial = delegate?.asMap() ?: ImmutableHashMap.empty() val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
return used.fold(initial) { return used.fold(initial) { acc, variableDescriptor ->
acc, variableDescriptor ->
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!) acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
} }
} }
@@ -417,24 +412,24 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
@JvmStatic @JvmStatic
fun getDefaultValueForInitializers( fun getDefaultValueForInitializers(
variable: VariableDescriptor, variable: VariableDescriptor,
instruction: Instruction, instruction: Instruction,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
): VariableControlFlowState { ): VariableControlFlowState {
//todo: think of replacing it with "MapWithDefaultValue" //todo: think of replacing it with "MapWithDefaultValue"
val declaredIn = blockScopeVariableInfo.declaredIn[variable] val declaredIn = blockScopeVariableInfo.declaredIn[variable]
val declaredOutsideThisDeclaration = val declaredOutsideThisDeclaration =
declaredIn == null //declared outside this pseudocode declaredIn == null //declared outside this pseudocode
|| declaredIn.blockScopeForContainingDeclaration != instruction.blockScope.blockScopeForContainingDeclaration || declaredIn.blockScopeForContainingDeclaration != instruction.blockScope.blockScopeForContainingDeclaration
return VariableControlFlowState.create(isInitialized = declaredOutsideThisDeclaration) return VariableControlFlowState.create(isInitialized = declaredOutsideThisDeclaration)
} }
private val EMPTY_INIT_CONTROL_FLOW_INFO = InitControlFlowInfo() private val EMPTY_INIT_CONTROL_FLOW_INFO = InitControlFlowInfo()
private fun mergeIncomingEdgesDataForInitializers( private fun mergeIncomingEdgesDataForInitializers(
instruction: Instruction, instruction: Instruction,
incomingEdgesData: Collection<InitControlFlowInfo>, incomingEdgesData: Collection<InitControlFlowInfo>,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
): InitControlFlowInfo { ): InitControlFlowInfo {
if (incomingEdgesData.size == 1) return incomingEdgesData.single() if (incomingEdgesData.size == 1) return incomingEdgesData.single()
if (incomingEdgesData.isEmpty()) return EMPTY_INIT_CONTROL_FLOW_INFO if (incomingEdgesData.isEmpty()) return EMPTY_INIT_CONTROL_FLOW_INFO
@@ -448,7 +443,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
var isDeclared = true var isDeclared = true
for (edgeData in incomingEdgesData) { for (edgeData in incomingEdgesData) {
val varControlFlowState = edgeData.getOrNull(variable) val varControlFlowState = edgeData.getOrNull(variable)
?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo) ?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState
if (!varControlFlowState.isDeclared) { if (!varControlFlowState.isDeclared) {
isDeclared = false isDeclared = false
@@ -33,8 +33,8 @@ interface UnreachableCode {
} }
class UnreachableCodeImpl( class UnreachableCodeImpl(
private val reachableElements: Set<KtElement>, private val reachableElements: Set<KtElement>,
private val unreachableElements: Set<KtElement> private val unreachableElements: Set<KtElement>
) : UnreachableCode { ) : UnreachableCode {
// This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
@@ -42,30 +42,29 @@ class UnreachableCodeImpl(
override fun getUnreachableTextRanges(element: KtElement): List<TextRange> { override fun getUnreachableTextRanges(element: KtElement): List<TextRange> {
return if (element.hasChildrenInSet(reachableElements)) { return if (element.hasChildrenInSet(reachableElements)) {
with (element.getLeavesOrReachableChildren().removeReachableElementsWithMeaninglessSiblings().mergeAdjacentTextRanges()) { with(element.getLeavesOrReachableChildren().removeReachableElementsWithMeaninglessSiblings().mergeAdjacentTextRanges()) {
if (isNotEmpty()) this if (isNotEmpty()) this
// Specific case like condition in when: // Specific case like condition in when:
// element is dead but its only child is alive and has the same text range // element is dead but its only child is alive and has the same text range
else listOf(element.textRange.endOffset.let { TextRange(it, it) }) else listOf(element.textRange.endOffset.let { TextRange(it, it) })
} }
} } else {
else {
listOf(element.textRange!!) listOf(element.textRange!!)
} }
} }
private fun KtElement.hasChildrenInSet(set: Set<KtElement>): Boolean = private fun KtElement.hasChildrenInSet(set: Set<KtElement>): Boolean =
PsiTreeUtil.collectElements(this) { it != this }.any { it in set } PsiTreeUtil.collectElements(this) { it != this }.any { it in set }
private fun KtElement.getLeavesOrReachableChildren(): List<PsiElement> { private fun KtElement.getLeavesOrReachableChildren(): List<PsiElement> {
val children = ArrayList<PsiElement>() val children = ArrayList<PsiElement>()
acceptChildren(object : PsiElementVisitor() { acceptChildren(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
val isReachable = element is KtElement && reachableElements.contains(element) && !element.hasChildrenInSet(unreachableElements) val isReachable =
element is KtElement && reachableElements.contains(element) && !element.hasChildrenInSet(unreachableElements)
if (isReachable || element.children.isEmpty()) { if (isReachable || element.children.isEmpty()) {
children.add(element) children.add(element)
} } else {
else {
element.acceptChildren(this) element.acceptChildren(this)
} }
} }
@@ -102,8 +101,7 @@ class UnreachableCodeImpl(
private fun List<PsiElement>.mergeAdjacentTextRanges(): List<TextRange> { private fun List<PsiElement>.mergeAdjacentTextRanges(): List<TextRange> {
val result = ArrayList<TextRange>() val result = ArrayList<TextRange>()
val lastRange = fold(null as TextRange?) { val lastRange = fold(null as TextRange?) { currentTextRange, element ->
currentTextRange, element ->
val elementRange = element.textRange!! val elementRange = element.textRange!!
when { when {
@@ -54,10 +54,10 @@ val List<WhenMissingCase>.hasUnknown: Boolean
private interface WhenExhaustivenessChecker { private interface WhenExhaustivenessChecker {
fun getMissingCases( fun getMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
subjectDescriptor: ClassDescriptor?, subjectDescriptor: ClassDescriptor?,
nullable: Boolean nullable: Boolean
): List<WhenMissingCase> ): List<WhenMissingCase>
fun isApplicable(subjectType: KotlinType): Boolean = false fun isApplicable(subjectType: KotlinType): Boolean = false
@@ -72,7 +72,7 @@ object NullMissingCase : WhenMissingCase() {
// It's not a regular exhaustiveness checker, invoke it only inside other checkers // It's not a regular exhaustiveness checker, invoke it only inside other checkers
private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ { private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ {
fun getMissingCases(expression: KtWhenExpression, context: BindingContext, nullable: Boolean) = fun getMissingCases(expression: KtWhenExpression, context: BindingContext, nullable: Boolean) =
if (nullable) getNullCaseIfMissing(expression, context) else listOf() if (nullable) getNullCaseIfMissing(expression, context) else listOf()
private fun getNullCaseIfMissing(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> { private fun getNullCaseIfMissing(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> {
for (entry in expression.entries) { for (entry in expression.entries) {
@@ -99,10 +99,10 @@ class BooleanMissingCase(val b: Boolean) : WhenMissingCase() {
private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker { private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
override fun getMissingCases( override fun getMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
subjectDescriptor: ClassDescriptor?, subjectDescriptor: ClassDescriptor?,
nullable: Boolean nullable: Boolean
): List<WhenMissingCase> { ): List<WhenMissingCase> {
// It's assumed (and not checked) that expression is of the boolean type // It's assumed (and not checked) that expression is of the boolean type
var containsFalse = false var containsFalse = false
@@ -117,8 +117,8 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
} }
} }
return (if (!containsTrue) listOf(BooleanMissingCase(true)) else listOf()) + return (if (!containsTrue) listOf(BooleanMissingCase(true)) else listOf()) +
(if (!containsFalse) listOf(BooleanMissingCase(false)) else listOf()) + (if (!containsFalse) listOf(BooleanMissingCase(false)) else listOf()) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable) WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
} }
override fun isApplicable(subjectType: KotlinType): Boolean { override fun isApplicable(subjectType: KotlinType): Boolean {
@@ -126,7 +126,7 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
} }
} }
class ClassMissingCase(val descriptor: ClassDescriptor): WhenMissingCase() { class ClassMissingCase(val descriptor: ClassDescriptor) : WhenMissingCase() {
val classIsSingleton get() = descriptor.kind.isSingleton val classIsSingleton get() = descriptor.kind.isSingleton
val classFqName get() = DescriptorUtils.getFqNameFromTopLevelClass(descriptor) val classFqName get() = DescriptorUtils.getFqNameFromTopLevelClass(descriptor)
@@ -138,11 +138,11 @@ class ClassMissingCase(val descriptor: ClassDescriptor): WhenMissingCase() {
internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker { internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker {
private fun getReference(expression: KtExpression?): KtSimpleNameExpression? = private fun getReference(expression: KtExpression?): KtSimpleNameExpression? =
when (expression) { when (expression) {
is KtSimpleNameExpression -> expression is KtSimpleNameExpression -> expression
is KtQualifiedExpression -> getReference(expression.selectorExpression) is KtQualifiedExpression -> getReference(expression.selectorExpression)
else -> null else -> null
} }
protected val ClassDescriptor.deepSealedSubclasses: List<ClassDescriptor> protected val ClassDescriptor.deepSealedSubclasses: List<ClassDescriptor>
get() = this.sealedSubclasses.flatMap { get() = this.sealedSubclasses.flatMap {
@@ -154,9 +154,9 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
get() = (this as? KtWhenConditionIsPattern)?.isNegated ?: false get() = (this as? KtWhenConditionIsPattern)?.isNegated ?: false
private fun KtWhenCondition.isRelevant(checkedDescriptor: ClassDescriptor) = private fun KtWhenCondition.isRelevant(checkedDescriptor: ClassDescriptor) =
this !is KtWhenConditionWithExpression || this !is KtWhenConditionWithExpression ||
DescriptorUtils.isObject(checkedDescriptor) || DescriptorUtils.isObject(checkedDescriptor) ||
DescriptorUtils.isEnumEntry(checkedDescriptor) DescriptorUtils.isEnumEntry(checkedDescriptor)
private fun KtWhenCondition.getCheckedDescriptor(context: BindingContext): ClassDescriptor? { private fun KtWhenCondition.getCheckedDescriptor(context: BindingContext): ClassDescriptor? {
return when (this) { return when (this) {
@@ -175,9 +175,9 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
} }
protected fun getMissingClassCases( protected fun getMissingClassCases(
whenExpression: KtWhenExpression, whenExpression: KtWhenExpression,
subclasses: Set<ClassDescriptor>, subclasses: Set<ClassDescriptor>,
context: BindingContext context: BindingContext
): List<WhenMissingCase> { ): List<WhenMissingCase> {
// when on empty enum / sealed is considered non-exhaustive, see test whenOnEmptySealed // when on empty enum / sealed is considered non-exhaustive, see test whenOnEmptySealed
if (subclasses.isEmpty()) return listOf(UnknownMissingCase) if (subclasses.isEmpty()) return listOf(UnknownMissingCase)
@@ -188,8 +188,8 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
val negated = condition.negated val negated = condition.negated
val checkedDescriptor = condition.getCheckedDescriptor(context) ?: continue val checkedDescriptor = condition.getCheckedDescriptor(context) ?: continue
val checkedDescriptorSubclasses = val checkedDescriptorSubclasses =
if (checkedDescriptor.modality == Modality.SEALED) checkedDescriptor.deepSealedSubclasses if (checkedDescriptor.modality == Modality.SEALED) checkedDescriptor.deepSealedSubclasses
else listOf(checkedDescriptor) else listOf(checkedDescriptor)
// Checks are important only for nested subclasses of the sealed class // Checks are important only for nested subclasses of the sealed class
// In additional, check without "is" is important only for objects // In additional, check without "is" is important only for objects
@@ -201,8 +201,7 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
if (checkedDescriptors.containsAll(checkedDescriptorSubclasses)) return listOf() if (checkedDescriptors.containsAll(checkedDescriptorSubclasses)) return listOf()
checkedDescriptors.addAll(subclasses) checkedDescriptors.addAll(subclasses)
checkedDescriptors.removeAll(checkedDescriptorSubclasses) checkedDescriptors.removeAll(checkedDescriptorSubclasses)
} } else {
else {
checkedDescriptors.addAll(checkedDescriptorSubclasses) checkedDescriptors.addAll(checkedDescriptorSubclasses)
} }
} }
@@ -213,19 +212,19 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecker() { private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecker() {
override fun getMissingCases( override fun getMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
subjectDescriptor: ClassDescriptor?, subjectDescriptor: ClassDescriptor?,
nullable: Boolean nullable: Boolean
): List<WhenMissingCase> { ): List<WhenMissingCase> {
assert(isEnumClass(subjectDescriptor)) { "isWhenOnEnumExhaustive should be called with an enum class descriptor" } assert(isEnumClass(subjectDescriptor)) { "isWhenOnEnumExhaustive should be called with an enum class descriptor" }
val entryDescriptors = val entryDescriptors =
DescriptorUtils.getAllDescriptors(subjectDescriptor!!.unsubstitutedInnerClassesScope) DescriptorUtils.getAllDescriptors(subjectDescriptor!!.unsubstitutedInnerClassesScope)
.filter(::isEnumEntry) .filter(::isEnumEntry)
.filterIsInstance<ClassDescriptor>() .filterIsInstance<ClassDescriptor>()
.toSet() .toSet()
return getMissingClassCases(expression, entryDescriptors, context) + return getMissingClassCases(expression, entryDescriptors, context) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable) WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
} }
override fun isApplicable(subjectType: KotlinType): Boolean { override fun isApplicable(subjectType: KotlinType): Boolean {
@@ -236,10 +235,10 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke
internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChecker() { internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChecker() {
override fun getMissingCases( override fun getMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
subjectDescriptor: ClassDescriptor?, subjectDescriptor: ClassDescriptor?,
nullable: Boolean nullable: Boolean
): List<WhenMissingCase> { ): List<WhenMissingCase> {
assert(DescriptorUtils.isSealedClass(subjectDescriptor)) { assert(DescriptorUtils.isSealedClass(subjectDescriptor)) {
"isWhenOnSealedClassExhaustive should be called with a sealed class descriptor: $subjectDescriptor" "isWhenOnSealedClassExhaustive should be called with a sealed class descriptor: $subjectDescriptor"
@@ -247,7 +246,7 @@ internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChe
val allSubclasses = subjectDescriptor!!.deepSealedSubclasses val allSubclasses = subjectDescriptor!!.deepSealedSubclasses
return getMissingClassCases(expression, allSubclasses.toSet(), context) + return getMissingClassCases(expression, allSubclasses.toSet(), context) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable) WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
} }
override fun isApplicable(subjectType: KotlinType): Boolean { override fun isApplicable(subjectType: KotlinType): Boolean {
@@ -258,13 +257,15 @@ internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChe
object WhenChecker { object WhenChecker {
private val exhaustivenessCheckers = listOf(WhenOnBooleanExhaustivenessChecker, private val exhaustivenessCheckers = listOf(
WhenOnEnumExhaustivenessChecker, WhenOnBooleanExhaustivenessChecker,
WhenOnSealedExhaustivenessChecker) WhenOnEnumExhaustivenessChecker,
WhenOnSealedExhaustivenessChecker
)
@JvmStatic @JvmStatic
fun isWhenByEnum(expression: KtWhenExpression, context: BindingContext) = fun isWhenByEnum(expression: KtWhenExpression, context: BindingContext) =
getClassDescriptorOfTypeIfEnum(whenSubjectType(expression, context)) != null getClassDescriptorOfTypeIfEnum(whenSubjectType(expression, context)) != null
@JvmStatic @JvmStatic
fun getClassDescriptorOfTypeIfEnum(type: KotlinType?): ClassDescriptor? { fun getClassDescriptorOfTypeIfEnum(type: KotlinType?): ClassDescriptor? {
@@ -276,26 +277,26 @@ object WhenChecker {
} }
@JvmStatic @JvmStatic
fun getClassDescriptorOfTypeIfSealed(type: KotlinType?): ClassDescriptor? fun getClassDescriptorOfTypeIfSealed(type: KotlinType?): ClassDescriptor? =
= type?.let { TypeUtils.getClassDescriptor(it) }?.takeIf { DescriptorUtils.isSealedClass(it) } type?.let { TypeUtils.getClassDescriptor(it) }?.takeIf { DescriptorUtils.isSealedClass(it) }
@JvmStatic @JvmStatic
fun whenSubjectType(expression: KtWhenExpression, context: BindingContext) = fun whenSubjectType(expression: KtWhenExpression, context: BindingContext) =
expression.subjectExpression?.let { context.get(SMARTCAST, it)?.defaultType ?: context.getType(it) } expression.subjectExpression?.let { context.get(SMARTCAST, it)?.defaultType ?: context.getType(it) }
@JvmStatic @JvmStatic
fun getEnumMissingCases( fun getEnumMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
enumClassDescriptor: ClassDescriptor enumClassDescriptor: ClassDescriptor
) = WhenOnEnumExhaustivenessChecker.getMissingCases(expression, context, enumClassDescriptor, false) ) = WhenOnEnumExhaustivenessChecker.getMissingCases(expression, context, enumClassDescriptor, false)
@JvmStatic @JvmStatic
fun getSealedMissingCases( fun getSealedMissingCases(
expression: KtWhenExpression, expression: KtWhenExpression,
context: BindingContext, context: BindingContext,
sealedClassDescriptor: ClassDescriptor sealedClassDescriptor: ClassDescriptor
) = WhenOnSealedExhaustivenessChecker.getMissingCases(expression, context, sealedClassDescriptor, false) ) = WhenOnSealedExhaustivenessChecker.getMissingCases(expression, context, sealedClassDescriptor, false)
fun getMissingCases(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> { fun getMissingCases(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> {
@@ -308,15 +309,15 @@ object WhenChecker {
@JvmStatic @JvmStatic
fun isWhenExhaustive(expression: KtWhenExpression, trace: BindingTrace) = fun isWhenExhaustive(expression: KtWhenExpression, trace: BindingTrace) =
if (getMissingCases(expression, trace.bindingContext).isEmpty()) { if (getMissingCases(expression, trace.bindingContext).isEmpty()) {
trace.record(BindingContext.EXHAUSTIVE_WHEN, expression) trace.record(BindingContext.EXHAUSTIVE_WHEN, expression)
true true
} else { } else {
false false
} }
fun containsNullCase(expression: KtWhenExpression, context: BindingContext) = fun containsNullCase(expression: KtWhenExpression, context: BindingContext) =
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty() WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty()
fun checkDuplicatedLabels(expression: KtWhenExpression, trace: BindingTrace) { fun checkDuplicatedLabels(expression: KtWhenExpression, trace: BindingTrace) {
if (expression.subjectExpression == null) return if (expression.subjectExpression == null) return
@@ -331,11 +332,11 @@ object WhenChecker {
is KtWhenConditionWithExpression -> { is KtWhenConditionWithExpression -> {
val constantExpression = condition.expression ?: continue@conditions val constantExpression = condition.expression ?: continue@conditions
val constant = ConstantExpressionEvaluator.getConstant( val constant = ConstantExpressionEvaluator.getConstant(
constantExpression, trace.bindingContext) ?: continue@conditions constantExpression, trace.bindingContext
) ?: continue@conditions
if (checkedConstants.contains(constant)) { if (checkedConstants.contains(constant)) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(constantExpression)) trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(constantExpression))
} } else {
else {
checkedConstants.add(constant) checkedConstants.add(constant)
} }
@@ -346,12 +347,12 @@ object WhenChecker {
val typeWithIsNegation = type to condition.isNegated val typeWithIsNegation = type to condition.isNegated
if (checkedTypes.contains(typeWithIsNegation)) { if (checkedTypes.contains(typeWithIsNegation)) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(typeReference)) trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(typeReference))
} } else {
else {
checkedTypes.add(typeWithIsNegation) checkedTypes.add(typeWithIsNegation)
} }
} }
else -> {} else -> {
}
} }
} }
} }
@@ -58,8 +58,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
val worker = builders.pop() val worker = builders.pop()
builder = if (!builders.isEmpty()) { builder = if (!builders.isEmpty()) {
builders.peek() builders.peek()
} } else {
else {
null null
} }
return worker return worker
@@ -70,8 +69,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
val shouldInlnie = invocationKind != null val shouldInlnie = invocationKind != null
if (builder != null && subroutine is KtFunctionLiteral) { if (builder != null && subroutine is KtFunctionLiteral) {
pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie) pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie)
} } else {
else {
pushBuilder(subroutine, subroutine, shouldInlnie) pushBuilder(subroutine, subroutine, shouldInlnie)
} }
delegateBuilder.enterBlockScope(subroutine) delegateBuilder.enterBlockScope(subroutine)
@@ -86,8 +84,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
val builder = builders.peek() val builder = builders.peek()
if (invocationKind == null) { if (invocationKind == null) {
builder.declareFunction(subroutine, worker.pseudocode) builder.declareFunction(subroutine, worker.pseudocode)
} } else {
else {
builder.declareInlinedFunction(subroutine, worker.pseudocode, invocationKind) builder.declareInlinedFunction(subroutine, worker.pseudocode, invocationKind)
} }
} }
@@ -95,9 +92,9 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
} }
private inner class ControlFlowInstructionsGeneratorWorker( private inner class ControlFlowInstructionsGeneratorWorker(
scopingElement: KtElement, scopingElement: KtElement,
override val returnSubroutine: KtElement, override val returnSubroutine: KtElement,
shouldInline: Boolean shouldInline: Boolean
) : ControlFlowBuilder { ) : ControlFlowBuilder {
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline) val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline)
@@ -128,12 +125,13 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
} }
val info = LoopInfo( val info = LoopInfo(
expression, expression,
createUnboundLabel("loop entry point"), createUnboundLabel("loop entry point"),
createUnboundLabel("loop exit point"), createUnboundLabel("loop exit point"),
createUnboundLabel("body entry point"), createUnboundLabel("body entry point"),
createUnboundLabel("body exit point"), createUnboundLabel("body exit point"),
createUnboundLabel("condition entry point")) createUnboundLabel("condition entry point")
)
bindLabel(info.entryPoint) bindLabel(info.entryPoint)
elementToLoopInfo.put(expression, info) elementToLoopInfo.put(expression, info)
return info return info
@@ -158,9 +156,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) { override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
val blockInfo = SubroutineInfo( val blockInfo = SubroutineInfo(
subroutine, subroutine,
/* entry point */ createUnboundLabel(), /* entry point */ createUnboundLabel(),
/* exit point */ createUnboundLabel()) /* exit point */ createUnboundLabel()
)
elementToSubroutineInfo.put(subroutine, blockInfo) elementToSubroutineInfo.put(subroutine, blockInfo)
allBlocks.push(blockInfo) allBlocks.push(blockInfo)
bindLabel(blockInfo.entryPoint) bindLabel(blockInfo.entryPoint)
@@ -173,10 +172,11 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
override fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label? = elementToLoopInfo[loop]?.conditionEntryPoint override fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label? = elementToLoopInfo[loop]?.conditionEntryPoint
override fun getLoopExitPoint(loop: KtLoopExpression): Label? =// It's quite possible to have null here, see testBreakInsideLocal override fun getLoopExitPoint(loop: KtLoopExpression): Label? =// It's quite possible to have null here, see testBreakInsideLocal
elementToLoopInfo[loop]?.exitPoint elementToLoopInfo[loop]?.exitPoint
override fun getSubroutineExitPoint(labelElement: KtElement): Label? =// It's quite possible to have null here, e.g. for non-local returns (see KT-10823) override fun getSubroutineExitPoint(labelElement: KtElement): Label? =
elementToSubroutineInfo[labelElement]?.exitPoint // It's quite possible to have null here, e.g. for non-local returns (see KT-10823)
elementToSubroutineInfo[labelElement]?.exitPoint
private val currentScope: BlockScope private val currentScope: BlockScope
get() = blockScopes.peek() get() = blockScopes.peek()
@@ -191,8 +191,8 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
val currentScope = currentScope val currentScope = currentScope
assert(currentScope.block === block) { assert(currentScope.block === block) {
"Exit from not the current block scope.\n" + "Exit from not the current block scope.\n" +
"Current scope is for a block: " + currentScope.block.text + ".\n" + "Current scope is for a block: " + currentScope.block.text + ".\n" +
"Exit from the scope for: " + block.text "Exit from the scope for: " + block.text
} }
blockScopes.pop() blockScopes.pop()
} }
@@ -252,11 +252,12 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
} }
override fun write( override fun write(
assignment: KtElement, assignment: KtElement,
lValue: KtElement, lValue: KtElement,
rValue: PseudoValue, rValue: PseudoValue,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>) { receiverValues: Map<PseudoValue, ReceiverValue>
) {
add(WriteValueInstruction(assignment, currentScope, target, receiverValues, lValue, rValue)) add(WriteValueInstruction(assignment, currentScope, target, receiverValues, lValue, rValue))
} }
@@ -342,22 +343,24 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
override fun createAnonymousObject(expression: KtObjectLiteralExpression) = read(expression) override fun createAnonymousObject(expression: KtObjectLiteralExpression) = read(expression)
override fun createLambda(expression: KtFunction) = override fun createLambda(expression: KtFunction) =
read(if (expression is KtFunctionLiteral) expression.getParent() as KtLambdaExpression else expression) read(if (expression is KtFunctionLiteral) expression.getParent() as KtLambdaExpression else expression)
override fun loadStringTemplate( override fun loadStringTemplate(
expression: KtStringTemplateExpression, expression: KtStringTemplateExpression,
inputValues: List<PseudoValue> inputValues: List<PseudoValue>
): InstructionWithValue = ): InstructionWithValue =
if (inputValues.isEmpty()) read(expression) if (inputValues.isEmpty()) read(expression)
else magic(expression, expression, inputValues, MagicKind.STRING_TEMPLATE) else magic(expression, expression, inputValues, MagicKind.STRING_TEMPLATE)
override fun magic( override fun magic(
instructionElement: KtElement, instructionElement: KtElement,
valueElement: KtElement?, valueElement: KtElement?,
inputValues: List<PseudoValue>, inputValues: List<PseudoValue>,
kind: MagicKind): MagicInstruction { kind: MagicKind
): MagicInstruction {
val instruction = MagicInstruction( val instruction = MagicInstruction(
instructionElement, valueElement, currentScope, inputValues, kind, valueFactory) instructionElement, valueElement, currentScope, inputValues, kind, valueFactory
)
add(instruction) add(instruction)
return instruction return instruction
} }
@@ -369,32 +372,35 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
} }
override fun readVariable( override fun readVariable(
expression: KtExpression, expression: KtExpression,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue> receiverValues: Map<PseudoValue, ReceiverValue>
) = read(expression, resolvedCall, receiverValues) ) = read(expression, resolvedCall, receiverValues)
override fun call( override fun call(
valueElement: KtElement, valueElement: KtElement,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
arguments: Map<PseudoValue, ValueParameterDescriptor>): CallInstruction { arguments: Map<PseudoValue, ValueParameterDescriptor>
): CallInstruction {
val returnType = resolvedCall.resultingDescriptor.returnType val returnType = resolvedCall.resultingDescriptor.returnType
val instruction = CallInstruction( val instruction = CallInstruction(
valueElement, valueElement,
currentScope, currentScope,
resolvedCall, resolvedCall,
receiverValues, receiverValues,
arguments, arguments,
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) null else valueFactory) if (returnType != null && KotlinBuiltIns.isNothing(returnType)) null else valueFactory
)
add(instruction) add(instruction)
return instruction return instruction
} }
override fun predefinedOperation( override fun predefinedOperation(
expression: KtExpression, expression: KtExpression,
operation: ControlFlowBuilder.PredefinedOperation, operation: ControlFlowBuilder.PredefinedOperation,
inputValues: List<PseudoValue>): OperationInstruction = magic(expression, expression, inputValues, getMagicKind(operation)) inputValues: List<PseudoValue>
): OperationInstruction = magic(expression, expression, inputValues, getMagicKind(operation))
private fun getMagicKind(operation: ControlFlowBuilder.PredefinedOperation) = when (operation) { private fun getMagicKind(operation: ControlFlowBuilder.PredefinedOperation) = when (operation) {
ControlFlowBuilder.PredefinedOperation.AND -> MagicKind.AND ControlFlowBuilder.PredefinedOperation.AND -> MagicKind.AND
@@ -403,17 +409,17 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
} }
override fun read( override fun read(
element: KtElement, element: KtElement,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue> receiverValues: Map<PseudoValue, ReceiverValue>
) = ReadValueInstruction(element, currentScope, target, receiverValues, valueFactory).apply { ) = ReadValueInstruction(element, currentScope, target, receiverValues, valueFactory).apply {
add(this) add(this)
} }
private fun read( private fun read(
expression: KtExpression, expression: KtExpression,
resolvedCall: ResolvedCall<*>? = null, resolvedCall: ResolvedCall<*>? = null,
receiverValues: Map<PseudoValue, ReceiverValue> = emptyMap() receiverValues: Map<PseudoValue, ReceiverValue> = emptyMap()
) = read(expression, if (resolvedCall != null) AccessTarget.Call(resolvedCall) else AccessTarget.BlackBox, receiverValues) ) = read(expression, if (resolvedCall != null) AccessTarget.Call(resolvedCall) else AccessTarget.BlackBox, receiverValues)
} }
@@ -23,28 +23,28 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
interface TypePredicate: (KotlinType) -> Boolean { interface TypePredicate : (KotlinType) -> Boolean {
override fun invoke(typeToCheck: KotlinType): Boolean override fun invoke(typeToCheck: KotlinType): Boolean
} }
data class SingleType(val targetType: KotlinType): TypePredicate { data class SingleType(val targetType: KotlinType) : TypePredicate {
override fun invoke(typeToCheck: KotlinType): Boolean = KotlinTypeChecker.DEFAULT.equalTypes(typeToCheck, targetType) override fun invoke(typeToCheck: KotlinType): Boolean = KotlinTypeChecker.DEFAULT.equalTypes(typeToCheck, targetType)
override fun toString(): String = targetType.render() override fun toString(): String = targetType.render()
} }
data class AllSubtypes(val upperBound: KotlinType): TypePredicate { data class AllSubtypes(val upperBound: KotlinType) : TypePredicate {
override fun invoke(typeToCheck: KotlinType): Boolean = KotlinTypeChecker.DEFAULT.isSubtypeOf(typeToCheck, upperBound) override fun invoke(typeToCheck: KotlinType): Boolean = KotlinTypeChecker.DEFAULT.isSubtypeOf(typeToCheck, upperBound)
override fun toString(): String = "{<: ${upperBound.render()}}" override fun toString(): String = "{<: ${upperBound.render()}}"
} }
data class ForAllTypes(val typeSets: List<TypePredicate>): TypePredicate { data class ForAllTypes(val typeSets: List<TypePredicate>) : TypePredicate {
override fun invoke(typeToCheck: KotlinType): Boolean = typeSets.all { it(typeToCheck) } override fun invoke(typeToCheck: KotlinType): Boolean = typeSets.all { it(typeToCheck) }
override fun toString(): String = "AND{${typeSets.joinToString(", ")}}" override fun toString(): String = "AND{${typeSets.joinToString(", ")}}"
} }
data class ForSomeType(val typeSets: List<TypePredicate>): TypePredicate { data class ForSomeType(val typeSets: List<TypePredicate>) : TypePredicate {
override fun invoke(typeToCheck: KotlinType): Boolean = typeSets.any { it(typeToCheck) } override fun invoke(typeToCheck: KotlinType): Boolean = typeSets.any { it(typeToCheck) }
override fun toString(): String = "OR{${typeSets.joinToString(", ")}}" override fun toString(): String = "OR{${typeSets.joinToString(", ")}}"
@@ -58,18 +58,18 @@ object AllTypes : TypePredicate {
// todo: simplify computed type predicate when possible // todo: simplify computed type predicate when possible
fun and(predicates: Collection<TypePredicate>): TypePredicate = fun and(predicates: Collection<TypePredicate>): TypePredicate =
when (predicates.size) { when (predicates.size) {
0 -> AllTypes 0 -> AllTypes
1 -> predicates.first() 1 -> predicates.first()
else -> ForAllTypes(predicates.toList()) else -> ForAllTypes(predicates.toList())
} }
fun or(predicates: Collection<TypePredicate>): TypePredicate? = fun or(predicates: Collection<TypePredicate>): TypePredicate? =
when (predicates.size) { when (predicates.size) {
0 -> null 0 -> null
1 -> predicates.first() 1 -> predicates.first()
else -> ForSomeType(predicates.toList()) else -> ForSomeType(predicates.toList())
} }
fun KotlinType.getSubtypesPredicate(): TypePredicate = when { fun KotlinType.getSubtypesPredicate(): TypePredicate = when {
KotlinBuiltIns.isAnyOrNullableAny(this) && isMarkedNullable -> AllTypes KotlinBuiltIns.isAnyOrNullableAny(this) && isMarkedNullable -> AllTypes
@@ -81,4 +81,4 @@ fun KotlinType.getSubtypesPredicate(): TypePredicate = when {
private fun KotlinType.render(): String = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(this) private fun KotlinType.render(): String = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(this)
fun <T> TypePredicate.expectedTypeFor(keys: Iterable<T>): Map<T, TypePredicate> = fun <T> TypePredicate.expectedTypeFor(keys: Iterable<T>): Map<T, TypePredicate> =
keys.fold(SmartFMap.emptyMap<T, TypePredicate>()) { map, key -> map.plus(key, this) } keys.fold(SmartFMap.emptyMap<T, TypePredicate>()) { map, key -> map.plus(key, this) }
@@ -24,9 +24,9 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
abstract class AbstractJumpInstruction( abstract class AbstractJumpInstruction(
element: KtElement, element: KtElement,
val targetLabel: Label, val targetLabel: Label,
blockScope: BlockScope blockScope: BlockScope
) : KtElementInstructionImpl(element, blockScope), JumpInstruction { ) : KtElementInstructionImpl(element, blockScope), JumpInstruction {
var resolvedTarget: Instruction? = null var resolvedTarget: Instruction? = null
set(value) { set(value) {
@@ -27,17 +27,17 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
class NondeterministicJumpInstruction( class NondeterministicJumpInstruction(
element: KtElement, element: KtElement,
targetLabels: List<Label>, targetLabels: List<Label>,
blockScope: BlockScope, blockScope: BlockScope,
private val inputValue: PseudoValue? private val inputValue: PseudoValue?
) : KtElementInstructionImpl(element, blockScope), JumpInstruction { ) : KtElementInstructionImpl(element, blockScope), JumpInstruction {
private var _next: Instruction? = null private var _next: Instruction? = null
private val _resolvedTargets: MutableMap<Label, Instruction> = linkedMapOf() private val _resolvedTargets: MutableMap<Label, Instruction> = linkedMapOf()
val targetLabels: List<Label> = ArrayList(targetLabels) val targetLabels: List<Label> = ArrayList(targetLabels)
private val resolvedTargets: Map<Label, Instruction> private val resolvedTargets: Map<Label, Instruction>
get() = _resolvedTargets get() = _resolvedTargets
fun setResolvedTarget(label: Label, resolvedTarget: Instruction) { fun setResolvedTarget(label: Label, resolvedTarget: Instruction) {
_resolvedTargets[label] = outgoingEdgeTo(resolvedTarget)!! _resolvedTargets[label] = outgoingEdgeTo(resolvedTarget)!!
@@ -76,5 +76,5 @@ class NondeterministicJumpInstruction(
fun copy(newTargetLabels: MutableList<Label>): Instruction = updateCopyInfo(createCopy(newTargetLabels)) fun copy(newTargetLabels: MutableList<Label>): Instruction = updateCopyInfo(createCopy(newTargetLabels))
private fun createCopy(newTargetLabels: List<Label>): InstructionImpl = private fun createCopy(newTargetLabels: List<Label>): InstructionImpl =
NondeterministicJumpInstruction(element, newTargetLabels, blockScope, inputValue) NondeterministicJumpInstruction(element, newTargetLabels, blockScope, inputValue)
} }
@@ -63,7 +63,8 @@ fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: Recei
resolvedCall.dispatchReceiver -> { resolvedCall.dispatchReceiver -> {
val rootCallableDescriptors = callableDescriptor.findTopMostOverriddenDescriptors() val rootCallableDescriptors = callableDescriptor.findTopMostOverriddenDescriptors()
return or(rootCallableDescriptors.mapNotNull { return or(rootCallableDescriptors.mapNotNull {
it.dispatchReceiverParameter?.type?.let { TypeUtils.makeNullableIfNeeded(it, resolvedCall.call.isSafeCall()) }?.getSubtypesPredicate() it.dispatchReceiverParameter?.type?.let { TypeUtils.makeNullableIfNeeded(it, resolvedCall.call.isSafeCall()) }
?.getSubtypesPredicate()
}) })
} }
} }
@@ -72,9 +73,9 @@ fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: Recei
} }
fun getExpectedTypePredicate( fun getExpectedTypePredicate(
value: PseudoValue, value: PseudoValue,
bindingContext: BindingContext, bindingContext: BindingContext,
builtIns: KotlinBuiltIns builtIns: KotlinBuiltIns
): TypePredicate { ): TypePredicate {
val pseudocode = value.createdAt?.owner ?: return AllTypes val pseudocode = value.createdAt?.owner ?: return AllTypes
val typePredicates = LinkedHashSet<TypePredicate?>() val typePredicates = LinkedHashSet<TypePredicate?>()
@@ -92,8 +93,8 @@ fun getExpectedTypePredicate(
val callee = call.calleeExpression ?: return null val callee = call.calleeExpression ?: return null
val candidates = callee.getReferenceTargets(bindingContext) val candidates = callee.getReferenceTargets(bindingContext)
.filterIsInstance<FunctionDescriptor>() .filterIsInstance<FunctionDescriptor>()
.sortedBy { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) } .sortedBy { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) }
if (candidates.isEmpty()) return null if (candidates.isEmpty()) return null
val explicitReceiver = call.explicitReceiver val explicitReceiver = call.explicitReceiver
@@ -103,17 +104,17 @@ fun getExpectedTypePredicate(
for (candidate in candidates) { for (candidate in candidates) {
val resolutionCandidate = ResolutionCandidate.create( val resolutionCandidate = ResolutionCandidate.create(
call, call,
candidate, candidate,
null, null,
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
null null
) )
val candidateCall = ResolvedCallImpl.create( val candidateCall = ResolvedCallImpl.create(
resolutionCandidate, resolutionCandidate,
DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"), DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"),
TracingStrategy.EMPTY, TracingStrategy.EMPTY,
DataFlowInfoForArgumentsImpl(DataFlowInfo.EMPTY, call) DataFlowInfoForArgumentsImpl(DataFlowInfo.EMPTY, call)
) )
val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(call, TracingStrategy.EMPTY, candidateCall) val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(call, TracingStrategy.EMPTY, candidateCall)
if (!status.isSuccess) continue if (!status.isSuccess) continue
@@ -143,7 +144,7 @@ fun getExpectedTypePredicate(
when (it) { when (it) {
is ReturnValueInstruction -> { is ReturnValueInstruction -> {
val returnElement = it.element val returnElement = it.element
val functionDescriptor = when(returnElement) { val functionDescriptor = when (returnElement) {
is KtReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext) is KtReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext)
else -> bindingContext[DECLARATION_TO_DESCRIPTOR, pseudocode.correspondingElement] else -> bindingContext[DECLARATION_TO_DESCRIPTOR, pseudocode.correspondingElement]
} }
@@ -164,8 +165,7 @@ fun getExpectedTypePredicate(
val receiverValue = it.receiverValues[value] val receiverValue = it.receiverValues[value]
if (receiverValue != null) { if (receiverValue != null) {
typePredicates.add(getReceiverTypePredicate((accessTarget as AccessTarget.Call).resolvedCall, receiverValue)) typePredicates.add(getReceiverTypePredicate((accessTarget as AccessTarget.Call).resolvedCall, receiverValue))
} } else {
else {
val expectedType = when (accessTarget) { val expectedType = when (accessTarget) {
is AccessTarget.Call -> is AccessTarget.Call ->
(accessTarget.resolvedCall.resultingDescriptor as? VariableDescriptor)?.type (accessTarget.resolvedCall.resultingDescriptor as? VariableDescriptor)?.type
@@ -182,8 +182,7 @@ fun getExpectedTypePredicate(
val receiverValue = it.receiverValues[value] val receiverValue = it.receiverValues[value]
if (receiverValue != null) { if (receiverValue != null) {
typePredicates.add(getReceiverTypePredicate(it.resolvedCall, receiverValue)) typePredicates.add(getReceiverTypePredicate(it.resolvedCall, receiverValue))
} } else {
else {
it.arguments[value]?.let { parameter -> it.arguments[value]?.let { parameter ->
val expectedType = when (it.resolvedCall.valueArguments[parameter]) { val expectedType = when (it.resolvedCall.valueArguments[parameter]) {
is VarargValueArgument -> is VarargValueArgument ->
@@ -281,8 +280,8 @@ fun Pseudocode.getElementValuesRecursively(element: KtElement): List<PseudoValue
} }
val KtElement.containingDeclarationForPseudocode: KtDeclaration? val KtElement.containingDeclarationForPseudocode: KtDeclaration?
get() = PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java) get() = PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java)
?: getNonStrictParentOfType<KtProperty>() ?: getNonStrictParentOfType<KtProperty>()
fun KtDeclaration.getContainingPseudocode(context: BindingContext): Pseudocode? { fun KtDeclaration.getContainingPseudocode(context: BindingContext): Pseudocode? {
val enclosingPseudocodeDeclaration = (this as? KtFunctionLiteral)?.let { val enclosingPseudocodeDeclaration = (this as? KtFunctionLiteral)?.let {
@@ -44,9 +44,9 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) ?: return val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) ?: return
val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.CallsInPlaceEffect) || val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.CallsInPlaceEffect) ||
languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect) || languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect) ||
// This condition is here for technical purposes of compiling 1.2-runtime with contracts // This condition is here for technical purposes of compiling 1.2-runtime with contracts
languageVersionSettings.getFlag(AnalysisFlag.Flags.allowKotlinPackage) languageVersionSettings.getFlag(AnalysisFlag.Flags.allowKotlinPackage)
val contractDescriptor = when { val contractDescriptor = when {
!isFeatureTurnedOn || !isContractDescriptionCallPreciseCheck(expression, trace.bindingContext) -> null !isFeatureTurnedOn || !isContractDescriptionCallPreciseCheck(expression, trace.bindingContext) -> null
@@ -63,20 +63,20 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
} }
private fun parseContract(expression: KtExpression?, trace: BindingTrace, ownerDescriptor: FunctionDescriptor): ContractDescription? = private fun parseContract(expression: KtExpression?, trace: BindingTrace, ownerDescriptor: FunctionDescriptor): ContractDescription? =
PsiContractParserDispatcher(trace, this).parseContract(expression, ownerDescriptor) PsiContractParserDispatcher(trace, this).parseContract(expression, ownerDescriptor)
internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean = internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean =
isContractDescriptionCallFastCheck(expression) && isContractDescriptionCallPreciseCheck(expression, context) isContractDescriptionCallFastCheck(expression) && isContractDescriptionCallPreciseCheck(expression, context)
private fun isContractAllowedHere(element: KtElement): Boolean = private fun isContractAllowedHere(element: KtElement): Boolean =
element is KtNamedFunction && element.isTopLevel && element.hasBlockBody() && !element.hasModifier(KtTokens.OPERATOR_KEYWORD) element is KtNamedFunction && element.isTopLevel && element.hasBlockBody() && !element.hasModifier(KtTokens.OPERATOR_KEYWORD)
private fun isContractAllowedHere(scope: LexicalScope): Boolean = private fun isContractAllowedHere(scope: LexicalScope): Boolean =
scope.kind == LexicalScopeKind.CODE_BLOCK && (scope.parent as? LexicalScope)?.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE scope.kind == LexicalScopeKind.CODE_BLOCK && (scope.parent as? LexicalScope)?.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE
private fun isContractDescriptionCallFastCheck(expression: KtExpression): Boolean = private fun isContractDescriptionCallFastCheck(expression: KtExpression): Boolean =
expression is KtCallExpression && expression.calleeExpression?.text == "contract" expression is KtCallExpression && expression.calleeExpression?.text == "contract"
private fun isContractDescriptionCallPreciseCheck(expression: KtExpression, context: BindingContext): Boolean = private fun isContractDescriptionCallPreciseCheck(expression: KtExpression, context: BindingContext): Boolean =
expression.getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false expression.getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false
} }
@@ -27,7 +27,8 @@ import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiContractParserDispatcher) : KtVisitor<BooleanExpression?, Unit>() { internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiContractParserDispatcher) :
KtVisitor<BooleanExpression?, Unit>() {
override fun visitIsExpression(expression: KtIsExpression, data: Unit): BooleanExpression? { override fun visitIsExpression(expression: KtIsExpression, data: Unit): BooleanExpression? {
val variable = dispatcher.parseVariable(expression.leftHandSide) ?: return null val variable = dispatcher.parseVariable(expression.leftHandSide) ?: return null
val typeReference = expression.typeReference ?: return null val typeReference = expression.typeReference ?: return null
@@ -101,11 +102,16 @@ internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiCo
if (expression.operationToken != KtTokens.EXCL) return super.visitUnaryExpression(expression, data) if (expression.operationToken != KtTokens.EXCL) return super.visitUnaryExpression(expression, data)
val arg = expression.baseExpression?.accept(this, data) ?: return null val arg = expression.baseExpression?.accept(this, data) ?: return null
if (arg !is ContractDescriptionValue) { if (arg !is ContractDescriptionValue) {
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression.baseExpression!!, "negations in contract description can be applied only to variables/values")) trace.report(
Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(
expression.baseExpression!!,
"negations in contract description can be applied only to variables/values"
)
)
} }
return LogicalNot(arg) return LogicalNot(arg)
} }
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): BooleanExpression? = override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): BooleanExpression? =
KtPsiUtil.deparenthesize(expression)?.accept(this, data) KtPsiUtil.deparenthesize(expression)?.accept(this, data)
} }
@@ -24,4 +24,5 @@ internal interface PsiEffectParser {
fun tryParseEffect(expression: KtExpression): EffectDeclaration? fun tryParseEffect(expression: KtExpression): EffectDeclaration?
} }
internal abstract class AbstractPsiEffectParser(val trace: BindingTrace, val contractParserDispatcher: PsiContractParserDispatcher) : PsiEffectParser internal abstract class AbstractPsiEffectParser(val trace: BindingTrace, val contractParserDispatcher: PsiContractParserDispatcher) :
PsiEffectParser
@@ -47,7 +47,8 @@ object PositioningStrategies {
} }
} }
@JvmField val DEFAULT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val DEFAULT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
when (element) { when (element) {
is KtObjectLiteralExpression -> { is KtObjectLiteralExpression -> {
@@ -61,8 +62,8 @@ object PositioningStrategies {
} }
is KtObjectDeclaration -> { is KtObjectDeclaration -> {
return markRange( return markRange(
element.getObjectKeyword()!!, element.getObjectKeyword()!!,
element.nameIdentifier ?: element.getObjectKeyword()!! element.nameIdentifier ?: element.getObjectKeyword()!!
) )
} }
is KtConstructorDelegationCall -> { is KtConstructorDelegationCall -> {
@@ -75,7 +76,8 @@ object PositioningStrategies {
} }
} }
@JvmField val DECLARATION_RETURN_TYPE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() { @JvmField
val DECLARATION_RETURN_TYPE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> { override fun mark(element: KtDeclaration): List<TextRange> {
return markElement(getElementToMark(element)) return markElement(getElementToMark(element))
} }
@@ -97,7 +99,8 @@ object PositioningStrategies {
} }
} }
@JvmField val ACTUAL_DECLARATION_NAME: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() { @JvmField
val ACTUAL_DECLARATION_NAME: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() {
override fun mark(element: KtNamedDeclaration): List<TextRange> { override fun mark(element: KtNamedDeclaration): List<TextRange> {
val nameIdentifier = element.nameIdentifier val nameIdentifier = element.nameIdentifier
return when { return when {
@@ -112,11 +115,11 @@ object PositioningStrategies {
get() { get() {
val map = when (factory) { val map = when (factory) {
NO_ACTUAL_FOR_EXPECT -> NO_ACTUAL_FOR_EXPECT ->
NO_ACTUAL_FOR_EXPECT.cast(this).c NO_ACTUAL_FOR_EXPECT.cast(this).c
ACTUAL_WITHOUT_EXPECT -> ACTUAL_WITHOUT_EXPECT ->
ACTUAL_WITHOUT_EXPECT.cast(this).b ACTUAL_WITHOUT_EXPECT.cast(this).b
else -> else ->
return null return null
} }
return map.keys.firstOrNull() return map.keys.firstOrNull()
} }
@@ -125,7 +128,8 @@ object PositioningStrategies {
private val classKindTokens = TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD, KtTokens.INTERFACE_KEYWORD) private val classKindTokens = TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD, KtTokens.INTERFACE_KEYWORD)
@JvmField val INCOMPATIBLE_DECLARATION: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() { @JvmField
val INCOMPATIBLE_DECLARATION: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() {
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out KtNamedDeclaration>): List<TextRange> { override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out KtNamedDeclaration>): List<TextRange> {
val element = diagnostic.psiElement val element = diagnostic.psiElement
val callableDeclaration = element as? KtCallableDeclaration val callableDeclaration = element as? KtCallableDeclaration
@@ -134,15 +138,14 @@ object PositioningStrategies {
null, Unknown, is ClassScopes, EnumEntries -> null null, Unknown, is ClassScopes, EnumEntries -> null
ClassKind -> { ClassKind -> {
val startElement = val startElement =
element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD) element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD)
?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD) ?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD)
val endElement = val endElement =
element.node.findChildByType(classKindTokens)?.psi element.node.findChildByType(classKindTokens)?.psi
?: element.nameIdentifier ?: element.nameIdentifier
if (startElement != null && endElement != null) { if (startElement != null && endElement != null) {
return markRange(startElement, endElement) return markRange(startElement, endElement)
} } else {
else {
endElement endElement
} }
} }
@@ -181,15 +184,16 @@ object PositioningStrategies {
} }
} }
@JvmField val DECLARATION_NAME: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() { @JvmField
val DECLARATION_NAME: PositioningStrategy<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() {
override fun mark(element: KtNamedDeclaration): List<TextRange> { override fun mark(element: KtNamedDeclaration): List<TextRange> {
val nameIdentifier = element.nameIdentifier val nameIdentifier = element.nameIdentifier
if (nameIdentifier != null) { if (nameIdentifier != null) {
if (element is KtClassOrObject) { if (element is KtClassOrObject) {
val startElement = val startElement =
element.getModifierList()?.getModifier(KtTokens.ENUM_KEYWORD) element.getModifierList()?.getModifier(KtTokens.ENUM_KEYWORD)
?: element.node.findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD))?.psi ?: element.node.findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD))?.psi
?: element ?: element
return markRange(startElement, nameIdentifier) return markRange(startElement, nameIdentifier)
} }
@@ -202,7 +206,8 @@ object PositioningStrategies {
} }
} }
@JvmField val DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : DeclarationHeader<KtDeclaration>() { @JvmField
val DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : DeclarationHeader<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> { override fun mark(element: KtDeclaration): List<TextRange> {
when (element) { when (element) {
is KtConstructor<*> -> { is KtConstructor<*> -> {
@@ -212,17 +217,15 @@ object PositioningStrategies {
} }
is KtFunction -> { is KtFunction -> {
val endOfSignatureElement = val endOfSignatureElement =
element.typeReference element.typeReference
?: element.valueParameterList ?: element.valueParameterList
?: element.nameIdentifier ?: element.nameIdentifier
?: element ?: element
val startElement val startElement = if (element is KtFunctionLiteral) {
= if (element is KtFunctionLiteral) { element.getReceiverTypeReference()
element.getReceiverTypeReference()
?: element.getValueParameterList() ?: element.getValueParameterList()
?: element ?: element
} } else element
else element
return markRange(startElement, endOfSignatureElement) return markRange(startElement, endOfSignatureElement)
} }
is KtProperty -> { is KtProperty -> {
@@ -231,15 +234,16 @@ object PositioningStrategies {
} }
is KtPropertyAccessor -> { is KtPropertyAccessor -> {
val endOfSignatureElement = val endOfSignatureElement =
element.returnTypeReference element.returnTypeReference
?: element.rightParenthesis?.psi ?: element.rightParenthesis?.psi
?: element.namePlaceholder ?: element.namePlaceholder
return markRange(element, endOfSignatureElement) return markRange(element, endOfSignatureElement)
} }
is KtClass -> { is KtClass -> {
val nameAsDeclaration = element.nameIdentifier ?: return markElement(element) val nameAsDeclaration = element.nameIdentifier ?: return markElement(element)
val primaryConstructorParameterList = element.getPrimaryConstructorParameterList() ?: return markElement(nameAsDeclaration) val primaryConstructorParameterList =
element.getPrimaryConstructorParameterList() ?: return markElement(nameAsDeclaration)
return markRange(nameAsDeclaration, primaryConstructorParameterList) return markRange(nameAsDeclaration, primaryConstructorParameterList)
} }
is KtObjectDeclaration -> { is KtObjectDeclaration -> {
@@ -253,7 +257,8 @@ object PositioningStrategies {
} }
} }
@JvmField val DECLARATION_SIGNATURE_OR_DEFAULT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val DECLARATION_SIGNATURE_OR_DEFAULT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
return if (element is KtDeclaration) return if (element is KtDeclaration)
DECLARATION_SIGNATURE.mark(element) DECLARATION_SIGNATURE.mark(element)
@@ -269,26 +274,24 @@ object PositioningStrategies {
} }
} }
@JvmField val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() { @JvmField
val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> = override fun mark(element: KtDeclaration): List<TextRange> =
markElement( markElement(
when (element) { when (element) {
is KtClassOrObject -> is KtClassOrObject ->
element.getDeclarationKeyword() ?: element.getDeclarationKeyword() ?: element.nameIdentifier ?: element
element.nameIdentifier ?:
element
is KtNamedFunction -> is KtNamedFunction ->
element.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: element.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: element.funKeyword ?: element
element.funKeyword ?:
element
else -> element else -> element
} }
) )
} }
@JvmField val TYPE_PARAMETERS_OR_DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() { @JvmField
val TYPE_PARAMETERS_OR_DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> { override fun mark(element: KtDeclaration): List<TextRange> {
if (element is KtTypeParameterListOwner) { if (element is KtTypeParameterListOwner) {
val jetTypeParameterList = element.typeParameterList val jetTypeParameterList = element.typeParameterList
@@ -300,19 +303,26 @@ object PositioningStrategies {
} }
} }
@JvmField val ABSTRACT_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.ABSTRACT_KEYWORD) @JvmField
val ABSTRACT_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.ABSTRACT_KEYWORD)
@JvmField val OPEN_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.OPEN_KEYWORD) @JvmField
val OPEN_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.OPEN_KEYWORD)
@JvmField val OVERRIDE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.OVERRIDE_KEYWORD) @JvmField
val OVERRIDE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.OVERRIDE_KEYWORD)
@JvmField val PRIVATE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.PRIVATE_KEYWORD) @JvmField
val PRIVATE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.PRIVATE_KEYWORD)
@JvmField val LATEINIT_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.LATEINIT_KEYWORD) @JvmField
val LATEINIT_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.LATEINIT_KEYWORD)
@JvmField val VARIANCE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.IN_KEYWORD, KtTokens.OUT_KEYWORD) @JvmField
val VARIANCE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.IN_KEYWORD, KtTokens.OUT_KEYWORD)
@JvmField val FOR_REDECLARATION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val FOR_REDECLARATION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
val nameIdentifier = when (element) { val nameIdentifier = when (element) {
is KtNamedDeclaration -> element.nameIdentifier is KtNamedDeclaration -> element.nameIdentifier
@@ -325,7 +335,8 @@ object PositioningStrategies {
return markElement(nameIdentifier ?: element) return markElement(nameIdentifier ?: element)
} }
} }
@JvmField val FOR_UNRESOLVED_REFERENCE: PositioningStrategy<KtReferenceExpression> = object : PositioningStrategy<KtReferenceExpression>() { @JvmField
val FOR_UNRESOLVED_REFERENCE: PositioningStrategy<KtReferenceExpression> = object : PositioningStrategy<KtReferenceExpression>() {
override fun mark(element: KtReferenceExpression): List<TextRange> { override fun mark(element: KtReferenceExpression): List<TextRange> {
if (element is KtArrayAccessExpression) { if (element is KtArrayAccessExpression) {
val ranges = element.bracketRanges val ranges = element.bracketRanges
@@ -337,7 +348,8 @@ object PositioningStrategies {
} }
} }
@JvmStatic fun modifierSetPosition(vararg tokens: KtModifierKeywordToken): PositioningStrategy<KtModifierListOwner> { @JvmStatic
fun modifierSetPosition(vararg tokens: KtModifierKeywordToken): PositioningStrategy<KtModifierListOwner> {
return object : PositioningStrategy<KtModifierListOwner>() { return object : PositioningStrategy<KtModifierListOwner>() {
override fun mark(element: KtModifierListOwner): List<TextRange> { override fun mark(element: KtModifierListOwner): List<TextRange> {
val modifierList = element.modifierList.sure { "No modifier list, but modifier has been found by the analyzer" } val modifierList = element.modifierList.sure { "No modifier list, but modifier has been found by the analyzer" }
@@ -353,15 +365,18 @@ object PositioningStrategies {
} }
} }
@JvmField val ARRAY_ACCESS: PositioningStrategy<KtArrayAccessExpression> = object : PositioningStrategy<KtArrayAccessExpression>() { @JvmField
val ARRAY_ACCESS: PositioningStrategy<KtArrayAccessExpression> = object : PositioningStrategy<KtArrayAccessExpression>() {
override fun mark(element: KtArrayAccessExpression): List<TextRange> { override fun mark(element: KtArrayAccessExpression): List<TextRange> {
return markElement(element.indicesNode) return markElement(element.indicesNode)
} }
} }
@JvmField val VISIBILITY_MODIFIER: PositioningStrategy<KtModifierListOwner> = object : PositioningStrategy<KtModifierListOwner>() { @JvmField
val VISIBILITY_MODIFIER: PositioningStrategy<KtModifierListOwner> = object : PositioningStrategy<KtModifierListOwner>() {
override fun mark(element: KtModifierListOwner): List<TextRange> { override fun mark(element: KtModifierListOwner): List<TextRange> {
val visibilityTokens = listOf(KtTokens.PRIVATE_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PUBLIC_KEYWORD, KtTokens.INTERNAL_KEYWORD) val visibilityTokens =
listOf(KtTokens.PRIVATE_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PUBLIC_KEYWORD, KtTokens.INTERNAL_KEYWORD)
val modifierList = element.modifierList val modifierList = element.modifierList
val result = visibilityTokens.mapNotNull { modifierList?.getModifier(it)?.textRange } val result = visibilityTokens.mapNotNull { modifierList?.getModifier(it)?.textRange }
@@ -380,38 +395,44 @@ object PositioningStrategies {
is KtPropertyAccessor -> element.namePlaceholder is KtPropertyAccessor -> element.namePlaceholder
is KtAnonymousInitializer -> element is KtAnonymousInitializer -> element
else -> throw IllegalArgumentException( else -> throw IllegalArgumentException(
"Can't find text range for element '${element::class.java.canonicalName}' with the text '${element.text}'") "Can't find text range for element '${element::class.java.canonicalName}' with the text '${element.text}'"
)
} }
return markElement(elementToMark) return markElement(elementToMark)
} }
} }
@JvmField val VARIANCE_IN_PROJECTION: PositioningStrategy<KtTypeProjection> = object : PositioningStrategy<KtTypeProjection>() { @JvmField
val VARIANCE_IN_PROJECTION: PositioningStrategy<KtTypeProjection> = object : PositioningStrategy<KtTypeProjection>() {
override fun mark(element: KtTypeProjection): List<TextRange> { override fun mark(element: KtTypeProjection): List<TextRange> {
return markElement(element.projectionToken!!) return markElement(element.projectionToken!!)
} }
} }
@JvmField val PARAMETER_DEFAULT_VALUE: PositioningStrategy<KtParameter> = object : PositioningStrategy<KtParameter>() { @JvmField
val PARAMETER_DEFAULT_VALUE: PositioningStrategy<KtParameter> = object : PositioningStrategy<KtParameter>() {
override fun mark(element: KtParameter): List<TextRange> { override fun mark(element: KtParameter): List<TextRange> {
return markNode(element.defaultValue!!.node) return markNode(element.defaultValue!!.node)
} }
} }
@JvmField val PARAMETER_VARARG_MODIFIER: PositioningStrategy<KtParameter> = object : PositioningStrategy<KtParameter>() { @JvmField
val PARAMETER_VARARG_MODIFIER: PositioningStrategy<KtParameter> = object : PositioningStrategy<KtParameter>() {
override fun mark(element: KtParameter): List<TextRange> { override fun mark(element: KtParameter): List<TextRange> {
val varargModifier = element.modifierList!!.getModifier(KtTokens.VARARG_KEYWORD)!! val varargModifier = element.modifierList!!.getModifier(KtTokens.VARARG_KEYWORD)!!
return markNode(varargModifier.node) return markNode(varargModifier.node)
} }
} }
@JvmField val CALL_ELEMENT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val CALL_ELEMENT: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
return markElement((element as? KtCallElement)?.calleeExpression ?: element) return markElement((element as? KtCallElement)?.calleeExpression ?: element)
} }
} }
@JvmField val DECLARATION_WITH_BODY: PositioningStrategy<KtDeclarationWithBody> = object : PositioningStrategy<KtDeclarationWithBody>() { @JvmField
val DECLARATION_WITH_BODY: PositioningStrategy<KtDeclarationWithBody> = object : PositioningStrategy<KtDeclarationWithBody>() {
override fun mark(element: KtDeclarationWithBody): List<TextRange> { override fun mark(element: KtDeclarationWithBody): List<TextRange> {
val lastBracketRange = (element.bodyExpression as? KtBlockExpression)?.lastBracketRange val lastBracketRange = (element.bodyExpression as? KtBlockExpression)?.lastBracketRange
return if (lastBracketRange != null) return if (lastBracketRange != null)
@@ -425,7 +446,8 @@ object PositioningStrategies {
} }
} }
@JvmField val VAL_OR_VAR_NODE: PositioningStrategy<KtNamedDeclaration> = object : PositioningStrategy<KtNamedDeclaration>() { @JvmField
val VAL_OR_VAR_NODE: PositioningStrategy<KtNamedDeclaration> = object : PositioningStrategy<KtNamedDeclaration>() {
override fun mark(element: KtNamedDeclaration): List<TextRange> { override fun mark(element: KtNamedDeclaration): List<TextRange> {
return when (element) { return when (element) {
is KtParameter -> markElement(element.valOrVarKeyword ?: element) is KtParameter -> markElement(element.valOrVarKeyword ?: element)
@@ -435,41 +457,47 @@ object PositioningStrategies {
} }
} }
@JvmField val ELSE_ENTRY: PositioningStrategy<KtWhenEntry> = object : PositioningStrategy<KtWhenEntry>() { @JvmField
val ELSE_ENTRY: PositioningStrategy<KtWhenEntry> = object : PositioningStrategy<KtWhenEntry>() {
override fun mark(element: KtWhenEntry): List<TextRange> { override fun mark(element: KtWhenEntry): List<TextRange> {
return markElement(element.elseKeyword!!) return markElement(element.elseKeyword!!)
} }
} }
@JvmField val WHEN_EXPRESSION: PositioningStrategy<KtWhenExpression> = object : PositioningStrategy<KtWhenExpression>() { @JvmField
val WHEN_EXPRESSION: PositioningStrategy<KtWhenExpression> = object : PositioningStrategy<KtWhenExpression>() {
override fun mark(element: KtWhenExpression): List<TextRange> { override fun mark(element: KtWhenExpression): List<TextRange> {
return markElement(element.whenKeyword) return markElement(element.whenKeyword)
} }
} }
@JvmField val WHEN_CONDITION_IN_RANGE: PositioningStrategy<KtWhenConditionInRange> = object : PositioningStrategy<KtWhenConditionInRange>() { @JvmField
val WHEN_CONDITION_IN_RANGE: PositioningStrategy<KtWhenConditionInRange> = object : PositioningStrategy<KtWhenConditionInRange>() {
override fun mark(element: KtWhenConditionInRange): List<TextRange> { override fun mark(element: KtWhenConditionInRange): List<TextRange> {
return markElement(element.operationReference) return markElement(element.operationReference)
} }
} }
@JvmField val SPECIAL_CONSTRUCT_TOKEN: PositioningStrategy<KtExpression> = object : PositioningStrategy<KtExpression>() { @JvmField
val SPECIAL_CONSTRUCT_TOKEN: PositioningStrategy<KtExpression> = object : PositioningStrategy<KtExpression>() {
override fun mark(element: KtExpression): List<TextRange> = override fun mark(element: KtExpression): List<TextRange> =
when (element) { when (element) {
is KtWhenExpression -> markElement(element.whenKeyword) is KtWhenExpression -> markElement(element.whenKeyword)
is KtIfExpression -> markElement(element.ifKeyword) is KtIfExpression -> markElement(element.ifKeyword)
is KtOperationExpression -> markElement(element.operationReference) is KtOperationExpression -> markElement(element.operationReference)
else -> error("Expression is not an if, when or operation expression: ${element.getElementTextWithContext()}") else -> error("Expression is not an if, when or operation expression: ${element.getElementTextWithContext()}")
} }
} }
@JvmField val NULLABLE_TYPE: PositioningStrategy<KtNullableType> = object : PositioningStrategy<KtNullableType>() { @JvmField
val NULLABLE_TYPE: PositioningStrategy<KtNullableType> = object : PositioningStrategy<KtNullableType>() {
override fun mark(element: KtNullableType): List<TextRange> { override fun mark(element: KtNullableType): List<TextRange> {
return markNode(element.questionMarkNode) return markNode(element.questionMarkNode)
} }
} }
@JvmField val CALL_EXPRESSION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val CALL_EXPRESSION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
if (element is KtCallExpression) { if (element is KtCallExpression) {
return markRange(element, element.typeArgumentList ?: element.calleeExpression ?: element) return markRange(element, element.typeArgumentList ?: element.calleeExpression ?: element)
@@ -478,13 +506,15 @@ object PositioningStrategies {
} }
} }
@JvmField val VALUE_ARGUMENTS: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() { @JvmField
val VALUE_ARGUMENTS: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() {
override fun mark(element: KtElement): List<TextRange> { override fun mark(element: KtElement): List<TextRange> {
return markElement((element as? KtValueArgumentList)?.rightParenthesis ?: element) return markElement((element as? KtValueArgumentList)?.rightParenthesis ?: element)
} }
} }
@JvmField val FUNCTION_PARAMETERS: PositioningStrategy<KtFunction> = object : PositioningStrategy<KtFunction>() { @JvmField
val FUNCTION_PARAMETERS: PositioningStrategy<KtFunction> = object : PositioningStrategy<KtFunction>() {
override fun mark(element: KtFunction): List<TextRange> { override fun mark(element: KtFunction): List<TextRange> {
val valueParameterList = element.valueParameterList val valueParameterList = element.valueParameterList
if (valueParameterList != null) { if (valueParameterList != null) {
@@ -497,7 +527,8 @@ object PositioningStrategies {
} }
} }
@JvmField val CUT_CHAR_QUOTES: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() { @JvmField
val CUT_CHAR_QUOTES: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() {
override fun mark(element: KtElement): List<TextRange> { override fun mark(element: KtElement): List<TextRange> {
if (element is KtConstantExpression) { if (element is KtConstantExpression) {
if (element.node.elementType == KtNodeTypes.CHARACTER_CONSTANT) { if (element.node.elementType == KtNodeTypes.CHARACTER_CONSTANT) {
@@ -509,7 +540,8 @@ object PositioningStrategies {
} }
} }
@JvmField val LONG_LITERAL_SUFFIX: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() { @JvmField
val LONG_LITERAL_SUFFIX: PositioningStrategy<KtElement> = object : PositioningStrategy<KtElement>() {
override fun mark(element: KtElement): List<TextRange> { override fun mark(element: KtElement): List<TextRange> {
if (element is KtConstantExpression) { if (element is KtConstantExpression) {
if (element.node.elementType == KtNodeTypes.INTEGER_CONSTANT) { if (element.node.elementType == KtNodeTypes.INTEGER_CONSTANT) {
@@ -521,19 +553,22 @@ object PositioningStrategies {
} }
} }
@JvmField val UNREACHABLE_CODE: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { @JvmField
val UNREACHABLE_CODE: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out PsiElement>): List<TextRange> { override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out PsiElement>): List<TextRange> {
return Errors.UNREACHABLE_CODE.cast(diagnostic).a return Errors.UNREACHABLE_CODE.cast(diagnostic).a
} }
} }
@JvmField val AS_TYPE: PositioningStrategy<KtBinaryExpressionWithTypeRHS> = object : PositioningStrategy<KtBinaryExpressionWithTypeRHS>() { @JvmField
val AS_TYPE: PositioningStrategy<KtBinaryExpressionWithTypeRHS> = object : PositioningStrategy<KtBinaryExpressionWithTypeRHS>() {
override fun mark(element: KtBinaryExpressionWithTypeRHS): List<TextRange> { override fun mark(element: KtBinaryExpressionWithTypeRHS): List<TextRange> {
return markRange(element.operationReference, element) return markRange(element.operationReference, element)
} }
} }
@JvmField val COMPANION_OBJECT: PositioningStrategy<KtObjectDeclaration> = object : PositioningStrategy<KtObjectDeclaration>() { @JvmField
val COMPANION_OBJECT: PositioningStrategy<KtObjectDeclaration> = object : PositioningStrategy<KtObjectDeclaration>() {
override fun mark(element: KtObjectDeclaration): List<TextRange> { override fun mark(element: KtObjectDeclaration): List<TextRange> {
if (element.hasModifier(KtTokens.COMPANION_KEYWORD)) { if (element.hasModifier(KtTokens.COMPANION_KEYWORD)) {
return modifierSetPosition(KtTokens.COMPANION_KEYWORD).mark(element) return modifierSetPosition(KtTokens.COMPANION_KEYWORD).mark(element)
@@ -542,38 +577,43 @@ object PositioningStrategies {
} }
} }
@JvmField val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: PositioningStrategy<KtConstructorDelegationCall> = @JvmField
object : PositioningStrategy<KtConstructorDelegationCall>() { val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: PositioningStrategy<KtConstructorDelegationCall> =
override fun mark(element: KtConstructorDelegationCall): List<TextRange> { object : PositioningStrategy<KtConstructorDelegationCall>() {
if (element.isImplicit) { override fun mark(element: KtConstructorDelegationCall): List<TextRange> {
val constructor = element.getStrictParentOfType<KtSecondaryConstructor>()!! if (element.isImplicit) {
val valueParameterList = constructor.valueParameterList ?: return markElement(constructor) val constructor = element.getStrictParentOfType<KtSecondaryConstructor>()!!
return markRange(constructor.getConstructorKeyword(), valueParameterList.lastChild) val valueParameterList = constructor.valueParameterList ?: return markElement(constructor)
} return markRange(constructor.getConstructorKeyword(), valueParameterList.lastChild)
return markElement(element.calleeExpression ?: element)
} }
return markElement(element.calleeExpression ?: element)
} }
}
@JvmField val DELEGATOR_SUPER_CALL: PositioningStrategy<KtEnumEntry> = object: PositioningStrategy<KtEnumEntry>() { @JvmField
val DELEGATOR_SUPER_CALL: PositioningStrategy<KtEnumEntry> = object : PositioningStrategy<KtEnumEntry>() {
override fun mark(element: KtEnumEntry): List<TextRange> { override fun mark(element: KtEnumEntry): List<TextRange> {
val specifiers = element.superTypeListEntries val specifiers = element.superTypeListEntries
return markElement(if (specifiers.isEmpty()) element else specifiers[0]) return markElement(if (specifiers.isEmpty()) element else specifiers[0])
} }
} }
@JvmField val UNUSED_VALUE: PositioningStrategy<KtBinaryExpression> = object: PositioningStrategy<KtBinaryExpression>() { @JvmField
val UNUSED_VALUE: PositioningStrategy<KtBinaryExpression> = object : PositioningStrategy<KtBinaryExpression>() {
override fun mark(element: KtBinaryExpression): List<TextRange> { override fun mark(element: KtBinaryExpression): List<TextRange> {
return listOf(TextRange(element.left!!.startOffset, element.operationReference.endOffset)) return listOf(TextRange(element.left!!.startOffset, element.operationReference.endOffset))
} }
} }
@JvmField val USELESS_ELVIS: PositioningStrategy<KtBinaryExpression> = object: PositioningStrategy<KtBinaryExpression>() { @JvmField
val USELESS_ELVIS: PositioningStrategy<KtBinaryExpression> = object : PositioningStrategy<KtBinaryExpression>() {
override fun mark(element: KtBinaryExpression): List<TextRange> { override fun mark(element: KtBinaryExpression): List<TextRange> {
return listOf(TextRange(element.operationReference.startOffset, element.endOffset)) return listOf(TextRange(element.operationReference.startOffset, element.endOffset))
} }
} }
@JvmField val IMPORT_ALIAS: PositioningStrategy<KtImportDirective> = object: PositioningStrategy<KtImportDirective>() { @JvmField
val IMPORT_ALIAS: PositioningStrategy<KtImportDirective> = object : PositioningStrategy<KtImportDirective>() {
override fun mark(element: KtImportDirective): List<TextRange> { override fun mark(element: KtImportDirective): List<TextRange> {
element.alias?.nameIdentifier?.let { return markElement(it) } element.alias?.nameIdentifier?.let { return markElement(it) }
element.importedReference?.let { element.importedReference?.let {
@@ -586,7 +626,8 @@ object PositioningStrategies {
} }
} }
@JvmField val RETURN_WITH_LABEL: PositioningStrategy<KtReturnExpression> = object: PositioningStrategy<KtReturnExpression>() { @JvmField
val RETURN_WITH_LABEL: PositioningStrategy<KtReturnExpression> = object : PositioningStrategy<KtReturnExpression>() {
override fun mark(element: KtReturnExpression): List<TextRange> { override fun mark(element: KtReturnExpression): List<TextRange> {
val labeledExpression = element.labeledExpression val labeledExpression = element.labeledExpression
if (labeledExpression != null) { if (labeledExpression != null) {
@@ -597,7 +638,8 @@ object PositioningStrategies {
} }
} }
@JvmField val RECEIVER: PositioningStrategy<KtCallableDeclaration> = object : DeclarationHeader<KtCallableDeclaration>() { @JvmField
val RECEIVER: PositioningStrategy<KtCallableDeclaration> = object : DeclarationHeader<KtCallableDeclaration>() {
override fun mark(element: KtCallableDeclaration): List<TextRange> { override fun mark(element: KtCallableDeclaration): List<TextRange> {
element.receiverTypeReference?.let { return markElement(it) } element.receiverTypeReference?.let { return markElement(it) }
return DEFAULT.mark(element) return DEFAULT.mark(element)
@@ -20,10 +20,11 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
fun <P : Any> renderParameter(parameter: P, renderer: DiagnosticParameterRenderer<P>?, context: RenderingContext): Any fun <P : Any> renderParameter(parameter: P, renderer: DiagnosticParameterRenderer<P>?, context: RenderingContext): Any =
= renderer?.render(parameter, context) ?: parameter renderer?.render(parameter, context) ?: parameter
fun ClassifierDescriptorWithTypeParameters.renderKindWithName(): String = DescriptorRenderer.getClassifierKindPrefix(this) + " '" + name + "'" fun ClassifierDescriptorWithTypeParameters.renderKindWithName(): String =
DescriptorRenderer.getClassifierKindPrefix(this) + " '" + name + "'"
fun ClassDescriptor.renderKind(): String = DescriptorRenderer.getClassifierKindPrefix(this) fun ClassDescriptor.renderKind(): String = DescriptorRenderer.getClassifierKindPrefix(this)
@@ -57,27 +57,33 @@ object Renderers {
private val LOG = Logger.getInstance(Renderers::class.java) private val LOG = Logger.getInstance(Renderers::class.java)
@JvmField val TO_STRING = Renderer<Any> { @JvmField
element -> val TO_STRING = Renderer<Any> { element ->
if (element is DeclarationDescriptor) { if (element is DeclarationDescriptor) {
LOG.warn("Diagnostic renderer TO_STRING was used to render an instance of DeclarationDescriptor.\n" LOG.warn(
+ "This is usually a bad idea, because descriptors' toString() includes some debug information, " "Diagnostic renderer TO_STRING was used to render an instance of DeclarationDescriptor.\n"
+ "which should not be seen by the user.\nDescriptor: " + element) + "This is usually a bad idea, because descriptors' toString() includes some debug information, "
+ "which should not be seen by the user.\nDescriptor: " + element
)
} }
element.toString() element.toString()
} }
@JvmField val STRING = Renderer<String> { it } @JvmField
val STRING = Renderer<String> { it }
@JvmField val THROWABLE = Renderer<Throwable> { @JvmField
val THROWABLE = Renderer<Throwable> {
val writer = StringWriter() val writer = StringWriter()
it.printStackTrace(PrintWriter(writer)) it.printStackTrace(PrintWriter(writer))
StringUtil.first(writer.toString(), 2048, true) StringUtil.first(writer.toString(), 2048, true)
} }
@JvmField val NAME = Renderer<Named> { it.name.asString() } @JvmField
val NAME = Renderer<Named> { it.name.asString() }
@JvmField val PLATFORM = Renderer<ModuleDescriptor> { @JvmField
val PLATFORM = Renderer<ModuleDescriptor> {
val platform = it.getMultiTargetPlatform() val platform = it.getMultiTargetPlatform()
" ${it.getCapability(ModuleInfo.Capability)?.displayedName ?: ""}" + when (platform) { " ${it.getCapability(ModuleInfo.Capability)?.displayedName ?: ""}" + when (platform) {
MultiTargetPlatform.Common -> "" MultiTargetPlatform.Common -> ""
@@ -86,13 +92,15 @@ object Renderers {
} }
} }
@JvmField val VISIBILITY = Renderer<Visibility> { @JvmField
val VISIBILITY = Renderer<Visibility> {
if (it == Visibilities.INVISIBLE_FAKE) if (it == Visibilities.INVISIBLE_FAKE)
"invisible (private in a supertype)" "invisible (private in a supertype)"
else it.displayName else it.displayName
} }
@JvmField val DECLARATION_NAME_WITH_KIND = Renderer<DeclarationDescriptor> { @JvmField
val DECLARATION_NAME_WITH_KIND = Renderer<DeclarationDescriptor> {
val name = it.name.asString() val name = it.name.asString()
when (it) { when (it) {
is PackageFragmentDescriptor -> "package '$name'" is PackageFragmentDescriptor -> "package '$name'"
@@ -108,7 +116,8 @@ object Renderers {
} }
} }
@JvmField val CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM = ContextDependentRenderer<DeclarationDescriptor> { descriptor, context -> @JvmField
val CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM = ContextDependentRenderer<DeclarationDescriptor> { descriptor, context ->
val declarationWithNameAndKind = DECLARATION_NAME_WITH_KIND.render(descriptor, context) val declarationWithNameAndKind = DECLARATION_NAME_WITH_KIND.render(descriptor, context)
val withPlatform = if (descriptor is MemberDescriptor && descriptor.isActual) val withPlatform = if (descriptor is MemberDescriptor && descriptor.isActual)
"actual $declarationWithNameAndKind" "actual $declarationWithNameAndKind"
@@ -119,37 +128,40 @@ object Renderers {
} }
@JvmField val NAME_OF_CONTAINING_DECLARATION_OR_FILE = Renderer<DeclarationDescriptor> { @JvmField
val NAME_OF_CONTAINING_DECLARATION_OR_FILE = Renderer<DeclarationDescriptor> {
if (DescriptorUtils.isTopLevelDeclaration(it) && it is DeclarationDescriptorWithVisibility && it.visibility == Visibilities.PRIVATE) { if (DescriptorUtils.isTopLevelDeclaration(it) && it is DeclarationDescriptorWithVisibility && it.visibility == Visibilities.PRIVATE) {
"file" "file"
} } else {
else {
val containingDeclaration = it.containingDeclaration val containingDeclaration = it.containingDeclaration
if (containingDeclaration is PackageFragmentDescriptor) { if (containingDeclaration is PackageFragmentDescriptor) {
containingDeclaration.fqName.asString().wrapIntoQuotes() containingDeclaration.fqName.asString().wrapIntoQuotes()
} } else {
else {
containingDeclaration!!.name.asString().wrapIntoQuotes() containingDeclaration!!.name.asString().wrapIntoQuotes()
} }
} }
} }
@JvmField val ELEMENT_TEXT = Renderer<PsiElement> { it.text } @JvmField
val ELEMENT_TEXT = Renderer<PsiElement> { it.text }
@JvmField val DECLARATION_NAME = Renderer<KtNamedDeclaration> { it.nameAsSafeName.asString() } @JvmField
val DECLARATION_NAME = Renderer<KtNamedDeclaration> { it.nameAsSafeName.asString() }
@JvmField val RENDER_CLASS_OR_OBJECT = Renderer { @JvmField
classOrObject: KtClassOrObject -> val RENDER_CLASS_OR_OBJECT = Renderer { classOrObject: KtClassOrObject ->
val name = classOrObject.name?.let { " ${it.wrapIntoQuotes()}" } ?: "" val name = classOrObject.name?.let { " ${it.wrapIntoQuotes()}" } ?: ""
if (classOrObject is KtClass) "Class" + name else "Object" + name if (classOrObject is KtClass) "Class" + name else "Object" + name
} }
@JvmField val RENDER_CLASS_OR_OBJECT_NAME = Renderer<ClassifierDescriptorWithTypeParameters> { it.renderKindWithName() } @JvmField
val RENDER_CLASS_OR_OBJECT_NAME = Renderer<ClassifierDescriptorWithTypeParameters> { it.renderKindWithName() }
@JvmField val RENDER_TYPE = SmartTypeRenderer(DescriptorRenderer.FQ_NAMES_IN_TYPES.withOptions { parameterNamesInFunctionalTypes = false }) @JvmField
val RENDER_TYPE = SmartTypeRenderer(DescriptorRenderer.FQ_NAMES_IN_TYPES.withOptions { parameterNamesInFunctionalTypes = false })
@JvmField val RENDER_POSITION_VARIANCE = Renderer { @JvmField
variance: Variance -> val RENDER_POSITION_VARIANCE = Renderer { variance: Variance ->
when (variance) { when (variance) {
Variance.INVARIANT -> "invariant" Variance.INVARIANT -> "invariant"
Variance.IN_VARIANCE -> "in" Variance.IN_VARIANCE -> "in"
@@ -157,17 +169,17 @@ object Renderers {
} }
} }
@JvmField val AMBIGUOUS_CALLS = Renderer { @JvmField
calls: Collection<ResolvedCall<*>> -> val AMBIGUOUS_CALLS = Renderer { calls: Collection<ResolvedCall<*>> ->
val descriptors = calls.map { it.resultingDescriptor } val descriptors = calls.map { it.resultingDescriptor }
val context = RenderingContext.Impl(descriptors) val context = RenderingContext.Impl(descriptors)
descriptors descriptors
.sortedWith(MemberComparator.INSTANCE) .sortedWith(MemberComparator.INSTANCE)
.joinToString(separator = "\n", prefix = "\n") { FQ_NAMES_IN_TYPES.render(it, context) } .joinToString(separator = "\n", prefix = "\n") { FQ_NAMES_IN_TYPES.render(it, context) }
} }
@JvmStatic fun <T> commaSeparated(itemRenderer: DiagnosticParameterRenderer<T>) = ContextDependentRenderer<Collection<T>> { @JvmStatic
collection, context -> fun <T> commaSeparated(itemRenderer: DiagnosticParameterRenderer<T>) = ContextDependentRenderer<Collection<T>> { collection, context ->
buildString { buildString {
val iterator = collection.iterator() val iterator = collection.iterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
@@ -180,31 +192,39 @@ object Renderers {
} }
} }
@JvmField val TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER = Renderer<InferenceErrorData> { @JvmField
val TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER = Renderer<InferenceErrorData> {
renderConflictingSubstitutionsInferenceError(it, TabledDescriptorRenderer.create()).toString() renderConflictingSubstitutionsInferenceError(it, TabledDescriptorRenderer.create()).toString()
} }
@JvmField val TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR_RENDERER = Renderer<InferenceErrorData> { @JvmField
val TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR_RENDERER = Renderer<InferenceErrorData> {
renderParameterConstraintError(it, TabledDescriptorRenderer.create()).toString() renderParameterConstraintError(it, TabledDescriptorRenderer.create()).toString()
} }
@JvmField val TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER = Renderer<InferenceErrorData> { @JvmField
val TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER = Renderer<InferenceErrorData> {
renderNoInformationForParameterError(it, TabledDescriptorRenderer.create()).toString() renderNoInformationForParameterError(it, TabledDescriptorRenderer.create()).toString()
} }
@JvmField val TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = Renderer<InferenceErrorData> { @JvmField
val TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = Renderer<InferenceErrorData> {
renderUpperBoundViolatedInferenceError(it, TabledDescriptorRenderer.create()).toString() renderUpperBoundViolatedInferenceError(it, TabledDescriptorRenderer.create()).toString()
} }
@JvmField val TYPE_INFERENCE_CANNOT_CAPTURE_TYPES_RENDERER = Renderer<InferenceErrorData> { @JvmField
val TYPE_INFERENCE_CANNOT_CAPTURE_TYPES_RENDERER = Renderer<InferenceErrorData> {
renderCannotCaptureTypeParameterError(it, TabledDescriptorRenderer.create()).toString() renderCannotCaptureTypeParameterError(it, TabledDescriptorRenderer.create()).toString()
} }
@JvmStatic fun renderConflictingSubstitutionsInferenceError( @JvmStatic
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer fun renderConflictingSubstitutionsInferenceError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer { ): TabledDescriptorRenderer {
LOG.assertTrue(inferenceErrorData.constraintSystem.status.hasConflictingConstraints(), LOG.assertTrue(
debugMessage("Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData)) inferenceErrorData.constraintSystem.status.hasConflictingConstraints(),
debugMessage("Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData)
)
val substitutedDescriptors = Lists.newArrayList<CallableDescriptor>() val substitutedDescriptors = Lists.newArrayList<CallableDescriptor>()
val substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(inferenceErrorData.constraintSystem) val substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(inferenceErrorData.constraintSystem)
@@ -219,10 +239,12 @@ object Renderers {
return result return result
} }
result.text(newText() result.text(
.normal("Cannot infer type parameter ") newText()
.strong(firstConflictingVariable.name) .normal("Cannot infer type parameter ")
.normal(" in ")) .strong(firstConflictingVariable.name)
.normal(" in ")
)
val table = newTable() val table = newTable()
result.table(table) result.table(table)
table.descriptor(inferenceErrorData.descriptor).text("None of the following substitutions") table.descriptor(inferenceErrorData.descriptor).text("None of the following substitutions")
@@ -249,29 +271,33 @@ object Renderers {
table.functionArgumentTypeList(receiverType, parameterTypes, { errorPositions.contains(it) }) table.functionArgumentTypeList(receiverType, parameterTypes, { errorPositions.contains(it) })
} }
table.text("can be applied to").functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes) table.text("can be applied to")
.functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes)
return result return result
} }
@JvmStatic fun renderParameterConstraintError( @JvmStatic
inferenceErrorData: InferenceErrorData, renderer: TabledDescriptorRenderer fun renderParameterConstraintError(
inferenceErrorData: InferenceErrorData, renderer: TabledDescriptorRenderer
): TabledDescriptorRenderer { ): TabledDescriptorRenderer {
val constraintErrors = inferenceErrorData.constraintSystem.status.constraintErrors val constraintErrors = inferenceErrorData.constraintSystem.status.constraintErrors
val errorPositions = constraintErrors.filter { it is ParameterConstraintError }.map { it.constraintPosition } val errorPositions = constraintErrors.filter { it is ParameterConstraintError }.map { it.constraintPosition }
return renderer.table( return renderer.table(
TabledDescriptorRenderer TabledDescriptorRenderer
.newTable() .newTable()
.descriptor(inferenceErrorData.descriptor) .descriptor(inferenceErrorData.descriptor)
.text("cannot be applied to") .text("cannot be applied to")
.functionArgumentTypeList(inferenceErrorData.receiverArgumentType, .functionArgumentTypeList(inferenceErrorData.receiverArgumentType,
inferenceErrorData.valueArgumentsTypes, inferenceErrorData.valueArgumentsTypes,
{ errorPositions.contains(it) })) { errorPositions.contains(it) })
)
} }
@JvmStatic fun renderNoInformationForParameterError( @JvmStatic
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer fun renderNoInformationForParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer { ): TabledDescriptorRenderer {
val firstUnknownVariable = inferenceErrorData.constraintSystem.typeVariables.firstOrNull { variable -> val firstUnknownVariable = inferenceErrorData.constraintSystem.typeVariables.firstOrNull { variable ->
inferenceErrorData.constraintSystem.getTypeBounds(variable).values.isEmpty() inferenceErrorData.constraintSystem.getTypeBounds(variable).values.isEmpty()
@@ -280,21 +306,28 @@ object Renderers {
} }
return result return result
.text(newText().normal("Not enough information to infer parameter ") .text(
.strong(firstUnknownVariable.name) newText().normal("Not enough information to infer parameter ")
.normal(" in ")) .strong(firstUnknownVariable.name)
.table(newTable() .normal(" in ")
.descriptor(inferenceErrorData.descriptor) )
.text("Please specify it explicitly.")) .table(
newTable()
.descriptor(inferenceErrorData.descriptor)
.text("Please specify it explicitly.")
)
} }
@JvmStatic fun renderUpperBoundViolatedInferenceError( @JvmStatic
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer fun renderUpperBoundViolatedInferenceError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer { ): TabledDescriptorRenderer {
val constraintSystem = inferenceErrorData.constraintSystem val constraintSystem = inferenceErrorData.constraintSystem
val status = constraintSystem.status val status = constraintSystem.status
LOG.assertTrue(status.hasViolatedUpperBound(), LOG.assertTrue(
debugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)) status.hasViolatedUpperBound(),
debugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)
)
val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(TYPE_BOUND_POSITION) val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(TYPE_BOUND_POSITION)
val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull { val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull {
@@ -304,7 +337,7 @@ object Renderers {
if (typeParameterDescriptor == null) { if (typeParameterDescriptor == null) {
if (inferenceErrorData.descriptor is TypeAliasConstructorDescriptor) { if (inferenceErrorData.descriptor is TypeAliasConstructorDescriptor) {
renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor( renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(
inferenceErrorData, result, systemWithoutWeakConstraints inferenceErrorData, result, systemWithoutWeakConstraints
)?.let { )?.let {
return it return it
} }
@@ -313,7 +346,12 @@ object Renderers {
return if (status.hasConflictingConstraints()) return if (status.hasConflictingConstraints())
renderConflictingSubstitutionsInferenceError(inferenceErrorData, result) renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
else { else {
LOG.error(debugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error", inferenceErrorData)) LOG.error(
debugMessage(
"There is no type parameter with violated upper bound for 'upper bound violated' error",
inferenceErrorData
)
)
result result
} }
} }
@@ -321,21 +359,30 @@ object Renderers {
val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor) val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor)
val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeVariable).value val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeVariable).value
if (inferredValueForTypeParameter == null) { if (inferredValueForTypeParameter == null) {
LOG.error(debugMessage("System without weak constraints is not successful, there is no value for type parameter " + LOG.error(
typeParameterDescriptor.name + "\n: " + systemWithoutWeakConstraints, inferenceErrorData)) debugMessage(
"System without weak constraints is not successful, there is no value for type parameter " +
typeParameterDescriptor.name + "\n: " + systemWithoutWeakConstraints, inferenceErrorData
)
)
return result return result
} }
result.text(newText() result.text(
.normal("Type parameter bound for ") newText()
.strong(typeParameterDescriptor.name) .normal("Type parameter bound for ")
.normal(" in ")) .strong(typeParameterDescriptor.name)
.table(newTable() .normal(" in ")
.descriptor(inferenceErrorData.descriptor)) )
.table(
newTable()
.descriptor(inferenceErrorData.descriptor)
)
var violatedUpperBound: KotlinType? = null var violatedUpperBound: KotlinType? = null
for (upperBound in typeParameterDescriptor.upperBounds) { for (upperBound in typeParameterDescriptor.upperBounds) {
val upperBoundWithSubstitutedInferredTypes = systemWithoutWeakConstraints.resultingSubstitutor.substitute(upperBound, Variance.INVARIANT) val upperBoundWithSubstitutedInferredTypes =
systemWithoutWeakConstraints.resultingSubstitutor.substitute(upperBound, Variance.INVARIANT)
if (upperBoundWithSubstitutedInferredTypes != null if (upperBoundWithSubstitutedInferredTypes != null
&& !KotlinTypeChecker.DEFAULT.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) { && !KotlinTypeChecker.DEFAULT.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
violatedUpperBound = upperBoundWithSubstitutedInferredTypes violatedUpperBound = upperBoundWithSubstitutedInferredTypes
@@ -343,26 +390,32 @@ object Renderers {
} }
} }
if (violatedUpperBound == null) { if (violatedUpperBound == null) {
LOG.error(debugMessage("Type parameter (chosen as violating its upper bound)" + LOG.error(
typeParameterDescriptor.name + " violates no bounds after substitution", inferenceErrorData)) debugMessage(
"Type parameter (chosen as violating its upper bound)" +
typeParameterDescriptor.name + " violates no bounds after substitution", inferenceErrorData
)
)
return result return result
} }
// TODO: context should be in fact shared for the table and these two types // TODO: context should be in fact shared for the table and these two types
val context = RenderingContext.of(inferredValueForTypeParameter, violatedUpperBound) val context = RenderingContext.of(inferredValueForTypeParameter, violatedUpperBound)
val typeRenderer = result.typeRenderer val typeRenderer = result.typeRenderer
result.text(newText() result.text(
.normal(" is not satisfied: inferred type ") newText()
.error(typeRenderer.render(inferredValueForTypeParameter, context)) .normal(" is not satisfied: inferred type ")
.normal(" is not a subtype of ") .error(typeRenderer.render(inferredValueForTypeParameter, context))
.strong(typeRenderer.render(violatedUpperBound, context))) .normal(" is not a subtype of ")
.strong(typeRenderer.render(violatedUpperBound, context))
)
return result return result
} }
private fun renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor( private fun renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(
inferenceErrorData: InferenceErrorData, inferenceErrorData: InferenceErrorData,
result: TabledDescriptorRenderer, result: TabledDescriptorRenderer,
systemWithoutWeakConstraints: ConstraintSystem systemWithoutWeakConstraints: ConstraintSystem
): TabledDescriptorRenderer? { ): TabledDescriptorRenderer? {
val descriptor = inferenceErrorData.descriptor val descriptor = inferenceErrorData.descriptor
if (descriptor !is TypeAliasConstructorDescriptor) { if (descriptor !is TypeAliasConstructorDescriptor) {
@@ -391,12 +444,16 @@ object Renderers {
val context = RenderingContext.of(violatingInferredType, violatedUpperBound) val context = RenderingContext.of(violatingInferredType, violatedUpperBound)
val typeRenderer = result.typeRenderer val typeRenderer = result.typeRenderer
result.text(newText().normal("Type parameter bound for ").strong(constraintInfo.typeParameter.name) result.text(
.normal(" in type inferred from type alias expansion for ")) newText().normal("Type parameter bound for ").strong(constraintInfo.typeParameter.name)
.table(newTable().descriptor(inferenceErrorData.descriptor)) .normal(" in type inferred from type alias expansion for ")
)
.table(newTable().descriptor(inferenceErrorData.descriptor))
result.text(newText().normal(" is not satisfied: inferred type ").error(typeRenderer.render(violatingInferredType, context)) result.text(
.normal(" is not a subtype of ").strong(typeRenderer.render(violatedUpperBound, context))) newText().normal(" is not satisfied: inferred type ").error(typeRenderer.render(violatingInferredType, context))
.normal(" is not a subtype of ").strong(typeRenderer.render(violatedUpperBound, context))
)
return result return result
} }
@@ -404,8 +461,9 @@ object Renderers {
return null return null
} }
@JvmStatic fun renderCannotCaptureTypeParameterError( @JvmStatic
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer fun renderCannotCaptureTypeParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer { ): TabledDescriptorRenderer {
val system = inferenceErrorData.constraintSystem val system = inferenceErrorData.constraintSystem
val errors = system.status.constraintErrors val errors = system.status.constraintErrors
@@ -419,7 +477,12 @@ object Renderers {
val boundWithCapturedType = typeBounds.bounds.firstOrNull { it.constrainingType.isCaptured() } val boundWithCapturedType = typeBounds.bounds.firstOrNull { it.constrainingType.isCaptured() }
val capturedTypeConstructor = boundWithCapturedType?.constrainingType?.constructor as? CapturedTypeConstructor val capturedTypeConstructor = boundWithCapturedType?.constrainingType?.constructor as? CapturedTypeConstructor
if (capturedTypeConstructor == null) { if (capturedTypeConstructor == null) {
LOG.error(debugMessage("There is no captured type in bounds, but there is an error 'cannot capture type parameter'", inferenceErrorData)) LOG.error(
debugMessage(
"There is no captured type in bounds, but there is an error 'cannot capture type parameter'",
inferenceErrorData
)
)
return result return result
} }
@@ -431,20 +494,25 @@ object Renderers {
} }
val explanation = val explanation =
"Type parameter has an upper bound ${result.typeRenderer.render(upperBound, RenderingContext.of(upperBound)).wrapIntoQuotes()}" + "Type parameter has an upper bound ${result.typeRenderer.render(
" that cannot be satisfied capturing 'in' projection" upperBound,
RenderingContext.of(upperBound)
).wrapIntoQuotes()}" +
" that cannot be satisfied capturing 'in' projection"
result.text(newText().normal( result.text(
newText().normal(
typeParameter.name.wrapIntoQuotes() + typeParameter.name.wrapIntoQuotes() +
" cannot capture " + " cannot capture " +
"${capturedTypeConstructor.typeProjection.toString().wrapIntoQuotes()}. " + "${capturedTypeConstructor.typeProjection.toString().wrapIntoQuotes()}. " +
explanation explanation
)) )
)
return result return result
} }
@JvmField val CLASSES_OR_SEPARATED = Renderer<Collection<ClassDescriptor>> { @JvmField
descriptors -> val CLASSES_OR_SEPARATED = Renderer<Collection<ClassDescriptor>> { descriptors ->
buildString { buildString {
var index = 0 var index = 0
for (descriptor in descriptors) { for (descriptor in descriptors) {
@@ -452,17 +520,18 @@ object Renderers {
index++ index++
if (index <= descriptors.size - 2) { if (index <= descriptors.size - 2) {
append(", ") append(", ")
} } else if (index == descriptors.size - 1) {
else if (index == descriptors.size - 1) {
append(" or ") append(" or ")
} }
} }
} }
} }
private fun renderTypes(types: Collection<KotlinType>, context: RenderingContext) = StringUtil.join(types, { RENDER_TYPE.render(it, context) }, ", ") private fun renderTypes(types: Collection<KotlinType>, context: RenderingContext) =
StringUtil.join(types, { RENDER_TYPE.render(it, context) }, ", ")
@JvmField val RENDER_COLLECTION_OF_TYPES = ContextDependentRenderer<Collection<KotlinType>> { types, context -> renderTypes(types, context) } @JvmField
val RENDER_COLLECTION_OF_TYPES = ContextDependentRenderer<Collection<KotlinType>> { types, context -> renderTypes(types, context) }
fun renderConstraintSystem(constraintSystem: ConstraintSystem, shortTypeBounds: Boolean): String { fun renderConstraintSystem(constraintSystem: ConstraintSystem, shortTypeBounds: Boolean): String {
val typeBounds = linkedSetOf<TypeBounds>() val typeBounds = linkedSetOf<TypeBounds>()
@@ -470,8 +539,8 @@ object Renderers {
typeBounds.add(constraintSystem.getTypeBounds(variable)) typeBounds.add(constraintSystem.getTypeBounds(variable))
} }
return "type parameter bounds:\n" + return "type parameter bounds:\n" +
StringUtil.join(typeBounds, { renderTypeBounds(it, short = shortTypeBounds) }, "\n") + "\n\n" + "status:\n" + StringUtil.join(typeBounds, { renderTypeBounds(it, short = shortTypeBounds) }, "\n") + "\n\n" + "status:\n" +
ConstraintsUtil.getDebugMessageForStatus(constraintSystem.status) ConstraintsUtil.getDebugMessageForStatus(constraintSystem.status)
} }
private fun renderTypeBounds(typeBounds: TypeBounds, short: Boolean): String { private fun renderTypeBounds(typeBounds: TypeBounds, short: Boolean): String {
@@ -482,14 +551,13 @@ object Renderers {
else -> ":= " else -> ":= "
} }
val renderer = if (short) DescriptorRenderer.SHORT_NAMES_IN_TYPES else DescriptorRenderer.FQ_NAMES_IN_TYPES val renderer = if (short) DescriptorRenderer.SHORT_NAMES_IN_TYPES else DescriptorRenderer.FQ_NAMES_IN_TYPES
val renderedBound = arrow + renderer.renderType(bound.constrainingType) + if (!bound.isProper) "*" else "" val renderedBound = arrow + renderer.renderType(bound.constrainingType) + if (!bound.isProper) "*" else ""
if (short) renderedBound else renderedBound + '(' + bound.position + ')' if (short) renderedBound else renderedBound + '(' + bound.position + ')'
} }
val typeVariableName = typeBounds.typeVariable.name val typeVariableName = typeBounds.typeVariable.name
return if (typeBounds.bounds.isEmpty()) { return if (typeBounds.bounds.isEmpty()) {
typeVariableName.asString() typeVariableName.asString()
} } else
else
"$typeVariableName ${StringUtil.join(typeBounds.bounds, renderBound, ", ")}" "$typeVariableName ${StringUtil.join(typeBounds.bounds, renderBound, ", ")}"
} }
@@ -503,8 +571,7 @@ object Renderers {
val context = RenderingContext.Empty val context = RenderingContext.Empty
if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) { if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
append(inferenceErrorData.expectedType) append(inferenceErrorData.expectedType)
} } else {
else {
append(RENDER_TYPE.render(inferenceErrorData.expectedType, context)) append(RENDER_TYPE.render(inferenceErrorData.expectedType, context))
} }
append("\nArgument types:\n") append("\nArgument types:\n")
@@ -519,26 +586,33 @@ object Renderers {
private val WHEN_MISSING_LIMIT = 7 private val WHEN_MISSING_LIMIT = 7
@JvmField val RENDER_WHEN_MISSING_CASES = Renderer<List<WhenMissingCase>> { @JvmField
val RENDER_WHEN_MISSING_CASES = Renderer<List<WhenMissingCase>> {
if (!it.hasUnknown) { if (!it.hasUnknown) {
val list = it.joinToString(", ", limit = WHEN_MISSING_LIMIT) { "'$it'" } val list = it.joinToString(", ", limit = WHEN_MISSING_LIMIT) { "'$it'" }
val branches = if (it.size > 1) "branches" else "branch" val branches = if (it.size > 1) "branches" else "branch"
"$list $branches or 'else' branch instead" "$list $branches or 'else' branch instead"
} } else {
else {
"'else' branch" "'else' branch"
} }
} }
@JvmField val FQ_NAMES_IN_TYPES = DescriptorRenderer.FQ_NAMES_IN_TYPES.asRenderer() @JvmField
@JvmField val COMPACT = DescriptorRenderer.COMPACT.asRenderer() val FQ_NAMES_IN_TYPES = DescriptorRenderer.FQ_NAMES_IN_TYPES.asRenderer()
@JvmField val COMPACT_WITHOUT_SUPERTYPES = DescriptorRenderer.COMPACT_WITHOUT_SUPERTYPES.asRenderer() @JvmField
@JvmField val WITHOUT_MODIFIERS = DescriptorRenderer.withOptions { val COMPACT = DescriptorRenderer.COMPACT.asRenderer()
@JvmField
val COMPACT_WITHOUT_SUPERTYPES = DescriptorRenderer.COMPACT_WITHOUT_SUPERTYPES.asRenderer()
@JvmField
val WITHOUT_MODIFIERS = DescriptorRenderer.withOptions {
modifiers = emptySet() modifiers = emptySet()
}.asRenderer() }.asRenderer()
@JvmField val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer() @JvmField
@JvmField val COMPACT_WITH_MODIFIERS = DescriptorRenderer.COMPACT_WITH_MODIFIERS.asRenderer() val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer()
@JvmField val DEPRECATION_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions { @JvmField
val COMPACT_WITH_MODIFIERS = DescriptorRenderer.COMPACT_WITH_MODIFIERS.asRenderer()
@JvmField
val DEPRECATION_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions {
withoutTypeParameters = false withoutTypeParameters = false
receiverAfterName = false receiverAfterName = false
renderAccessors = true renderAccessors = true
@@ -37,7 +37,7 @@ private class AdaptiveClassifierNamePolicy(private val ambiguousNames: List<Name
return when { return when {
hasUniqueName(classifier) -> ClassifierNamePolicy.SHORT.renderClassifier(classifier, renderer) hasUniqueName(classifier) -> ClassifierNamePolicy.SHORT.renderClassifier(classifier, renderer)
classifier is ClassDescriptor || classifier is ClassDescriptor ||
classifier is TypeAliasDescriptor -> classifier is TypeAliasDescriptor ->
ClassifierNamePolicy.FULLY_QUALIFIED.renderClassifier(classifier, renderer) ClassifierNamePolicy.FULLY_QUALIFIED.renderClassifier(classifier, renderer)
classifier is TypeParameterDescriptor -> { classifier is TypeParameterDescriptor -> {
val name = classifier.name val name = classifier.name
@@ -55,7 +55,7 @@ private class AdaptiveClassifierNamePolicy(private val ambiguousNames: List<Name
} }
private fun DescriptorRenderer.renderAmbiguousTypeParameter( private fun DescriptorRenderer.renderAmbiguousTypeParameter(
typeParameter: TypeParameterDescriptor, index: Int, firstOccurence: Boolean typeParameter: TypeParameterDescriptor, index: Int, firstOccurence: Boolean
) = buildString { ) = buildString {
append(typeParameter.name) append(typeParameter.name)
append("#$index") append("#$index")
@@ -68,7 +68,7 @@ private class AdaptiveClassifierNamePolicy(private val ambiguousNames: List<Name
private val ADAPTIVE_CLASSIFIER_POLICY_KEY = object : RenderingContext.Key<ClassifierNamePolicy>("ADAPTIVE_CLASSIFIER_POLICY") { private val ADAPTIVE_CLASSIFIER_POLICY_KEY = object : RenderingContext.Key<ClassifierNamePolicy>("ADAPTIVE_CLASSIFIER_POLICY") {
override fun compute(objectsToRender: Collection<Any?>): ClassifierNamePolicy { override fun compute(objectsToRender: Collection<Any?>): ClassifierNamePolicy {
val ambiguousNames = val ambiguousNames =
collectClassifiersFqNames(objectsToRender).groupBy { it.shortNameOrSpecial() }.filter { it.value.size > 1 }.map { it.key } collectClassifiersFqNames(objectsToRender).groupBy { it.shortNameOrSpecial() }.filter { it.value.size > 1 }.map { it.key }
return AdaptiveClassifierNamePolicy(ambiguousNames) return AdaptiveClassifierNamePolicy(ambiguousNames)
} }
} }
@@ -83,8 +83,7 @@ private fun collectMentionedClassifiersFqNames(contextObjects: Collection<Any?>,
} }
contextObjects.filterIsInstance<KotlinType>().forEach { diagnosticType -> contextObjects.filterIsInstance<KotlinType>().forEach { diagnosticType ->
diagnosticType.contains { diagnosticType.contains { innerType ->
innerType ->
innerType.addMentionedTypeConstructor() innerType.addMentionedTypeConstructor()
innerType.getAbbreviation()?.addMentionedTypeConstructor() innerType.getAbbreviation()?.addMentionedTypeConstructor()
false false
@@ -101,12 +100,14 @@ private fun collectMentionedClassifiersFqNames(contextObjects: Collection<Any?>,
collectMentionedClassifiersFqNames(it.upperBounds, result) collectMentionedClassifiersFqNames(it.upperBounds, result)
} }
contextObjects.filterIsInstance<CallableDescriptor>().forEach { contextObjects.filterIsInstance<CallableDescriptor>().forEach {
collectMentionedClassifiersFqNames(listOf( collectMentionedClassifiersFqNames(
listOf(
it.typeParameters, it.typeParameters,
it.returnType, it.returnType,
it.valueParameters, it.valueParameters,
it.dispatchReceiverParameter?.type, it.dispatchReceiverParameter?.type,
it.extensionReceiverParameter?.type it.extensionReceiverParameter?.type
), result) ), result
)
} }
} }
@@ -38,9 +38,9 @@ import org.jetbrains.kotlin.types.expressions.LocalClassDescriptorHolder
import org.jetbrains.kotlin.types.expressions.LocalLazyDeclarationResolver import org.jetbrains.kotlin.types.expressions.LocalLazyDeclarationResolver
fun StorageComponentContainer.configureModule( fun StorageComponentContainer.configureModule(
moduleContext: ModuleContext, moduleContext: ModuleContext,
platform: TargetPlatform, platform: TargetPlatform,
platformVersion: TargetPlatformVersion platformVersion: TargetPlatformVersion
) { ) {
useInstance(moduleContext) useInstance(moduleContext)
useInstance(moduleContext.module) useInstance(moduleContext.module)
@@ -54,7 +54,7 @@ fun StorageComponentContainer.configureModule(
platform.platformConfigurator.configureModuleComponents(this) platform.platformConfigurator.configureModuleComponents(this)
for (extension in StorageComponentContainerContributor.getInstances(moduleContext.project)) { for (extension in StorageComponentContainerContributor.getInstances(moduleContext.project)) {
extension.registerModuleComponents(this, platform, moduleContext.module) extension.registerModuleComponents(this, platform, moduleContext.module)
} }
configurePlatformIndependentComponents() configurePlatformIndependentComponents()
@@ -66,22 +66,22 @@ private fun StorageComponentContainer.configurePlatformIndependentComponents() {
} }
fun StorageComponentContainer.configureModule( fun StorageComponentContainer.configureModule(
moduleContext: ModuleContext, moduleContext: ModuleContext,
platform: TargetPlatform, platform: TargetPlatform,
platformVersion: TargetPlatformVersion, platformVersion: TargetPlatformVersion,
trace: BindingTrace trace: BindingTrace
) { ) {
configureModule(moduleContext, platform, platformVersion) configureModule(moduleContext, platform, platformVersion)
useInstance(trace) useInstance(trace)
} }
fun createContainerForBodyResolve( fun createContainerForBodyResolve(
moduleContext: ModuleContext, moduleContext: ModuleContext,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
platform: TargetPlatform, platform: TargetPlatform,
statementFilter: StatementFilter, statementFilter: StatementFilter,
targetPlatformVersion: TargetPlatformVersion, targetPlatformVersion: TargetPlatformVersion,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): StorageComponentContainer = createContainer("BodyResolve", platform) { ): StorageComponentContainer = createContainer("BodyResolve", platform) {
configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace) configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace)
@@ -95,13 +95,13 @@ fun createContainerForBodyResolve(
} }
fun createContainerForLazyBodyResolve( fun createContainerForLazyBodyResolve(
moduleContext: ModuleContext, moduleContext: ModuleContext,
kotlinCodeAnalyzer: KotlinCodeAnalyzer, kotlinCodeAnalyzer: KotlinCodeAnalyzer,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
platform: TargetPlatform, platform: TargetPlatform,
bodyResolveCache: BodyResolveCache, bodyResolveCache: BodyResolveCache,
targetPlatformVersion: TargetPlatformVersion, targetPlatformVersion: TargetPlatformVersion,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): StorageComponentContainer = createContainer("LazyBodyResolve", platform) { ): StorageComponentContainer = createContainer("LazyBodyResolve", platform) {
configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace) configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace)
@@ -115,14 +115,14 @@ fun createContainerForLazyBodyResolve(
} }
fun createContainerForLazyLocalClassifierAnalyzer( fun createContainerForLazyLocalClassifierAnalyzer(
moduleContext: ModuleContext, moduleContext: ModuleContext,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
platform: TargetPlatform, platform: TargetPlatform,
lookupTracker: LookupTracker, lookupTracker: LookupTracker,
targetPlatformVersion: TargetPlatformVersion, targetPlatformVersion: TargetPlatformVersion,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
statementFilter: StatementFilter, statementFilter: StatementFilter,
localClassDescriptorHolder: LocalClassDescriptorHolder localClassDescriptorHolder: LocalClassDescriptorHolder
): StorageComponentContainer = createContainer("LocalClassifierAnalyzer", platform) { ): StorageComponentContainer = createContainer("LocalClassifierAnalyzer", platform) {
configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace) configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace)
@@ -147,13 +147,13 @@ fun createContainerForLazyLocalClassifierAnalyzer(
} }
fun createContainerForLazyResolve( fun createContainerForLazyResolve(
moduleContext: ModuleContext, moduleContext: ModuleContext,
declarationProviderFactory: DeclarationProviderFactory, declarationProviderFactory: DeclarationProviderFactory,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
platform: TargetPlatform, platform: TargetPlatform,
targetPlatformVersion: TargetPlatformVersion, targetPlatformVersion: TargetPlatformVersion,
targetEnvironment: TargetEnvironment, targetEnvironment: TargetEnvironment,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): StorageComponentContainer = createContainer("LazyResolve", platform) { ): StorageComponentContainer = createContainer("LazyResolve", platform) {
configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace) configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace)
@@ -168,12 +168,12 @@ fun createContainerForLazyResolve(
} }
fun createLazyResolveSession(moduleContext: ModuleContext, files: Collection<KtFile>): ResolveSession = fun createLazyResolveSession(moduleContext: ModuleContext, files: Collection<KtFile>): ResolveSession =
createContainerForLazyResolve( createContainerForLazyResolve(
moduleContext, moduleContext,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
BindingTraceContext(), BindingTraceContext(),
TargetPlatform.Common, TargetPlatform.Common,
TargetPlatformVersion.NoVersion, TargetPlatformVersion.NoVersion,
CompilerEnvironment, CompilerEnvironment,
LanguageVersionSettingsImpl.DEFAULT LanguageVersionSettingsImpl.DEFAULT
).get<ResolveSession>() ).get<ResolveSession>()
@@ -34,7 +34,8 @@ class MainFunctionDetector {
/** Assumes that the function declaration is already resolved and the descriptor can be found in the `bindingContext`. */ /** Assumes that the function declaration is already resolved and the descriptor can be found in the `bindingContext`. */
constructor(bindingContext: BindingContext) { constructor(bindingContext: BindingContext) {
this.getFunctionDescriptor = { function -> this.getFunctionDescriptor = { function ->
bindingContext.get(BindingContext.FUNCTION, function) ?: throw IllegalStateException("No descriptor resolved for " + function + " " + function.text) bindingContext.get(BindingContext.FUNCTION, function)
?: throw IllegalStateException("No descriptor resolved for " + function + " " + function.text)
} }
} }
@@ -83,9 +84,9 @@ class MainFunctionDetector {
private fun getMainFunction(module: ModuleDescriptor, packageView: PackageViewDescriptor): FunctionDescriptor? { private fun getMainFunction(module: ModuleDescriptor, packageView: PackageViewDescriptor): FunctionDescriptor? {
for (packageFragment in packageView.fragments.filter { it.module == module }) { for (packageFragment in packageView.fragments.filter { it.module == module }) {
DescriptorUtils.getAllDescriptors(packageFragment.getMemberScope()) DescriptorUtils.getAllDescriptors(packageFragment.getMemberScope())
.filterIsInstance<FunctionDescriptor>() .filterIsInstance<FunctionDescriptor>()
.firstOrNull { isMain(it) } .firstOrNull { isMain(it) }
?.let { return it } ?.let { return it }
} }
for (subpackageName in module.getSubPackagesOf(packageView.fqName, MemberScope.ALL_NAME_FILTER)) { for (subpackageName in module.getSubPackagesOf(packageView.fqName, MemberScope.ALL_NAME_FILTER)) {
@@ -108,7 +109,7 @@ class MainFunctionDetector {
} }
val parameters = descriptor.valueParameters.mapTo(mutableListOf()) { it.type } val parameters = descriptor.valueParameters.mapTo(mutableListOf()) { it.type }
descriptor.extensionReceiverParameter?.type?.let {parameters += it} descriptor.extensionReceiverParameter?.type?.let { parameters += it }
if (parameters.size != 1 || !descriptor.typeParameters.isEmpty()) return false if (parameters.size != 1 || !descriptor.typeParameters.isEmpty()) return false
@@ -127,14 +128,14 @@ class MainFunctionDetector {
} }
val returnType = descriptor.returnType val returnType = descriptor.returnType
if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) return false if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) return false
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) return true if (DescriptorUtils.isTopLevelDeclaration(descriptor)) return true
val containingDeclaration = descriptor.containingDeclaration val containingDeclaration = descriptor.containingDeclaration
return containingDeclaration is ClassDescriptor return containingDeclaration is ClassDescriptor
&& containingDeclaration.kind.isSingleton && containingDeclaration.kind.isSingleton
&& (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation) && (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation)
} }
private fun getJVMFunctionName(functionDescriptor: FunctionDescriptor): String { private fun getJVMFunctionName(functionDescriptor: FunctionDescriptor): String {
@@ -29,14 +29,17 @@ import org.jetbrains.kotlin.lexer.KtTokens
*/ */
class KDocLinkParser : PsiParser { class KDocLinkParser : PsiParser {
companion object { companion object {
@JvmStatic fun parseMarkdownLink(root: IElementType, chameleon: ASTNode): ASTNode { @JvmStatic
fun parseMarkdownLink(root: IElementType, chameleon: ASTNode): ASTNode {
val parentElement = chameleon.treeParent.psi val parentElement = chameleon.treeParent.psi
val project = parentElement.project val project = parentElement.project
val builder = PsiBuilderFactory.getInstance().createBuilder(project, val builder = PsiBuilderFactory.getInstance().createBuilder(
chameleon, project,
KotlinLexer(), chameleon,
root.language, KotlinLexer(),
chameleon.text) root.language,
chameleon.text
)
val parser = KDocLinkParser() val parser = KDocLinkParser()
return parser.parse(root, builder).firstChildNode return parser.parse(root, builder).firstChildNode
@@ -60,8 +63,7 @@ class KDocLinkParser : PsiParser {
if (builder.tokenType == KtTokens.RBRACKET) { if (builder.tokenType == KtTokens.RBRACKET) {
builder.advanceLexer() builder.advanceLexer()
} }
} } else {
else {
if (!builder.eof()) { if (!builder.eof()) {
builder.error("Expression expected") builder.error("Expression expected")
while (!builder.eof()) { while (!builder.eof()) {
@@ -32,18 +32,17 @@ class KDocSection(node: ASTNode) : KDocTag(node) {
* or null for the default section). * or null for the default section).
*/ */
override fun getName(): String? = override fun getName(): String? =
(firstChild as? KDocTag)?.name (firstChild as? KDocTag)?.name
override fun getSubjectName(): String? = override fun getSubjectName(): String? =
(firstChild as? KDocTag)?.getSubjectName() (firstChild as? KDocTag)?.getSubjectName()
override fun getContent(): String = override fun getContent(): String =
(firstChild as? KDocTag)?.getContent() ?: super.getContent() (firstChild as? KDocTag)?.getContent() ?: super.getContent()
fun findTagsByName(name: String): List<KDocTag> { fun findTagsByName(name: String): List<KDocTag> {
return getChildrenOfType<KDocTag>().filter { it.name == name } return getChildrenOfType<KDocTag>().filter { it.name == name }
} }
fun findTagByName(name: String): KDocTag? fun findTagByName(name: String): KDocTag? = findTagsByName(name).firstOrNull()
= findTagsByName(name).firstOrNull()
} }
@@ -67,9 +67,9 @@ open class KDocTag(node: ASTNode) : KDocElementImpl(node) {
} }
private fun childrenAfterTagName(): List<ASTNode> = private fun childrenAfterTagName(): List<ASTNode> =
node.getChildren(null) node.getChildren(null)
.dropWhile { it.elementType == KDocTokens.TAG_NAME } .dropWhile { it.elementType == KDocTokens.TAG_NAME }
.dropWhile { it.elementType == TokenType.WHITE_SPACE } .dropWhile { it.elementType == TokenType.WHITE_SPACE }
/** /**
* Returns the content of this tag (all text following the tag name and the subject if present, * Returns the content of this tag (all text following the tag name and the subject if present,
@@ -109,8 +109,7 @@ open class KDocTag(node: ASTNode) : KDocElementImpl(node) {
if (!isCodeBlock()) if (!isCodeBlock())
indentedCodeBlock = indentedCodeBlock || node.text.startsWith(indentationWhiteSpaces) || node.text.startsWith("\t") indentedCodeBlock = indentedCodeBlock || node.text.startsWith(indentationWhiteSpaces) || node.text.startsWith("\t")
startCodeBlock() startCodeBlock()
} } else if (KDocTokens.CONTENT_TOKENS.contains(type)) {
else if (KDocTokens.CONTENT_TOKENS.contains(type)) {
flushCodeBlock() flushCodeBlock()
indentedCodeBlock = false indentedCodeBlock = false
} }
@@ -63,7 +63,9 @@ class KotlinParserDefinition : ParserDefinition {
return when (elementType) { return when (elementType) {
is KtStubElementType<*, *> -> elementType.createPsiFromAst(astNode) is KtStubElementType<*, *> -> elementType.createPsiFromAst(astNode)
KtNodeTypes.TYPE_CODE_FRAGMENT, KtNodeTypes.EXPRESSION_CODE_FRAGMENT, KtNodeTypes.BLOCK_CODE_FRAGMENT -> ASTWrapperPsiElement(astNode) KtNodeTypes.TYPE_CODE_FRAGMENT, KtNodeTypes.EXPRESSION_CODE_FRAGMENT, KtNodeTypes.BLOCK_CODE_FRAGMENT -> ASTWrapperPsiElement(
astNode
)
is KDocElementType -> elementType.createPsi(astNode) is KDocElementType -> elementType.createPsi(astNode)
KDocTokens.MARKDOWN_LINK -> KDocLink(astNode) KDocTokens.MARKDOWN_LINK -> KDocLink(astNode)
else -> (elementType as KtNodeType).createPsi(astNode) else -> (elementType as KtNodeType).createPsi(astNode)
@@ -23,7 +23,8 @@ import org.jetbrains.kotlin.lexer.KtTokens
object PrecedingCommentsBinder : WhitespacesAndCommentsBinder { object PrecedingCommentsBinder : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
if (tokens.isEmpty()) return 0 if (tokens.isEmpty()) return 0
// 1. bind doc comment // 1. bind doc comment
@@ -44,7 +45,7 @@ object PrecedingCommentsBinder : WhitespacesAndCommentsBinder {
} }
} }
else -> break@tokens else -> break@tokens
} }
} }
@@ -54,7 +55,8 @@ object PrecedingCommentsBinder : WhitespacesAndCommentsBinder {
object PrecedingDocCommentsBinder : WhitespacesAndCommentsBinder { object PrecedingDocCommentsBinder : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
if (tokens.isEmpty()) return 0 if (tokens.isEmpty()) return 0
for (idx in tokens.indices.reversed()) { for (idx in tokens.indices.reversed()) {
@@ -68,7 +70,8 @@ object PrecedingDocCommentsBinder : WhitespacesAndCommentsBinder {
// Binds comments on the same line // Binds comments on the same line
object TrailingCommentsBinder : WhitespacesAndCommentsBinder { object TrailingCommentsBinder : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
if (tokens.isEmpty()) return 0 if (tokens.isEmpty()) return 0
var result = 0 var result = 0
@@ -89,7 +92,8 @@ object TrailingCommentsBinder : WhitespacesAndCommentsBinder {
private class AllCommentsBinder(val isTrailing: Boolean) : WhitespacesAndCommentsBinder { private class AllCommentsBinder(val isTrailing: Boolean) : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
if (tokens.isEmpty()) return 0 if (tokens.isEmpty()) return 0
val size = tokens.size val size = tokens.size
@@ -110,14 +114,16 @@ val TRAILING_ALL_COMMENTS_BINDER: WhitespacesAndCommentsBinder = AllCommentsBind
object DoNotBindAnything : WhitespacesAndCommentsBinder { object DoNotBindAnything : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
return 0 return 0
} }
} }
object BindFirstShebangWithWhitespaceOnly : WhitespacesAndCommentsBinder { object BindFirstShebangWithWhitespaceOnly : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
if (tokens.firstOrNull() == KtTokens.SHEBANG_COMMENT) { if (tokens.firstOrNull() == KtTokens.SHEBANG_COMMENT) {
return if (tokens.getOrNull(1) == KtTokens.WHITE_SPACE) 2 else 1 return if (tokens.getOrNull(1) == KtTokens.WHITE_SPACE) 2 else 1
} }
@@ -128,7 +134,8 @@ object BindFirstShebangWithWhitespaceOnly : WhitespacesAndCommentsBinder {
class BindAll(val isTrailing: Boolean) : WhitespacesAndCommentsBinder { class BindAll(val isTrailing: Boolean) : WhitespacesAndCommentsBinder {
override fun getEdgePosition( override fun getEdgePosition(
tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { tokens: List<IElementType>, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter
): Int {
return if (!isTrailing) 0 else tokens.size return if (!isTrailing) 0 else tokens.size
} }
} }
@@ -22,7 +22,7 @@ import gnu.trove.TIntArrayList
import org.jetbrains.kotlin.psi.psiUtil.getContentRange import org.jetbrains.kotlin.psi.psiUtil.getContentRange
import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted
class KotlinStringLiteralTextEscaper(host: KtStringTemplateExpression): LiteralTextEscaper<KtStringTemplateExpression>(host) { class KotlinStringLiteralTextEscaper(host: KtStringTemplateExpression) : LiteralTextEscaper<KtStringTemplateExpression>(host) {
private var sourceOffsets: IntArray? = null private var sourceOffsets: IntArray? = null
override fun decode(rangeInsideHost: TextRange, outChars: StringBuilder): Boolean { override fun decode(rangeInsideHost: TextRange, outChars: StringBuilder): Boolean {
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
// This node represents "fake" reference expression for ENUM_ENTRY(arguments) constructor syntax // This node represents "fake" reference expression for ENUM_ENTRY(arguments) constructor syntax
// It uses the superclass enum node to provide access to the real constructor name // It uses the superclass enum node to provide access to the real constructor name
class KtEnumEntrySuperclassReferenceExpression : class KtEnumEntrySuperclassReferenceExpression :
KtExpressionImplStub<KotlinEnumEntrySuperclassReferenceExpressionStub>, KtSimpleNameExpression { KtExpressionImplStub<KotlinEnumEntrySuperclassReferenceExpressionStub>, KtSimpleNameExpression {
constructor(node: ASTNode) : super(node) constructor(node: ASTNode) : super(node)
@@ -37,7 +37,7 @@ class KtEnumEntrySuperclassReferenceExpression :
// It is the owner enum class (not an enum entry but the whole enum) // It is the owner enum class (not an enum entry but the whole enum)
private val referencedElement: KtClass private val referencedElement: KtClass
get() = calcReferencedElement()!! get() = calcReferencedElement()!!
private fun calcReferencedElement(): KtClass? { private fun calcReferencedElement(): KtClass? {
val owner = this.getStrictParentOfType<KtEnumEntry>() val owner = this.getStrictParentOfType<KtEnumEntry>()
@@ -35,7 +35,8 @@ abstract class KtExpressionImpl(node: ASTNode) : KtElementImpl(node), KtExpressi
} }
is KtSimpleNameStringTemplateEntry -> { is KtSimpleNameStringTemplateEntry -> {
if (newElement !is KtSimpleNameExpression && !newElement.isThisWithoutLabel()) { if (newElement !is KtSimpleNameExpression && !newElement.isThisWithoutLabel()) {
val newEntry = parent.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(newElement)) as KtBlockStringTemplateEntry val newEntry =
parent.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(newElement)) as KtBlockStringTemplateEntry
return newEntry.expression!! return newEntry.expression!!
} }
} }
@@ -43,8 +43,7 @@ class KtPrimaryConstructor : KtConstructor<KtPrimaryConstructor> {
if (this.modifierList == null) { if (this.modifierList == null) {
getConstructorKeyword()?.delete() getConstructorKeyword()?.delete()
} }
} } else {
else {
if (modifier == KtTokens.PUBLIC_KEYWORD) return if (modifier == KtTokens.PUBLIC_KEYWORD) return
val newModifierList = KtPsiFactory(this).createModifierList(modifier) val newModifierList = KtPsiFactory(this).createModifierList(modifier)
addBefore(newModifierList, getOrCreateConstructorKeyword()) addBefore(newModifierList, getOrCreateConstructorKeyword())
@@ -62,8 +61,7 @@ class KtPrimaryConstructor : KtConstructor<KtPrimaryConstructor> {
val modifierList = modifierList val modifierList = modifierList
return if (modifierList != null) { return if (modifierList != null) {
modifierList.addBefore(annotationEntry, modifierList.firstChild) as KtAnnotationEntry modifierList.addBefore(annotationEntry, modifierList.firstChild) as KtAnnotationEntry
} } else {
else {
val parameterList = valueParameterList!! val parameterList = valueParameterList!!
val newModifierList = KtPsiFactory(project).createModifierList(annotationEntry.text) val newModifierList = KtPsiFactory(project).createModifierList(annotationEntry.text)
(addBefore(newModifierList, parameterList) as KtModifierList).annotationEntries.first() (addBefore(newModifierList, parameterList) as KtModifierList).annotationEntries.first()
@@ -26,10 +26,11 @@ import org.jetbrains.kotlin.resolve.TargetPlatform
fun KtPsiFactory(project: Project?, markGenerated: Boolean = true): KtPsiFactory = KtPsiFactory(project!!, markGenerated) fun KtPsiFactory(project: Project?, markGenerated: Boolean = true): KtPsiFactory = KtPsiFactory(project!!, markGenerated)
@JvmOverloads @JvmOverloads
fun KtPsiFactory(elementForProject: PsiElement, markGenerated: Boolean = true): KtPsiFactory = KtPsiFactory(elementForProject.project, markGenerated) fun KtPsiFactory(elementForProject: PsiElement, markGenerated: Boolean = true): KtPsiFactory =
KtPsiFactory(elementForProject.project, markGenerated)
private val DO_NOT_ANALYZE_NOTIFICATION = "This file was created by KtPsiFactory and should not be analyzed\n" + private val DO_NOT_ANALYZE_NOTIFICATION = "This file was created by KtPsiFactory and should not be analyzed\n" +
"Use createAnalyzableFile to create file that can be analyzed\n" "Use createAnalyzableFile to create file that can be analyzed\n"
var KtFile.doNotAnalyze: String? by UserDataProperty(Key.create("DO_NOT_ANALYZE")) var KtFile.doNotAnalyze: String? by UserDataProperty(Key.create("DO_NOT_ANALYZE"))
var KtFile.analysisContext: PsiElement? by UserDataProperty(Key.create("ANALYSIS_CONTEXT")) var KtFile.analysisContext: PsiElement? by UserDataProperty(Key.create("ANALYSIS_CONTEXT"))
@@ -72,7 +73,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
fun createThisExpression() = fun createThisExpression() =
(createExpression("this.x") as KtQualifiedExpression).receiverExpression as KtThisExpression (createExpression("this.x") as KtQualifiedExpression).receiverExpression as KtThisExpression
fun createThisExpression(qualifier: String) = fun createThisExpression(qualifier: String) =
(createExpression("this@$qualifier.x") as KtQualifiedExpression).receiverExpression as KtThisExpression (createExpression("this@$qualifier.x") as KtQualifiedExpression).receiverExpression as KtThisExpression
@@ -109,7 +110,8 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
fun createFunctionTypeParameter(typeReference: KtTypeReference): KtParameter { fun createFunctionTypeParameter(typeReference: KtTypeReference): KtParameter {
return (createType("(A) -> B").typeElement as KtFunctionType).parameters.first().apply { this.typeReference!!.replace(typeReference) } return (createType("(A) -> B").typeElement as KtFunctionType).parameters.first()
.apply { this.typeReference!!.replace(typeReference) }
} }
fun createTypeAlias(name: String, typeParameters: List<String>, typeElement: KtTypeElement): KtTypeAlias { fun createTypeAlias(name: String, typeParameters: List<String>, typeElement: KtTypeElement): KtTypeAlias {
@@ -184,7 +186,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
return createFileAnnotationListWithAnnotation(annotationText).annotationEntries.first() return createFileAnnotationListWithAnnotation(annotationText).annotationEntries.first()
} }
fun createFileAnnotationListWithAnnotation(annotationText: String) : KtFileAnnotationList { fun createFileAnnotationListWithAnnotation(annotationText: String): KtFileAnnotationList {
return createFile("@file:$annotationText").fileAnnotationList!! return createFile("@file:$annotationText").fileAnnotationList!!
} }
@@ -193,7 +195,14 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
private fun doCreateFile(fileName: String, text: String): KtFile { private fun doCreateFile(fileName: String, text: String): KtFile {
return PsiFileFactory.getInstance(project).createFileFromText(fileName, KotlinFileType.INSTANCE, text, LocalTimeCounter.currentTime(), false, markGenerated) as KtFile return PsiFileFactory.getInstance(project).createFileFromText(
fileName,
KotlinFileType.INSTANCE,
text,
LocalTimeCounter.currentTime(),
false,
markGenerated
) as KtFile
} }
fun createFile(fileName: String, text: String): KtFile { fun createFile(fileName: String, text: String): KtFile {
@@ -217,13 +226,19 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
fun createPhysicalFile(fileName: String, text: String): KtFile { fun createPhysicalFile(fileName: String, text: String): KtFile {
return PsiFileFactory.getInstance(project).createFileFromText(fileName, KotlinFileType.INSTANCE, text, LocalTimeCounter.currentTime(), true) as KtFile return PsiFileFactory.getInstance(project).createFileFromText(
fileName,
KotlinFileType.INSTANCE,
text,
LocalTimeCounter.currentTime(),
true
) as KtFile
} }
fun createProperty(modifiers: String?, name: String, type: String?, isVar: Boolean, initializer: String?): KtProperty { fun createProperty(modifiers: String?, name: String, type: String?, isVar: Boolean, initializer: String?): KtProperty {
val text = modifiers.let { "$it "} + val text = modifiers.let { "$it " } +
(if (isVar) " var " else " val ") + name + (if (isVar) " var " else " val ") + name +
(if (type != null) ":" + type else "") + (if (initializer == null) "" else " = " + initializer) (if (type != null) ":" + type else "") + (if (initializer == null) "" else " = " + initializer)
return createProperty(text) return createProperty(text)
} }
@@ -333,7 +348,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
return createClass("class A(){}").getBody()!! return createClass("class A(){}").getBody()!!
} }
fun createParameter(text : String): KtParameter { fun createParameter(text: String): KtParameter {
return createClass("class A($text)").primaryConstructorParameters.first() return createClass("class A($text)").primaryConstructorParameters.first()
} }
@@ -346,13 +361,13 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
fun createTypeParameter(text: String) = createTypeParameterList("<$text>").parameters.first()!! fun createTypeParameter(text: String) = createTypeParameterList("<$text>").parameters.first()!!
fun createLambdaParameterListIfAny(text: String) = fun createLambdaParameterListIfAny(text: String) =
createLambdaExpression(text, "0").functionLiteral.valueParameterList createLambdaExpression(text, "0").functionLiteral.valueParameterList
fun createLambdaParameterList(text: String) = createLambdaParameterListIfAny(text)!! fun createLambdaParameterList(text: String) = createLambdaParameterListIfAny(text)!!
fun createLambdaExpression(parameters: String, body: String): KtLambdaExpression = fun createLambdaExpression(parameters: String, body: String): KtLambdaExpression =
(if (parameters.isNotEmpty()) createExpression("{ $parameters -> $body }") (if (parameters.isNotEmpty()) createExpression("{ $parameters -> $body }")
else createExpression("{ $body }")) as KtLambdaExpression else createExpression("{ $body }")) as KtLambdaExpression
fun createEnumEntry(text: String): KtEnumEntry { fun createEnumEntry(text: String): KtEnumEntry {
@@ -404,7 +419,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
throw IllegalArgumentException("import path must not be empty") throw IllegalArgumentException("import path must not be empty")
} }
val file = createFile(buildString { appendImport (importPath) }) val file = createFile(buildString { appendImport(importPath) })
return file.importDirectives.first() return file.importDirectives.first()
} }
@@ -435,7 +450,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
fun createPrimaryConstructor(text: String = ""): KtPrimaryConstructor { fun createPrimaryConstructor(text: String = ""): KtPrimaryConstructor {
return createClass(if (text.isNotEmpty())"class A $text" else "class A()" ).primaryConstructor!! return createClass(if (text.isNotEmpty()) "class A $text" else "class A()").primaryConstructor!!
} }
fun createPrimaryConstructorWithModifiers(modifiers: String?): KtPrimaryConstructor { fun createPrimaryConstructorWithModifiers(modifiers: String?): KtPrimaryConstructor {
@@ -443,10 +458,9 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
} }
fun createConstructorKeyword(): PsiElement = fun createConstructorKeyword(): PsiElement =
createClass("class A constructor()").primaryConstructor!!.getConstructorKeyword()!! createClass("class A constructor()").primaryConstructor!!.getConstructorKeyword()!!
fun createLabeledExpression(labelName: String): KtLabeledExpression fun createLabeledExpression(labelName: String): KtLabeledExpression = createExpression("$labelName@ 1") as KtLabeledExpression
= createExpression("$labelName@ 1") as KtLabeledExpression
fun createTypeCodeFragment(text: String, context: PsiElement?): KtTypeCodeFragment { fun createTypeCodeFragment(text: String, context: PsiElement?): KtTypeCodeFragment {
return KtTypeCodeFragment(project, "fragment.kt", text, context) return KtTypeCodeFragment(project, "fragment.kt", text, context)
@@ -512,6 +526,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
TYPE_CONSTRAINTS, TYPE_CONSTRAINTS,
DONE DONE
} }
private val sb = StringBuilder() private val sb = StringBuilder()
private var state = State.MODIFIERS private var state = State.MODIFIERS
@@ -820,7 +835,8 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
return BlockWrapper(block, expression) return BlockWrapper(block, expression)
} }
private class BlockWrapper(fakeBlockExpression: KtBlockExpression, private val expression: KtExpression) : KtBlockExpression(fakeBlockExpression.node), KtPsiUtil.KtExpressionWrapper { private class BlockWrapper(fakeBlockExpression: KtBlockExpression, private val expression: KtExpression) :
KtBlockExpression(fakeBlockExpression.node), KtPsiUtil.KtExpressionWrapper {
override fun getStatements(): List<KtExpression> { override fun getStatements(): List<KtExpression> {
return listOf(expression) return listOf(expression)
} }
@@ -30,22 +30,22 @@ class KtTypeAlias : KtTypeParameterListOwnerStub<KotlinTypeAliasStub>, KtNamedDe
constructor(stub: KotlinTypeAliasStub) : super(stub, KtStubElementTypes.TYPEALIAS) constructor(stub: KotlinTypeAliasStub) : super(stub, KtStubElementTypes.TYPEALIAS)
override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R =
visitor.visitTypeAlias(this, data) visitor.visitTypeAlias(this, data)
fun isTopLevel(): Boolean = fun isTopLevel(): Boolean =
stub?.isTopLevel() ?: parent is KtFile stub?.isTopLevel() ?: parent is KtFile
@IfNotParsed @IfNotParsed
fun getTypeAliasKeyword(): PsiElement? = fun getTypeAliasKeyword(): PsiElement? =
findChildByType(KtTokens.TYPE_ALIAS_KEYWORD) findChildByType(KtTokens.TYPE_ALIAS_KEYWORD)
@IfNotParsed @IfNotParsed
fun getTypeReference(): KtTypeReference? { fun getTypeReference(): KtTypeReference? {
return if (stub != null) { return if (stub != null) {
val typeReferences = getStubOrPsiChildrenAsList<KtTypeReference, KotlinPlaceHolderStub<KtTypeReference>>(KtStubElementTypes.TYPE_REFERENCE) val typeReferences =
getStubOrPsiChildrenAsList<KtTypeReference, KotlinPlaceHolderStub<KtTypeReference>>(KtStubElementTypes.TYPE_REFERENCE)
typeReferences[0] typeReferences[0]
} } else {
else {
findChildByType(KtNodeTypes.TYPE_REFERENCE) findChildByType(KtNodeTypes.TYPE_REFERENCE)
} }
} }
@@ -21,10 +21,10 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
class KtTypeCodeFragment( class KtTypeCodeFragment(
project: Project, project: Project,
name: String, name: String,
text: CharSequence, text: CharSequence,
context: PsiElement? context: PsiElement?
) : KtCodeFragment(project, name, text, null, KtNodeTypes.TYPE_CODE_FRAGMENT, context) { ) : KtCodeFragment(project, name, text, null, KtNodeTypes.TYPE_CODE_FRAGMENT, context) {
override fun getContentElement() = findChildByClass(KtTypeReference::class.java) override fun getContentElement() = findChildByClass(KtTypeReference::class.java)
} }
@@ -26,9 +26,9 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
private fun KtModifierListOwner.addModifierList(newModifierList: KtModifierList): KtModifierList { private fun KtModifierListOwner.addModifierList(newModifierList: KtModifierList): KtModifierList {
val anchor = firstChild!! val anchor = firstChild!!
.siblings(forward = true) .siblings(forward = true)
.dropWhile { it is PsiComment || it is PsiWhiteSpace } .dropWhile { it is PsiComment || it is PsiWhiteSpace }
.first() .first()
return addBefore(newModifierList, anchor) as KtModifierList return addBefore(newModifierList, anchor) as KtModifierList
} }
@@ -40,8 +40,7 @@ fun KtModifierListOwner.setModifierList(newModifierList: KtModifierList) {
val currentModifierList = modifierList val currentModifierList = modifierList
if (currentModifierList != null) { if (currentModifierList != null) {
currentModifierList.replace(newModifierList) currentModifierList.replace(newModifierList)
} } else {
else {
addModifierList(newModifierList) addModifierList(newModifierList)
} }
} }
@@ -50,8 +49,7 @@ fun addModifier(owner: KtModifierListOwner, modifier: KtModifierKeywordToken) {
val modifierList = owner.modifierList val modifierList = owner.modifierList
if (modifierList == null) { if (modifierList == null) {
createModifierList(modifier.value, owner) createModifierList(modifier.value, owner)
} } else {
else {
addModifier(modifierList, modifier) addModifier(modifierList, modifier)
} }
} }
@@ -60,8 +58,7 @@ fun addAnnotationEntry(owner: KtModifierListOwner, annotationEntry: KtAnnotation
val modifierList = owner.modifierList val modifierList = owner.modifierList
return if (modifierList == null) { return if (modifierList == null) {
createModifierList(annotationEntry.text, owner).annotationEntries.first() createModifierList(annotationEntry.text, owner).annotationEntries.first()
} } else {
else {
modifierList.addBefore(annotationEntry, modifierList.firstChild) as KtAnnotationEntry modifierList.addBefore(annotationEntry, modifierList.firstChild) as KtAnnotationEntry
} }
} }
@@ -71,8 +68,8 @@ internal fun addModifier(modifierList: KtModifierList, modifier: KtModifierKeywo
val newModifier = KtPsiFactory(modifierList).createModifier(modifier) val newModifier = KtPsiFactory(modifierList).createModifier(modifier)
val modifierToReplace = MODIFIERS_TO_REPLACE[modifier] val modifierToReplace = MODIFIERS_TO_REPLACE[modifier]
?.mapNotNull { modifierList.getModifier(it) } ?.mapNotNull { modifierList.getModifier(it) }
?.firstOrNull() ?.firstOrNull()
if (modifier == FINAL_KEYWORD && !modifierList.hasModifier(OVERRIDE_KEYWORD)) { if (modifier == FINAL_KEYWORD && !modifierList.hasModifier(OVERRIDE_KEYWORD)) {
if (modifierToReplace != null) { if (modifierToReplace != null) {
@@ -85,8 +82,7 @@ internal fun addModifier(modifierList: KtModifierList, modifier: KtModifierKeywo
} }
if (modifierToReplace != null && modifierList.firstChild == modifierList.lastChild) { if (modifierToReplace != null && modifierList.firstChild == modifierList.lastChild) {
modifierToReplace.replace(newModifier) modifierToReplace.replace(newModifier)
} } else {
else {
modifierToReplace?.delete() modifierToReplace?.delete()
val newModifierOrder = MODIFIERS_ORDER.indexOf(modifier) val newModifierOrder = MODIFIERS_ORDER.indexOf(modifier)
@@ -129,34 +125,36 @@ fun sortModifiers(modifiers: List<KtModifierKeywordToken>): List<KtModifierKeywo
} }
private val MODIFIERS_TO_REPLACE = mapOf( private val MODIFIERS_TO_REPLACE = mapOf(
OVERRIDE_KEYWORD to listOf(OPEN_KEYWORD), OVERRIDE_KEYWORD to listOf(OPEN_KEYWORD),
ABSTRACT_KEYWORD to listOf(OPEN_KEYWORD, FINAL_KEYWORD), ABSTRACT_KEYWORD to listOf(OPEN_KEYWORD, FINAL_KEYWORD),
OPEN_KEYWORD to listOf(FINAL_KEYWORD, ABSTRACT_KEYWORD), OPEN_KEYWORD to listOf(FINAL_KEYWORD, ABSTRACT_KEYWORD),
FINAL_KEYWORD to listOf(ABSTRACT_KEYWORD, OPEN_KEYWORD), FINAL_KEYWORD to listOf(ABSTRACT_KEYWORD, OPEN_KEYWORD),
PUBLIC_KEYWORD to listOf(PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD), PUBLIC_KEYWORD to listOf(PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
PROTECTED_KEYWORD to listOf(PUBLIC_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD), PROTECTED_KEYWORD to listOf(PUBLIC_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
PRIVATE_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, INTERNAL_KEYWORD), PRIVATE_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, INTERNAL_KEYWORD),
INTERNAL_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD), INTERNAL_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD),
HEADER_KEYWORD to listOf(IMPL_KEYWORD, ACTUAL_KEYWORD, EXPECT_KEYWORD), HEADER_KEYWORD to listOf(IMPL_KEYWORD, ACTUAL_KEYWORD, EXPECT_KEYWORD),
IMPL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD), IMPL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD),
EXPECT_KEYWORD to listOf(IMPL_KEYWORD, ACTUAL_KEYWORD, HEADER_KEYWORD), EXPECT_KEYWORD to listOf(IMPL_KEYWORD, ACTUAL_KEYWORD, HEADER_KEYWORD),
ACTUAL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD) ACTUAL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD)
) )
val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD, val MODIFIERS_ORDER = listOf(
HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD, PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD,
FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD, HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD,
CONST_KEYWORD, FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD,
EXTERNAL_KEYWORD, CONST_KEYWORD,
OVERRIDE_KEYWORD, EXTERNAL_KEYWORD,
LATEINIT_KEYWORD, OVERRIDE_KEYWORD,
TAILREC_KEYWORD, LATEINIT_KEYWORD,
VARARG_KEYWORD, TAILREC_KEYWORD,
SUSPEND_KEYWORD, VARARG_KEYWORD,
INNER_KEYWORD, SUSPEND_KEYWORD,
ENUM_KEYWORD, ANNOTATION_KEYWORD, INNER_KEYWORD,
COMPANION_KEYWORD, ENUM_KEYWORD, ANNOTATION_KEYWORD,
INLINE_KEYWORD, COMPANION_KEYWORD,
INFIX_KEYWORD, INLINE_KEYWORD,
OPERATOR_KEYWORD, INFIX_KEYWORD,
DATA_KEYWORD) OPERATOR_KEYWORD,
DATA_KEYWORD
)
@@ -28,8 +28,7 @@ val SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE: Key<Boolean> = Key.create<Boolean>("SUPP
fun KtElement.suppressDiagnosticsInDebugMode(): Boolean { fun KtElement.suppressDiagnosticsInDebugMode(): Boolean {
return if (this is KtFile) { return if (this is KtFile) {
this.suppressDiagnosticsInDebugMode this.suppressDiagnosticsInDebugMode
} } else {
else {
val file = this.containingFile val file = this.containingFile
file is KtFile && file.suppressDiagnosticsInDebugMode file is KtFile && file.suppressDiagnosticsInDebugMode
} }
@@ -31,23 +31,32 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import java.util.* import java.util.*
fun KtPsiFactory.createExpressionByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): KtExpression fun KtPsiFactory.createExpressionByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): KtExpression =
= createByPattern(pattern, *args, reformat = reformat) { createExpression(it) } createByPattern(pattern, *args, reformat = reformat) { createExpression(it) }
fun KtPsiFactory.createValueArgumentListByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): KtValueArgumentList fun KtPsiFactory.createValueArgumentListByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): KtValueArgumentList =
= createByPattern(pattern, *args, reformat = reformat) { createCallArguments(it) } createByPattern(pattern, *args, reformat = reformat) { createCallArguments(it) }
fun <TDeclaration : KtDeclaration> KtPsiFactory.createDeclarationByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): TDeclaration fun <TDeclaration : KtDeclaration> KtPsiFactory.createDeclarationByPattern(
= createByPattern(pattern, *args, reformat = reformat) { createDeclaration<TDeclaration>(it) } pattern: String,
vararg args: Any,
reformat: Boolean = true
): TDeclaration = createByPattern(pattern, *args, reformat = reformat) { createDeclaration<TDeclaration>(it) }
fun KtPsiFactory.createDestructuringDeclarationByPattern(pattern: String, vararg args: Any, reformat: Boolean = true): KtDestructuringDeclaration fun KtPsiFactory.createDestructuringDeclarationByPattern(
= createByPattern(pattern, *args, reformat = reformat) { createDestructuringDeclaration(it) } pattern: String,
vararg args: Any,
reformat: Boolean = true
): KtDestructuringDeclaration = createByPattern(pattern, *args, reformat = reformat) { createDestructuringDeclaration(it) }
private abstract class ArgumentType<T : Any>(val klass: Class<T>) private abstract class ArgumentType<T : Any>(val klass: Class<T>)
private class PlainTextArgumentType<T : Any>(klass: Class<T>, val toPlainText: (T) -> String) : ArgumentType<T>(klass) private class PlainTextArgumentType<T : Any>(klass: Class<T>, val toPlainText: (T) -> String) : ArgumentType<T>(klass)
private abstract class PsiElementPlaceholderArgumentType<T : Any, TPlaceholder : PsiElement>(klass: Class<T>, val placeholderClass: Class<TPlaceholder>) : ArgumentType<T>(klass) { private abstract class PsiElementPlaceholderArgumentType<T : Any, TPlaceholder : PsiElement>(
klass: Class<T>,
val placeholderClass: Class<TPlaceholder>
) : ArgumentType<T>(klass) {
abstract fun replacePlaceholderElement(placeholder: TPlaceholder, argument: T): PsiChildRange abstract fun replacePlaceholderElement(placeholder: TPlaceholder, argument: T): PsiChildRange
} }
@@ -64,7 +73,8 @@ private class PsiElementArgumentType<T : PsiElement>(klass: Class<T>) : PsiEleme
} }
} }
private object PsiChildRangeArgumentType : PsiElementPlaceholderArgumentType<PsiChildRange, KtElement>(PsiChildRange::class.java, KtElement::class.java) { private object PsiChildRangeArgumentType :
PsiElementPlaceholderArgumentType<PsiChildRange, KtElement>(PsiChildRange::class.java, KtElement::class.java) {
override fun replacePlaceholderElement(placeholder: KtElement, argument: PsiChildRange): PsiChildRange { override fun replacePlaceholderElement(placeholder: KtElement, argument: PsiChildRange): PsiChildRange {
val project = placeholder.project val project = placeholder.project
val codeStyleManager = CodeStyleManager.getInstance(project) val codeStyleManager = CodeStyleManager.getInstance(project)
@@ -79,8 +89,7 @@ private object PsiChildRangeArgumentType : PsiElementPlaceholderArgumentType<Psi
codeStyleManager.reformatNewlyAddedElement(last.node.treeParent, last.node) codeStyleManager.reformatNewlyAddedElement(last.node.treeParent, last.node)
} }
PsiChildRange(first, last) PsiChildRange(first, last)
} } else {
else {
placeholder.delete() placeholder.delete()
PsiChildRange.EMPTY PsiChildRange.EMPTY
} }
@@ -88,20 +97,25 @@ private object PsiChildRangeArgumentType : PsiElementPlaceholderArgumentType<Psi
} }
private val SUPPORTED_ARGUMENT_TYPES = listOf( private val SUPPORTED_ARGUMENT_TYPES = listOf(
PsiElementArgumentType(KtExpression::class.java), PsiElementArgumentType(KtExpression::class.java),
PsiElementArgumentType(KtTypeReference::class.java), PsiElementArgumentType(KtTypeReference::class.java),
PlainTextArgumentType(String::class.java, toPlainText = { it }), PlainTextArgumentType(String::class.java, toPlainText = { it }),
PlainTextArgumentType(Name::class.java, toPlainText = Name::render), PlainTextArgumentType(Name::class.java, toPlainText = Name::render),
PsiChildRangeArgumentType PsiChildRangeArgumentType
) )
@TestOnly @TestOnly
var CREATEBYPATTERN_MAY_NOT_REFORMAT = false var CREATEBYPATTERN_MAY_NOT_REFORMAT = false
fun <TElement : KtElement> createByPattern(pattern: String, vararg args: Any, reformat: Boolean = true, factory: (String) -> TElement): TElement { fun <TElement : KtElement> createByPattern(
pattern: String,
vararg args: Any,
reformat: Boolean = true,
factory: (String) -> TElement
): TElement {
val argumentTypes = args.map { arg -> val argumentTypes = args.map { arg ->
SUPPORTED_ARGUMENT_TYPES.firstOrNull { it.klass.isInstance(arg) } SUPPORTED_ARGUMENT_TYPES.firstOrNull { it.klass.isInstance(arg) }
?: throw IllegalArgumentException("Unsupported argument type: ${arg::class.java}, should be one of: ${SUPPORTED_ARGUMENT_TYPES.joinToString { it.klass.simpleName }}") ?: throw IllegalArgumentException("Unsupported argument type: ${arg::class.java}, should be one of: ${SUPPORTED_ARGUMENT_TYPES.joinToString { it.klass.simpleName }}")
} }
// convert arguments that can be converted into plain text // convert arguments that can be converted into plain text
@@ -155,17 +169,16 @@ fun <TElement : KtElement> createByPattern(pattern: String, vararg args: Any, re
throw java.lang.IllegalArgumentException("Reformatting is not allowed in the current context; please change the invocation to use reformat=false") throw java.lang.IllegalArgumentException("Reformatting is not allowed in the current context; please change the invocation to use reformat=false")
} }
val stringPlaceholderRanges = allPlaceholders val stringPlaceholderRanges = allPlaceholders
.filter { args[it.key] is String } .filter { args[it.key] is String }
.flatMap { it.value } .flatMap { it.value }
.map { it.range } .map { it.range }
.filterNot { it.isEmpty } .filterNot { it.isEmpty }
.sortedByDescending { it.startOffset } .sortedByDescending { it.startOffset }
// reformat whole text except for String arguments (as they can contain user's formatting to be preserved) // reformat whole text except for String arguments (as they can contain user's formatting to be preserved)
resultElement = if (stringPlaceholderRanges.none()) { resultElement = if (stringPlaceholderRanges.none()) {
codeStyleManager.reformat(resultElement, true) as TElement codeStyleManager.reformat(resultElement, true) as TElement
} } else {
else {
var bound = resultElement.endOffset - 1 var bound = resultElement.endOffset - 1
for (range in stringPlaceholderRanges) { for (range in stringPlaceholderRanges) {
// we extend reformatting range by 1 to the right because otherwise some of spaces are not reformatted // we extend reformatting range by 1 to the right because otherwise some of spaces are not reformatted
@@ -223,8 +236,7 @@ private fun processPattern(pattern: String, args: List<Any>): PatternData {
val nextChar = charOrNull(++i) val nextChar = charOrNull(++i)
if (nextChar == '$') { if (nextChar == '$') {
append(nextChar) append(nextChar)
} } else {
else {
check(nextChar?.isDigit() ?: false, "unclosed '$'") check(nextChar?.isDigit() ?: false, "unclosed '$'")
val lastIndex = (i..pattern.length - 1).firstOrNull { !pattern[it].isDigit() } ?: pattern.length val lastIndex = (i..pattern.length - 1).firstOrNull { !pattern[it].isDigit() } ?: pattern.length
@@ -235,8 +247,7 @@ private fun processPattern(pattern: String, args: List<Any>): PatternData {
val arg: Any? = if (n < args.size) args[n] else null /* report wrong number of arguments later */ val arg: Any? = if (n < args.size) args[n] else null /* report wrong number of arguments later */
val placeholderText = if (charOrNull(i) != ':' || charOrNull(i + 1) != '\'') { val placeholderText = if (charOrNull(i) != ':' || charOrNull(i + 1) != '\'') {
arg as? String ?: "xyz" arg as? String ?: "xyz"
} } else {
else {
check(arg !is String, "do not specify placeholder text for $$n - plain text argument passed") check(arg !is String, "do not specify placeholder text for $$n - plain text argument passed")
i += 2 // skip ':' and '\'' i += 2 // skip ':' and '\''
val endIndex = pattern.indexOf('\'', i) val endIndex = pattern.indexOf('\'', i)
@@ -252,8 +263,7 @@ private fun processPattern(pattern: String, args: List<Any>): PatternData {
ranges.getOrPut(n, { ArrayList() }).add(Placeholder(range, placeholderText)) ranges.getOrPut(n, { ArrayList() }).add(Placeholder(range, placeholderText))
continue continue
} }
} } else {
else {
append(c) append(c)
} }
i++ i++
@@ -152,25 +152,24 @@ fun KtExpression.getQualifiedExpressionForReceiverOrThis(): KtExpression {
} }
fun KtExpression.isDotReceiver(): Boolean = fun KtExpression.isDotReceiver(): Boolean =
(parent as? KtDotQualifiedExpression)?.receiverExpression == this (parent as? KtDotQualifiedExpression)?.receiverExpression == this
// ---------- Block expression ------------------------------------------------------------------------------------------------------------- // ---------- Block expression -------------------------------------------------------------------------------------------------------------
fun KtElement.blockExpressionsOrSingle(): Sequence<KtElement> = fun KtElement.blockExpressionsOrSingle(): Sequence<KtElement> =
if (this is KtBlockExpression) statements.asSequence() else sequenceOf(this) if (this is KtBlockExpression) statements.asSequence() else sequenceOf(this)
fun KtExpression.lastBlockStatementOrThis(): KtExpression fun KtExpression.lastBlockStatementOrThis(): KtExpression = (this as? KtBlockExpression)?.statements?.lastOrNull() ?: this
= (this as? KtBlockExpression)?.statements?.lastOrNull() ?: this
fun KtBlockExpression.contentRange(): PsiChildRange { fun KtBlockExpression.contentRange(): PsiChildRange {
val first = (lBrace?.nextSibling ?: firstChild) val first = (lBrace?.nextSibling ?: firstChild)
?.siblings(withItself = false) ?.siblings(withItself = false)
?.firstOrNull { it !is PsiWhiteSpace } ?.firstOrNull { it !is PsiWhiteSpace }
val rBrace = rBrace val rBrace = rBrace
if (first == rBrace) return PsiChildRange.EMPTY if (first == rBrace) return PsiChildRange.EMPTY
val last = rBrace!! val last = rBrace!!
.siblings(forward = false, withItself = false) .siblings(forward = false, withItself = false)
.first { it !is PsiWhiteSpace } .first { it !is PsiWhiteSpace }
if (last == lBrace) return PsiChildRange.EMPTY if (last == lBrace) return PsiChildRange.EMPTY
return PsiChildRange(first, last) return PsiChildRange(first, last)
} }
@@ -248,8 +247,7 @@ fun KtAnnotationsContainer.collectAnnotationEntriesFromStubOrPsi(): List<KtAnnot
} }
private fun StubElement<*>.collectAnnotationEntriesFromStubElement(): List<KtAnnotationEntry> { private fun StubElement<*>.collectAnnotationEntriesFromStubElement(): List<KtAnnotationEntry> {
return childrenStubs.flatMap { return childrenStubs.flatMap { child ->
child ->
when (child.stubType) { when (child.stubType) {
KtNodeTypes.ANNOTATION_ENTRY -> listOf(child.psi as KtAnnotationEntry) KtNodeTypes.ANNOTATION_ENTRY -> listOf(child.psi as KtAnnotationEntry)
KtNodeTypes.ANNOTATION -> (child.psi as KtAnnotation).entries KtNodeTypes.ANNOTATION -> (child.psi as KtAnnotation).entries
@@ -283,14 +281,17 @@ inline fun <reified T : KtElement> forEachDescendantOfTypeVisitor(noinline block
} }
} }
inline fun <reified T : KtElement, R> flatMapDescendantsOfTypeVisitor(accumulator: MutableCollection<R>, noinline map: (T) -> Collection<R>): KtVisitorVoid { inline fun <reified T : KtElement, R> flatMapDescendantsOfTypeVisitor(
accumulator: MutableCollection<R>,
noinline map: (T) -> Collection<R>
): KtVisitorVoid {
return forEachDescendantOfTypeVisitor<T> { accumulator.addAll(map(it)) } return forEachDescendantOfTypeVisitor<T> { accumulator.addAll(map(it)) }
} }
// ----------- Other ----------------------------------------------------------------------------------------------------------------------- // ----------- Other -----------------------------------------------------------------------------------------------------------------------
fun KtClassOrObject.effectiveDeclarations(): List<KtDeclaration> { fun KtClassOrObject.effectiveDeclarations(): List<KtDeclaration> {
return when(this) { return when (this) {
is KtClass -> getDeclarations() + getPrimaryConstructorParameters().filter { p -> p.hasValOrVar() } is KtClass -> getDeclarations() + getPrimaryConstructorParameters().filter { p -> p.hasValOrVar() }
else -> declarations else -> declarations
} }
@@ -368,7 +369,7 @@ fun KtSimpleNameExpression.isCallee(): Boolean {
is KtBinaryExpression -> parent.operationReference == this is KtBinaryExpression -> parent.operationReference == this
else -> { else -> {
val callElement = val callElement =
getStrictParentOfType<KtUserType>() getStrictParentOfType<KtUserType>()
?.getStrictParentOfType<KtTypeReference>() ?.getStrictParentOfType<KtTypeReference>()
?.getStrictParentOfType<KtConstructorCalleeExpression>() ?.getStrictParentOfType<KtConstructorCalleeExpression>()
?.getStrictParentOfType<KtCallElement>() ?.getStrictParentOfType<KtCallElement>()
@@ -376,8 +377,7 @@ fun KtSimpleNameExpression.isCallee(): Boolean {
if (callElement != null) { if (callElement != null) {
val ktConstructorCalleeExpression = callElement.calleeExpression as? KtConstructorCalleeExpression val ktConstructorCalleeExpression = callElement.calleeExpression as? KtConstructorCalleeExpression
(ktConstructorCalleeExpression?.typeReference?.typeElement as? KtUserType)?.referenceExpression == this (ktConstructorCalleeExpression?.typeReference?.typeElement as? KtUserType)?.referenceExpression == this
} } else {
else {
false false
} }
} }
@@ -387,8 +387,7 @@ fun KtSimpleNameExpression.isCallee(): Boolean {
val KtStringTemplateExpression.plainContent: String val KtStringTemplateExpression.plainContent: String
get() = getContentRange().substring(text) get() = getContentRange().substring(text)
fun KtStringTemplateExpression.isSingleQuoted(): Boolean fun KtStringTemplateExpression.isSingleQuoted(): Boolean = node.firstChildNode.textLength == 1
= node.firstChildNode.textLength == 1
fun KtNamedDeclaration.getValueParameters(): List<KtParameter> { fun KtNamedDeclaration.getValueParameters(): List<KtParameter> {
return getValueParameterList()?.parameters ?: Collections.emptyList() return getValueParameterList()?.parameters ?: Collections.emptyList()
@@ -409,13 +408,13 @@ fun KtLambdaArgument.getLambdaArgumentName(bindingContext: BindingContext): Name
} }
fun KtExpression.asAssignment(): KtBinaryExpression? = fun KtExpression.asAssignment(): KtBinaryExpression? =
if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null
private fun KtModifierList.modifierFromTokenSet(set: TokenSet): PsiElement? { private fun KtModifierList.modifierFromTokenSet(set: TokenSet): PsiElement? {
return set.types return set.types
.asSequence() .asSequence()
.map { getModifier(it as KtModifierKeywordToken) } .map { getModifier(it as KtModifierKeywordToken) }
.firstOrNull { it != null } .firstOrNull { it != null }
} }
@@ -423,34 +422,34 @@ private fun KtModifierListOwner.modifierFromTokenSet(set: TokenSet) = modifierLi
fun KtModifierList.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS) fun KtModifierList.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS)
fun KtModifierList.visibilityModifierType(): KtModifierKeywordToken? fun KtModifierList.visibilityModifierType(): KtModifierKeywordToken? = visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
fun KtModifierListOwner.visibilityModifier() = modifierList?.modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS) fun KtModifierListOwner.visibilityModifier() = modifierList?.modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS)
fun KtModifierListOwner.visibilityModifierType(): KtModifierKeywordToken? fun KtModifierListOwner.visibilityModifierType(): KtModifierKeywordToken? =
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken? visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
private val MODALITY_MODIFIERS = TokenSet.create( private val MODALITY_MODIFIERS = TokenSet.create(
KtTokens.ABSTRACT_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.SEALED_KEYWORD, KtTokens.OPEN_KEYWORD KtTokens.ABSTRACT_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.SEALED_KEYWORD, KtTokens.OPEN_KEYWORD
) )
fun KtDeclaration.modalityModifier() = modifierFromTokenSet(MODALITY_MODIFIERS) fun KtDeclaration.modalityModifier() = modifierFromTokenSet(MODALITY_MODIFIERS)
fun KtStringTemplateExpression.isPlain() = entries.all { it is KtLiteralStringTemplateEntry } fun KtStringTemplateExpression.isPlain() = entries.all { it is KtLiteralStringTemplateEntry }
fun KtStringTemplateExpression.isPlainWithEscapes() = entries.all { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry } fun KtStringTemplateExpression.isPlainWithEscapes() =
entries.all { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
// Correct for class members only (including constructors and nested classes) // Correct for class members only (including constructors and nested classes)
// Returns null e.g. for member function parameters, member function locals, property accessors // Returns null e.g. for member function parameters, member function locals, property accessors
val KtDeclaration.containingClassOrObject: KtClassOrObject? val KtDeclaration.containingClassOrObject: KtClassOrObject?
get() = parent.let { get() = parent.let {
when (it) { when (it) {
is KtClassBody -> it.parent as? KtClassOrObject is KtClassBody -> it.parent as? KtClassOrObject
is KtClassOrObject -> it is KtClassOrObject -> it
is KtParameterList -> (it.parent as? KtPrimaryConstructor)?.getContainingClassOrObject() is KtParameterList -> (it.parent as? KtPrimaryConstructor)?.getContainingClassOrObject()
else -> null else -> null
}
} }
}
fun KtExpression.getOutermostParenthesizerOrThis(): KtExpression { fun KtExpression.getOutermostParenthesizerOrThis(): KtExpression {
return (parentsWithSelf.zip(parents)).firstOrNull { return (parentsWithSelf.zip(parents)).firstOrNull {
@@ -506,7 +505,7 @@ fun KtElement.containingClass(): KtClass? = getStrictParentOfType()
fun KtClassOrObject.findPropertyByName(name: String): KtNamedDeclaration? { fun KtClassOrObject.findPropertyByName(name: String): KtNamedDeclaration? {
return declarations.firstOrNull { it is KtProperty && it.name == name } as KtNamedDeclaration? return declarations.firstOrNull { it is KtProperty && it.name == name } as KtNamedDeclaration?
?: primaryConstructorParameters.firstOrNull { it.hasValOrVar() && it.name == name } ?: primaryConstructorParameters.firstOrNull { it.hasValOrVar() && it.name == name }
} }
fun isTypeConstructorReference(e: PsiElement): Boolean { fun isTypeConstructorReference(e: PsiElement): Boolean {
@@ -516,7 +515,8 @@ fun isTypeConstructorReference(e: PsiElement): Boolean {
fun KtParameter.isPropertyParameter() = ownerFunction is KtPrimaryConstructor && hasValOrVar() fun KtParameter.isPropertyParameter() = ownerFunction is KtPrimaryConstructor && hasValOrVar()
fun isDoubleColonReceiver(expression: KtExpression) = expression.getParentOfTypeAndBranch<KtDoubleColonExpression> { this.receiverExpression } != null fun isDoubleColonReceiver(expression: KtExpression) =
expression.getParentOfTypeAndBranch<KtDoubleColonExpression> { this.receiverExpression } != null
fun KtFunctionLiteral.getOrCreateParameterList(): KtParameterList { fun KtFunctionLiteral.getOrCreateParameterList(): KtParameterList {
valueParameterList?.let { return it } valueParameterList?.let { return it }
@@ -535,15 +535,16 @@ fun KtFunctionLiteral.getOrCreateParameterList(): KtParameterList {
fun KtCallExpression.getOrCreateValueArgumentList(): KtValueArgumentList { fun KtCallExpression.getOrCreateValueArgumentList(): KtValueArgumentList {
valueArgumentList?.let { return it } valueArgumentList?.let { return it }
return addAfter(KtPsiFactory(this).createCallArguments("()"), return addAfter(
typeArgumentList ?: calleeExpression) as KtValueArgumentList KtPsiFactory(this).createCallArguments("()"),
typeArgumentList ?: calleeExpression
) as KtValueArgumentList
} }
fun KtCallExpression.addTypeArgument(typeArgument: KtTypeProjection) { fun KtCallExpression.addTypeArgument(typeArgument: KtTypeProjection) {
if (typeArgumentList != null) { if (typeArgumentList != null) {
typeArgumentList?.addArgument(typeArgument) typeArgumentList?.addArgument(typeArgument)
} } else {
else {
addAfter(KtPsiFactory(this).createTypeArguments("<${typeArgument.text}>"), calleeExpression) addAfter(KtPsiFactory(this).createTypeArguments("<${typeArgument.text}>"), calleeExpression)
} }
} }
@@ -556,7 +557,7 @@ fun KtDeclaration.hasBody() = when (this) {
fun KtExpression.referenceExpression(): KtReferenceExpression? = fun KtExpression.referenceExpression(): KtReferenceExpression? =
(if (this is KtCallExpression) calleeExpression else this) as? KtReferenceExpression (if (this is KtCallExpression) calleeExpression else this) as? KtReferenceExpression
fun KtExpression.getLabeledParent(labelName: String): KtLabeledExpression? { fun KtExpression.getLabeledParent(labelName: String): KtLabeledExpression? {
parents.forEach { parents.forEach {
@@ -68,11 +68,9 @@ val PsiElement.parentsWithSelf: Sequence<PsiElement>
val PsiElement.parents: Sequence<PsiElement> val PsiElement.parents: Sequence<PsiElement>
get() = parentsWithSelf.drop(1) get() = parentsWithSelf.drop(1)
fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement? fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement? = PsiTreeUtil.prevLeaf(this, skipEmptyElements)
= PsiTreeUtil.prevLeaf(this, skipEmptyElements)
fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement? fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement? = PsiTreeUtil.nextLeaf(this, skipEmptyElements)
= PsiTreeUtil.nextLeaf(this, skipEmptyElements)
val PsiElement.prevLeafs: Sequence<PsiElement> val PsiElement.prevLeafs: Sequence<PsiElement>
get() = generateSequence({ prevLeaf() }, { it.prevLeaf() }) get() = generateSequence({ prevLeaf() }, { it.prevLeaf() })
@@ -97,7 +95,7 @@ fun PsiElement.nextLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
} }
fun <T : PsiElement> PsiElement.getParentOfTypesAndPredicate( fun <T : PsiElement> PsiElement.getParentOfTypesAndPredicate(
strict: Boolean = false, vararg parentClasses: Class<T>, predicate: (T) -> Boolean strict: Boolean = false, vararg parentClasses: Class<T>, predicate: (T) -> Boolean
): T? { ): T? {
var element = if (strict) parent else this var element = if (strict) parent else this
while (element != null) { while (element != null) {
@@ -175,7 +173,10 @@ inline fun <reified T : PsiElement> PsiElement.getParentOfTypeAndBranch(strict:
return getParentOfType<T>(strict)?.getIfChildIsInBranch(this, branch) return getParentOfType<T>(strict)?.getIfChildIsInBranch(this, branch)
} }
inline fun <reified T : PsiElement> PsiElement.getParentOfTypeAndBranches(strict: Boolean = false, noinline branches: T.() -> Iterable<PsiElement?>): T? { inline fun <reified T : PsiElement> PsiElement.getParentOfTypeAndBranches(
strict: Boolean = false,
noinline branches: T.() -> Iterable<PsiElement?>
): T? {
return getParentOfType<T>(strict)?.getIfChildIsInBranches(this, branches) return getParentOfType<T>(strict)?.getIfChildIsInBranches(this, branches)
} }
@@ -188,7 +189,9 @@ fun PsiElement.isInsideOf(elements: Iterable<PsiElement>): Boolean = elements.an
fun PsiChildRange.trimWhiteSpaces(): PsiChildRange { fun PsiChildRange.trimWhiteSpaces(): PsiChildRange {
if (first == null) return this if (first == null) return this
return PsiChildRange(first.siblings().firstOrNull { it !is PsiWhiteSpace }, last!!.siblings(forward = false).firstOrNull { it !is PsiWhiteSpace }) return PsiChildRange(
first.siblings().firstOrNull { it !is PsiWhiteSpace },
last!!.siblings(forward = false).firstOrNull { it !is PsiWhiteSpace })
} }
// -------------------- Recursive tree visiting -------------------------------------------------------------------------------------------- // -------------------- Recursive tree visiting --------------------------------------------------------------------------------------------
@@ -197,7 +200,10 @@ inline fun <reified T : PsiElement> PsiElement.forEachDescendantOfType(noinline
forEachDescendantOfType({ true }, action) forEachDescendantOfType({ true }, action)
} }
inline fun <reified T : PsiElement> PsiElement.forEachDescendantOfType(crossinline canGoInside: (PsiElement) -> Boolean, noinline action: (T) -> Unit) { inline fun <reified T : PsiElement> PsiElement.forEachDescendantOfType(
crossinline canGoInside: (PsiElement) -> Boolean,
noinline action: (T) -> Unit
) {
this.accept(object : PsiRecursiveElementVisitor() { this.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
if (canGoInside(element)) { if (canGoInside(element)) {
@@ -215,7 +221,10 @@ inline fun <reified T : PsiElement> PsiElement.anyDescendantOfType(noinline pred
return findDescendantOfType(predicate) != null return findDescendantOfType(predicate) != null
} }
inline fun <reified T : PsiElement> PsiElement.anyDescendantOfType(crossinline canGoInside: (PsiElement) -> Boolean, noinline predicate: (T) -> Boolean = { true }): Boolean { inline fun <reified T : PsiElement> PsiElement.anyDescendantOfType(
crossinline canGoInside: (PsiElement) -> Boolean,
noinline predicate: (T) -> Boolean = { true }
): Boolean {
return findDescendantOfType(canGoInside, predicate) != null return findDescendantOfType(canGoInside, predicate) != null
} }
@@ -223,7 +232,10 @@ inline fun <reified T : PsiElement> PsiElement.findDescendantOfType(noinline pre
return findDescendantOfType({ true }, predicate) return findDescendantOfType({ true }, predicate)
} }
inline fun <reified T : PsiElement> PsiElement.findDescendantOfType(crossinline canGoInside: (PsiElement) -> Boolean, noinline predicate: (T) -> Boolean = { true }): T? { inline fun <reified T : PsiElement> PsiElement.findDescendantOfType(
crossinline canGoInside: (PsiElement) -> Boolean,
noinline predicate: (T) -> Boolean = { true }
): T? {
var result: T? = null var result: T? = null
this.accept(object : PsiRecursiveElementWalkingVisitor() { this.accept(object : PsiRecursiveElementWalkingVisitor() {
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
@@ -245,7 +257,10 @@ inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(noinline
return collectDescendantsOfType({ true }, predicate) return collectDescendantsOfType({ true }, predicate)
} }
inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(crossinline canGoInside: (PsiElement) -> Boolean, noinline predicate: (T) -> Boolean = { true }): List<T> { inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(
crossinline canGoInside: (PsiElement) -> Boolean,
noinline predicate: (T) -> Boolean = { true }
): List<T> {
val result = ArrayList<T>() val result = ArrayList<T>()
forEachDescendantOfType<T>(canGoInside) { forEachDescendantOfType<T>(canGoInside) {
if (predicate(it)) { if (predicate(it)) {
@@ -294,11 +309,11 @@ fun PsiFile.elementsInRange(range: TextRange): List<PsiElement> {
val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break
val element = leaf val element = leaf
.parentsWithSelf .parentsWithSelf
.first { .first {
val parent = it.parent val parent = it.parent
it is PsiFile || parent.textRange !in currentRange it is PsiFile || parent.textRange !in currentRange
} }
result.add(element) result.add(element)
offset = element.endOffset offset = element.endOffset
@@ -333,8 +348,8 @@ fun PsiElement.getElementTextWithContext(): String {
} }
// Find parent for element among file children // Find parent for element among file children
val topLevelElement = PsiTreeUtil.findFirstParent(this, { it.parent is PsiFile }) ?: val topLevelElement = PsiTreeUtil.findFirstParent(this, { it.parent is PsiFile })
throw AssertionError("For non-file element we should always be able to find parent in file children") ?: throw AssertionError("For non-file element we should always be able to find parent in file children")
val startContextOffset = topLevelElement.startOffset val startContextOffset = topLevelElement.startOffset
val elementContextOffset = textRange.startOffset val elementContextOffset = textRange.startOffset
@@ -344,9 +359,9 @@ fun PsiElement.getElementTextWithContext(): String {
val isInjected = containingFile is VirtualFileWindow val isInjected = containingFile is VirtualFileWindow
return StringBuilder(topLevelElement.text) return StringBuilder(topLevelElement.text)
.insert(inFileParentOffset, "<caret>") .insert(inFileParentOffset, "<caret>")
.insert(0, "File name: ${containingFile.name} Physical: ${containingFile.isPhysical} Injected: $isInjected\n") .insert(0, "File name: ${containingFile.name} Physical: ${containingFile.isPhysical} Injected: $isInjected\n")
.toString() .toString()
} }
fun PsiElement.getTextWithLocation(): String = "'${this.text}' at ${DiagnosticUtils.atLocation(this)}" fun PsiElement.getTextWithLocation(): String = "'${this.text}' at ${DiagnosticUtils.atLocation(this)}"
@@ -356,7 +371,7 @@ fun replaceFileAnnotationList(file: KtFile, annotationList: KtFileAnnotationList
return file.fileAnnotationList!!.replace(annotationList) as KtFileAnnotationList return file.fileAnnotationList!!.replace(annotationList) as KtFileAnnotationList
} }
val beforeAnchor : PsiElement? = when { val beforeAnchor: PsiElement? = when {
file.packageDirective?.packageKeyword != null -> file.packageDirective!! file.packageDirective?.packageKeyword != null -> file.packageDirective!!
file.importList != null -> file.importList!! file.importList != null -> file.importList!!
file.declarations.firstOrNull() != null -> file.declarations.first() file.declarations.firstOrNull() != null -> file.declarations.first()
@@ -379,7 +394,7 @@ fun replaceFileAnnotationList(file: KtFile, annotationList: KtFileAnnotationList
operator fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isInScope(this, element) operator fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isInScope(this, element)
fun <E : PsiElement> E.createSmartPointer(): SmartPsiElementPointer<E> = fun <E : PsiElement> E.createSmartPointer(): SmartPsiElementPointer<E> =
SmartPointerManager.getInstance(project).createSmartPsiElementPointer(this) SmartPointerManager.getInstance(project).createSmartPsiElementPointer(this)
fun PsiElement.before(element: PsiElement) = textRange.endOffset <= element.textRange.startOffset fun PsiElement.before(element: PsiElement) = textRange.endOffset <= element.textRange.startOffset
@@ -113,7 +113,7 @@ interface KotlinPropertyStub : KotlinCallableStubBase<KtProperty> {
fun hasReturnTypeRef(): Boolean fun hasReturnTypeRef(): Boolean
} }
interface KotlinCallableStubBase<TDeclaration: KtCallableDeclaration> : KotlinStubWithFqName<TDeclaration> { interface KotlinCallableStubBase<TDeclaration : KtCallableDeclaration> : KotlinStubWithFqName<TDeclaration> {
fun isTopLevel(): Boolean fun isTopLevel(): Boolean
fun isExtension(): Boolean fun isExtension(): Boolean
} }
@@ -25,9 +25,10 @@ import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget
import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationUseSiteTargetStub import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationUseSiteTargetStub
import org.jetbrains.kotlin.psi.stubs.impl.KotlinAnnotationUseSiteTargetStubImpl import org.jetbrains.kotlin.psi.stubs.impl.KotlinAnnotationUseSiteTargetStubImpl
class KtAnnotationUseSiteTargetElementType(debugName: String) : KtStubElementType<KotlinAnnotationUseSiteTargetStub, KtAnnotationUseSiteTarget>( class KtAnnotationUseSiteTargetElementType(debugName: String) :
KtStubElementType<KotlinAnnotationUseSiteTargetStub, KtAnnotationUseSiteTarget>(
debugName, KtAnnotationUseSiteTarget::class.java, KotlinAnnotationUseSiteTargetStub::class.java debugName, KtAnnotationUseSiteTarget::class.java, KotlinAnnotationUseSiteTargetStub::class.java
) { ) {
override fun createStub(psi: KtAnnotationUseSiteTarget, parentStub: StubElement<PsiElement>): KotlinAnnotationUseSiteTargetStub { override fun createStub(psi: KtAnnotationUseSiteTarget, parentStub: StubElement<PsiElement>): KotlinAnnotationUseSiteTargetStub {
val useSiteTarget = psi.getAnnotationUseSiteTarget().name val useSiteTarget = psi.getAnnotationUseSiteTarget().name
@@ -25,14 +25,17 @@ import org.jetbrains.kotlin.psi.KtEnumEntrySuperclassReferenceExpression
import org.jetbrains.kotlin.psi.stubs.KotlinEnumEntrySuperclassReferenceExpressionStub import org.jetbrains.kotlin.psi.stubs.KotlinEnumEntrySuperclassReferenceExpressionStub
import org.jetbrains.kotlin.psi.stubs.impl.KotlinEnumEntrySuperclassReferenceExpressionStubImpl import org.jetbrains.kotlin.psi.stubs.impl.KotlinEnumEntrySuperclassReferenceExpressionStubImpl
class KtEnumEntrySuperClassReferenceExpressionElementType(@NonNls debugName: String) class KtEnumEntrySuperClassReferenceExpressionElementType(@NonNls debugName: String) :
: KtStubElementType<KotlinEnumEntrySuperclassReferenceExpressionStub, KtEnumEntrySuperclassReferenceExpression>( KtStubElementType<KotlinEnumEntrySuperclassReferenceExpressionStub, KtEnumEntrySuperclassReferenceExpression>(
debugName, debugName,
KtEnumEntrySuperclassReferenceExpression::class.java, KtEnumEntrySuperclassReferenceExpression::class.java,
KotlinEnumEntrySuperclassReferenceExpressionStub::class.java KotlinEnumEntrySuperclassReferenceExpressionStub::class.java
) { ) {
override fun createStub(psi: KtEnumEntrySuperclassReferenceExpression, parentStub: StubElement<*>): KotlinEnumEntrySuperclassReferenceExpressionStub { override fun createStub(
psi: KtEnumEntrySuperclassReferenceExpression,
parentStub: StubElement<*>
): KotlinEnumEntrySuperclassReferenceExpressionStub {
return KotlinEnumEntrySuperclassReferenceExpressionStubImpl(parentStub, StringRef.fromString(psi.getReferencedName())!!) return KotlinEnumEntrySuperclassReferenceExpressionStubImpl(parentStub, StringRef.fromString(psi.getReferencedName())!!)
} }
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
import org.jetbrains.kotlin.psi.stubs.impl.KotlinImportAliasStubImpl import org.jetbrains.kotlin.psi.stubs.impl.KotlinImportAliasStubImpl
class KtImportAliasElementType(debugName: String) : class KtImportAliasElementType(debugName: String) :
KtStubElementType<KotlinImportAliasStub, KtImportAlias>(debugName, KtImportAlias::class.java, KotlinImportAliasStub::class.java) { KtStubElementType<KotlinImportAliasStub, KtImportAlias>(debugName, KtImportAlias::class.java, KotlinImportAliasStub::class.java) {
override fun createStub(psi: KtImportAlias, parentStub: StubElement<PsiElement>?): KotlinImportAliasStub { override fun createStub(psi: KtImportAlias, parentStub: StubElement<PsiElement>?): KotlinImportAliasStub {
return KotlinImportAliasStubImpl(parentStub, StringRef.fromString(psi.name)) return KotlinImportAliasStubImpl(parentStub, StringRef.fromString(psi.name))
} }
@@ -24,9 +24,9 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinAnnotationEntryStubImpl( class KotlinAnnotationEntryStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val shortName: StringRef, private val shortName: StringRef,
private val hasValueArguments: Boolean private val hasValueArguments: Boolean
) : KotlinStubBaseImpl<KtAnnotationEntry>(parent, KtStubElementTypes.ANNOTATION_ENTRY), KotlinAnnotationEntryStub { ) : KotlinStubBaseImpl<KtAnnotationEntry>(parent, KtStubElementTypes.ANNOTATION_ENTRY), KotlinAnnotationEntryStub {
override fun getShortName() = shortName.string override fun getShortName() = shortName.string
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationUseSiteTargetStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinAnnotationUseSiteTargetStubImpl( class KotlinAnnotationUseSiteTargetStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val target: StringRef private val target: StringRef
) : KotlinStubBaseImpl<KtAnnotationUseSiteTarget>(parent, KtStubElementTypes.ANNOTATION_TARGET), KotlinAnnotationUseSiteTargetStub { ) : KotlinStubBaseImpl<KtAnnotationUseSiteTarget>(parent, KtStubElementTypes.ANNOTATION_TARGET), KotlinAnnotationUseSiteTargetStub {
override fun getUseSiteTarget() = target.string override fun getUseSiteTarget() = target.string
@@ -25,20 +25,21 @@ import org.jetbrains.kotlin.name.FqName
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinFunctionStubImpl( class KotlinFunctionStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val nameRef: StringRef?, private val nameRef: StringRef?,
private val isTopLevel: Boolean, private val isTopLevel: Boolean,
private val fqName: FqName?, private val fqName: FqName?,
private val isExtension: Boolean, private val isExtension: Boolean,
private val hasBlockBody: Boolean, private val hasBlockBody: Boolean,
private val hasBody: Boolean, private val hasBody: Boolean,
private val hasTypeParameterListBeforeFunctionName: Boolean private val hasTypeParameterListBeforeFunctionName: Boolean
) : KotlinStubBaseImpl<KtNamedFunction>(parent, KtStubElementTypes.FUNCTION), KotlinFunctionStub { ) : KotlinStubBaseImpl<KtNamedFunction>(parent, KtStubElementTypes.FUNCTION), KotlinFunctionStub {
init { init {
if (isTopLevel && fqName == null) { if (isTopLevel && fqName == null) {
throw IllegalArgumentException("fqName shouldn't be null for top level functions") throw IllegalArgumentException("fqName shouldn't be null for top level functions")
} }
} }
override fun getFqName() = fqName override fun getFqName() = fqName
override fun getName() = StringRef.toString(nameRef) override fun getName() = StringRef.toString(nameRef)
@@ -25,12 +25,12 @@ import org.jetbrains.kotlin.name.FqName
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinParameterStubImpl( class KotlinParameterStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val fqName: StringRef?, private val fqName: StringRef?,
private val name: StringRef?, private val name: StringRef?,
private val isMutable: Boolean, private val isMutable: Boolean,
private val hasValOrVar: Boolean, private val hasValOrVar: Boolean,
private val hasDefaultValue: Boolean private val hasDefaultValue: Boolean
) : KotlinStubBaseImpl<KtParameter>(parent, KtStubElementTypes.VALUE_PARAMETER), KotlinParameterStub { ) : KotlinStubBaseImpl<KtParameter>(parent, KtStubElementTypes.VALUE_PARAMETER), KotlinParameterStub {
override fun getName(): String? { override fun getName(): String? {
return StringRef.toString(name) return StringRef.toString(name)
@@ -30,7 +30,8 @@ import java.util.ArrayList
val STUB_TO_STRING_PREFIX = "KotlinStub$" val STUB_TO_STRING_PREFIX = "KotlinStub$"
open class KotlinStubBaseImpl<T : KtElementImplStub<*>>(parent: StubElement<*>?, elementType: IStubElementType<*, *>) : StubBase<T>(parent, elementType) { open class KotlinStubBaseImpl<T : KtElementImplStub<*>>(parent: StubElement<*>?, elementType: IStubElementType<*, *>) :
StubBase<T>(parent, elementType) {
override fun toString(): String { override fun toString(): String {
val stubInterface = this::class.java.interfaces.single { it.name.contains("Stub") } val stubInterface = this::class.java.interfaces.single { it.name.contains("Stub") }
@@ -62,8 +63,7 @@ open class KotlinStubBaseImpl<T : KtElementImplStub<*>>(parent: StubElement<*>?,
val value = property.invoke(this) val value = property.invoke(this)
val name = getPropertyName(property) val name = getPropertyName(property)
"$name=$value" "$name=$value"
} } catch (e: Exception) {
catch (e: Exception) {
LOGGER.error(e) LOGGER.error(e)
null null
} }
@@ -80,6 +80,11 @@ open class KotlinStubBaseImpl<T : KtElementImplStub<*>>(parent: StubElement<*>?,
companion object { companion object {
private val LOGGER: Logger = Logger.getInstance(KotlinStubBaseImpl::class.java) private val LOGGER: Logger = Logger.getInstance(KotlinStubBaseImpl::class.java)
private val BASE_STUB_INTERFACES = listOf(KotlinStubWithFqName::class.java, KotlinClassOrObjectStub::class.java, NamedStub::class.java, KotlinCallableStubBase::class.java) private val BASE_STUB_INTERFACES = listOf(
KotlinStubWithFqName::class.java,
KotlinClassOrObjectStub::class.java,
NamedStub::class.java,
KotlinCallableStubBase::class.java
)
} }
} }
@@ -23,5 +23,5 @@ import org.jetbrains.kotlin.psi.stubs.KotlinUserTypeStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinUserTypeStubImpl( class KotlinUserTypeStubImpl(
parent: StubElement<out PsiElement>? parent: StubElement<out PsiElement>?
) : KotlinStubBaseImpl<KtUserType>(parent, KtStubElementTypes.USER_TYPE), KotlinUserTypeStub ) : KotlinStubBaseImpl<KtUserType>(parent, KtStubElementTypes.USER_TYPE), KotlinUserTypeStub
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.psi.stubs.impl
import com.intellij.util.io.StringRef import com.intellij.util.io.StringRef
object Utils { object Utils {
fun wrapStrings(names : List<String>) : Array<StringRef> { fun wrapStrings(names: List<String>): Array<StringRef> {
return Array(names.size) { i -> StringRef.fromString(names[i])!! } return Array(names.size) { i -> StringRef.fromString(names[i])!! }
} }
} }
@@ -45,25 +45,27 @@ import org.jetbrains.kotlin.types.TypeConstructor
* This class has its own synthetic declaration inside. * This class has its own synthetic declaration inside.
*/ */
class SyntheticClassOrObjectDescriptor( class SyntheticClassOrObjectDescriptor(
c: LazyClassContext, c: LazyClassContext,
parentClassOrObject: KtPureClassOrObject, parentClassOrObject: KtPureClassOrObject,
containingDeclaration: DeclarationDescriptor, containingDeclaration: DeclarationDescriptor,
name: Name, name: Name,
source: SourceElement, source: SourceElement,
outerScope: LexicalScope, outerScope: LexicalScope,
private val modality: Modality, private val modality: Modality,
private val visibility: Visibility, private val visibility: Visibility,
constructorVisibility: Visibility, constructorVisibility: Visibility,
private val kind: ClassKind, private val kind: ClassKind,
private val isCompanionObject: Boolean private val isCompanionObject: Boolean
) : ClassDescriptorBase(c.storageManager, containingDeclaration, name, source, false), ClassDescriptorWithResolutionScopes { ) : ClassDescriptorBase(c.storageManager, containingDeclaration, name, source, false), ClassDescriptorWithResolutionScopes {
val syntheticDeclaration: KtPureClassOrObject = SyntheticDeclaration(parentClassOrObject, name.asString()) val syntheticDeclaration: KtPureClassOrObject = SyntheticDeclaration(parentClassOrObject, name.asString())
private val thisDescriptor: SyntheticClassOrObjectDescriptor get() = this // code readability private val thisDescriptor: SyntheticClassOrObjectDescriptor get() = this // code readability
private val typeConstructor = SyntheticTypeConstructor(c.storageManager) private val typeConstructor = SyntheticTypeConstructor(c.storageManager)
private val resolutionScopesSupport = ClassResolutionScopesSupport(thisDescriptor, c.storageManager, { outerScope }) private val resolutionScopesSupport = ClassResolutionScopesSupport(thisDescriptor, c.storageManager, { outerScope })
private val syntheticSupertypes = mutableListOf<KotlinType>().apply { c.syntheticResolveExtension.addSyntheticSupertypes(thisDescriptor, this) } private val syntheticSupertypes =
private val unsubstitutedMemberScope = LazyClassMemberScope(c, SyntheticClassMemberDeclarationProvider(syntheticDeclaration), this, c.trace) mutableListOf<KotlinType>().apply { c.syntheticResolveExtension.addSyntheticSupertypes(thisDescriptor, this) }
private val unsubstitutedMemberScope =
LazyClassMemberScope(c, SyntheticClassMemberDeclarationProvider(syntheticDeclaration), this, c.trace)
private val unsubstitutedPrimaryConstructor = createUnsubstitutedPrimaryConstructor(constructorVisibility) private val unsubstitutedPrimaryConstructor = createUnsubstitutedPrimaryConstructor(constructorVisibility)
override val annotations: Annotations get() = Annotations.EMPTY override val annotations: Annotations get() = Annotations.EMPTY
@@ -97,11 +99,15 @@ class SyntheticClassOrObjectDescriptor(
override fun getScopeForClassHeaderResolution(): LexicalScope = resolutionScopesSupport.scopeForClassHeaderResolution() override fun getScopeForClassHeaderResolution(): LexicalScope = resolutionScopesSupport.scopeForClassHeaderResolution()
override fun getScopeForConstructorHeaderResolution(): LexicalScope = resolutionScopesSupport.scopeForConstructorHeaderResolution() override fun getScopeForConstructorHeaderResolution(): LexicalScope = resolutionScopesSupport.scopeForConstructorHeaderResolution()
override fun getScopeForCompanionObjectHeaderResolution(): LexicalScope = resolutionScopesSupport.scopeForCompanionObjectHeaderResolution() override fun getScopeForCompanionObjectHeaderResolution(): LexicalScope =
override fun getScopeForMemberDeclarationResolution(): LexicalScope = resolutionScopesSupport.scopeForMemberDeclarationResolution() resolutionScopesSupport.scopeForCompanionObjectHeaderResolution()
override fun getScopeForStaticMemberDeclarationResolution(): LexicalScope = resolutionScopesSupport.scopeForStaticMemberDeclarationResolution()
override fun getScopeForInitializerResolution(): LexicalScope = throw UnsupportedOperationException("Not supported for synthetic class or object") override fun getScopeForMemberDeclarationResolution(): LexicalScope = resolutionScopesSupport.scopeForMemberDeclarationResolution()
override fun getScopeForStaticMemberDeclarationResolution(): LexicalScope =
resolutionScopesSupport.scopeForStaticMemberDeclarationResolution()
override fun getScopeForInitializerResolution(): LexicalScope =
throw UnsupportedOperationException("Not supported for synthetic class or object")
override fun toString(): String = "synthetic class " + name.toString() + " in " + containingDeclaration override fun toString(): String = "synthetic class " + name.toString() + " in " + containingDeclaration
@@ -121,7 +127,7 @@ class SyntheticClassOrObjectDescriptor(
} }
private class SyntheticClassMemberDeclarationProvider( private class SyntheticClassMemberDeclarationProvider(
override val correspondingClassOrObject: KtPureClassOrObject override val correspondingClassOrObject: KtPureClassOrObject
) : ClassMemberDeclarationProvider { ) : ClassMemberDeclarationProvider {
override val ownerInfo: KtClassLikeInfo? = null override val ownerInfo: KtClassLikeInfo? = null
override fun getDeclarations(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): List<KtDeclaration> = emptyList() override fun getDeclarations(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): List<KtDeclaration> = emptyList()
@@ -134,8 +140,8 @@ class SyntheticClassOrObjectDescriptor(
} }
internal inner class SyntheticDeclaration( internal inner class SyntheticDeclaration(
private val _parent: KtPureElement, private val _parent: KtPureElement,
private val _name: String private val _name: String
) : KtPureClassOrObject { ) : KtPureClassOrObject {
fun descriptor() = thisDescriptor fun descriptor() = thisDescriptor
@@ -28,14 +28,13 @@ import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable
class AllUnderImportScope( class AllUnderImportScope(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
excludedImportNames: Collection<FqName> excludedImportNames: Collection<FqName>
) : BaseImportingScope(null) { ) : BaseImportingScope(null) {
private val scopes: List<MemberScope> = if (descriptor is ClassDescriptor) { private val scopes: List<MemberScope> = if (descriptor is ClassDescriptor) {
listOf(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope) listOf(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope)
} } else {
else {
assert(descriptor is PackageViewDescriptor) { assert(descriptor is PackageViewDescriptor) {
"Must be class or package view descriptor: $descriptor" "Must be class or package view descriptor: $descriptor"
} }
@@ -44,8 +43,7 @@ class AllUnderImportScope(
private val excludedNames: Set<Name> = if (excludedImportNames.isEmpty()) { // optimization private val excludedNames: Set<Name> = if (excludedImportNames.isEmpty()) { // optimization
emptySet<Name>() emptySet<Name>()
} } else {
else {
val fqName = DescriptorUtils.getFqNameSafe(descriptor) val fqName = DescriptorUtils.getFqNameSafe(descriptor)
// toSet() is used here instead mapNotNullTo(hashSetOf()) because it results in not keeping empty sets as separate instances // toSet() is used here instead mapNotNullTo(hashSetOf()) because it results in not keeping empty sets as separate instances
excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet() excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet()
@@ -54,21 +52,20 @@ class AllUnderImportScope(
override fun computeImportedNames(): Set<Name>? = scopes.flatMapToNullable(hashSetOf(), MemberScope::computeAllNames) override fun computeImportedNames(): Set<Name>? = scopes.flatMapToNullable(hashSetOf(), MemberScope::computeAllNames)
override fun getContributedDescriptors( override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter, kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean, nameFilter: (Name) -> Boolean,
changeNamesForAliased: Boolean changeNamesForAliased: Boolean
): Collection<DeclarationDescriptor> { ): Collection<DeclarationDescriptor> {
val nameFilterToUse = if (excludedNames.isEmpty()) { // optimization val nameFilterToUse = if (excludedNames.isEmpty()) { // optimization
nameFilter nameFilter
} } else {
else {
{ it !in excludedNames && nameFilter(it) } { it !in excludedNames && nameFilter(it) }
} }
val noPackagesKindFilter = kindFilter.withoutKinds(DescriptorKindFilter.PACKAGES_MASK) val noPackagesKindFilter = kindFilter.withoutKinds(DescriptorKindFilter.PACKAGES_MASK)
return scopes return scopes
.flatMap { it.getContributedDescriptors(noPackagesKindFilter, nameFilterToUse) } .flatMap { it.getContributedDescriptors(noPackagesKindFilter, nameFilterToUse) }
.filter { it !is PackageViewDescriptor } // subpackages are not imported .filter { it !is PackageViewDescriptor } // subpackages are not imported
} }
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.* import java.util.*
class CompositeBindingContext private constructor( class CompositeBindingContext private constructor(
private val delegates: LinkedHashSet<BindingContext> private val delegates: LinkedHashSet<BindingContext>
) : BindingContext { ) : BindingContext {
override fun getType(expression: KtExpression): KotlinType? { override fun getType(expression: KtExpression): KotlinType? {
return delegates.asSequence().map { it.getType(expression) }.firstOrNull { it != null } return delegates.asSequence().map { it.getType(expression) }.firstOrNull { it != null }
@@ -67,7 +67,7 @@ class CompositeBindingContext private constructor(
} }
private class CompositeDiagnostics( private class CompositeDiagnostics(
private val delegates: List<Diagnostics> private val delegates: List<Diagnostics>
) : Diagnostics { ) : Diagnostics {
override fun iterator(): Iterator<Diagnostic> { override fun iterator(): Iterator<Diagnostic> {
@@ -34,22 +34,18 @@ object DataClassDescriptorResolver {
private val COMPONENT_FUNCTION_NAME_PREFIX = "component" private val COMPONENT_FUNCTION_NAME_PREFIX = "component"
fun createComponentName(index: Int): Name fun createComponentName(index: Int): Name = Name.identifier(COMPONENT_FUNCTION_NAME_PREFIX + index)
= Name.identifier(COMPONENT_FUNCTION_NAME_PREFIX + index)
fun getComponentIndex(componentName: String): Int fun getComponentIndex(componentName: String): Int = componentName.substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
= componentName.substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
fun isComponentLike(name: Name): Boolean fun isComponentLike(name: Name): Boolean = isComponentLike(name.asString())
= isComponentLike(name.asString())
private fun isComponentLike(name: String): Boolean { private fun isComponentLike(name: String): Boolean {
if (!name.startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false if (!name.startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false
try { try {
getComponentIndex(name) getComponentIndex(name)
} } catch (e: NumberFormatException) {
catch (e: NumberFormatException) {
return false return false
} }
@@ -57,58 +53,58 @@ object DataClassDescriptorResolver {
} }
fun createEqualsFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor = fun createEqualsFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateFunctionFromAny(classDescriptor, EQUALS_METHOD_NAME) doCreateFunctionFromAny(classDescriptor, EQUALS_METHOD_NAME)
fun createHashCodeFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor = fun createHashCodeFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateFunctionFromAny(classDescriptor, HASH_CODE_METHOD_NAME) doCreateFunctionFromAny(classDescriptor, HASH_CODE_METHOD_NAME)
fun createToStringFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor = fun createToStringFunctionDescriptor(classDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateFunctionFromAny(classDescriptor, TO_STRING_METHOD_NAME) doCreateFunctionFromAny(classDescriptor, TO_STRING_METHOD_NAME)
private fun doCreateFunctionFromAny(classDescriptor: ClassDescriptor, name: Name): SimpleFunctionDescriptor { private fun doCreateFunctionFromAny(classDescriptor: ClassDescriptor, name: Name): SimpleFunctionDescriptor {
val functionDescriptor = SimpleFunctionDescriptorImpl.create( val functionDescriptor = SimpleFunctionDescriptorImpl.create(
classDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, classDescriptor.source classDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, classDescriptor.source
) )
val functionFromAny = classDescriptor.builtIns.any.getMemberScope(emptyList()) val functionFromAny = classDescriptor.builtIns.any.getMemberScope(emptyList())
.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single() .getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
functionDescriptor.initialize( functionDescriptor.initialize(
null, null,
classDescriptor.thisAsReceiverParameter, classDescriptor.thisAsReceiverParameter,
functionFromAny.typeParameters, functionFromAny.typeParameters,
functionFromAny.valueParameters.map { it.copy(functionDescriptor, it.name, it.index) }, functionFromAny.valueParameters.map { it.copy(functionDescriptor, it.name, it.index) },
functionFromAny.returnType, functionFromAny.returnType,
Modality.OPEN, Modality.OPEN,
Visibilities.PUBLIC Visibilities.PUBLIC
) )
return functionDescriptor return functionDescriptor
} }
fun createComponentFunctionDescriptor( fun createComponentFunctionDescriptor(
parameterIndex: Int, parameterIndex: Int,
property: PropertyDescriptor, property: PropertyDescriptor,
parameter: ValueParameterDescriptor, parameter: ValueParameterDescriptor,
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
trace: BindingTrace trace: BindingTrace
): SimpleFunctionDescriptor { ): SimpleFunctionDescriptor {
val functionDescriptor = SimpleFunctionDescriptorImpl.create( val functionDescriptor = SimpleFunctionDescriptorImpl.create(
classDescriptor, classDescriptor,
Annotations.EMPTY, Annotations.EMPTY,
createComponentName(parameterIndex), createComponentName(parameterIndex),
CallableMemberDescriptor.Kind.SYNTHESIZED, CallableMemberDescriptor.Kind.SYNTHESIZED,
parameter.source parameter.source
) )
functionDescriptor.initialize( functionDescriptor.initialize(
null, null,
classDescriptor.thisAsReceiverParameter, classDescriptor.thisAsReceiverParameter,
emptyList<TypeParameterDescriptor>(), emptyList<TypeParameterDescriptor>(),
emptyList<ValueParameterDescriptor>(), emptyList<ValueParameterDescriptor>(),
property.type, property.type,
Modality.FINAL, Modality.FINAL,
property.visibility property.visibility
) )
functionDescriptor.isOperator = true functionDescriptor.isOperator = true
@@ -117,16 +113,16 @@ object DataClassDescriptorResolver {
} }
fun createCopyFunctionDescriptor( fun createCopyFunctionDescriptor(
constructorParameters: Collection<ValueParameterDescriptor>, constructorParameters: Collection<ValueParameterDescriptor>,
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
trace: BindingTrace trace: BindingTrace
): SimpleFunctionDescriptor { ): SimpleFunctionDescriptor {
val functionDescriptor = SimpleFunctionDescriptorImpl.create( val functionDescriptor = SimpleFunctionDescriptorImpl.create(
classDescriptor, classDescriptor,
Annotations.EMPTY, Annotations.EMPTY,
COPY_METHOD_NAME, COPY_METHOD_NAME,
CallableMemberDescriptor.Kind.SYNTHESIZED, CallableMemberDescriptor.Kind.SYNTHESIZED,
classDescriptor.source classDescriptor.source
) )
val parameterDescriptors = arrayListOf<ValueParameterDescriptor>() val parameterDescriptors = arrayListOf<ValueParameterDescriptor>()
@@ -136,8 +132,8 @@ object DataClassDescriptorResolver {
// If a parameter doesn't have the corresponding property, it must not have a default value in the 'copy' function // If a parameter doesn't have the corresponding property, it must not have a default value in the 'copy' function
val declaresDefaultValue = propertyDescriptor != null val declaresDefaultValue = propertyDescriptor != null
val parameterDescriptor = ValueParameterDescriptorImpl( val parameterDescriptor = ValueParameterDescriptorImpl(
functionDescriptor, null, parameter.index, parameter.annotations, parameter.name, parameter.type, declaresDefaultValue, functionDescriptor, null, parameter.index, parameter.annotations, parameter.name, parameter.type, declaresDefaultValue,
parameter.isCrossinline, parameter.isNoinline, parameter.varargElementType, parameter.source parameter.isCrossinline, parameter.isNoinline, parameter.varargElementType, parameter.source
) )
parameterDescriptors.add(parameterDescriptor) parameterDescriptors.add(parameterDescriptor)
if (declaresDefaultValue) { if (declaresDefaultValue) {
@@ -146,13 +142,13 @@ object DataClassDescriptorResolver {
} }
functionDescriptor.initialize( functionDescriptor.initialize(
null, null,
classDescriptor.thisAsReceiverParameter, classDescriptor.thisAsReceiverParameter,
emptyList<TypeParameterDescriptor>(), emptyList<TypeParameterDescriptor>(),
parameterDescriptors, parameterDescriptors,
classDescriptor.defaultType, classDescriptor.defaultType,
Modality.FINAL, Modality.FINAL,
Visibilities.PUBLIC Visibilities.PUBLIC
) )
trace.record(BindingContext.DATA_CLASS_COPY_FUNCTION, classDescriptor, functionDescriptor) trace.record(BindingContext.DATA_CLASS_COPY_FUNCTION, classDescriptor, functionDescriptor)
@@ -38,8 +38,8 @@ import org.jetbrains.kotlin.utils.keysToMap
import java.util.* import java.util.*
class DeclarationResolver( class DeclarationResolver(
private val annotationResolver: AnnotationResolver, private val annotationResolver: AnnotationResolver,
private val trace: BindingTrace private val trace: BindingTrace
) { ) {
fun resolveAnnotationsOnFiles(c: TopDownAnalysisContext, scopeProvider: FileScopeProvider) { fun resolveAnnotationsOnFiles(c: TopDownAnalysisContext, scopeProvider: FileScopeProvider) {
@@ -74,14 +74,17 @@ class DeclarationResolver(
} }
} }
fun checkRedeclarationsInPackages(topLevelDescriptorProvider: TopLevelDescriptorProvider, topLevelFqNames: Multimap<FqName, KtElement>) { fun checkRedeclarationsInPackages(
topLevelDescriptorProvider: TopLevelDescriptorProvider,
topLevelFqNames: Multimap<FqName, KtElement>
) {
for ((fqName, declarationsOrPackageDirectives) in topLevelFqNames.asMap()) { for ((fqName, declarationsOrPackageDirectives) in topLevelFqNames.asMap()) {
if (fqName.isRoot) continue if (fqName.isRoot) continue
// TODO: report error on expected class and actual val, or vice versa // TODO: report error on expected class and actual val, or vice versa
val (expected, actual) = val (expected, actual) =
getTopLevelDescriptorsByFqName(topLevelDescriptorProvider, fqName, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS) getTopLevelDescriptorsByFqName(topLevelDescriptorProvider, fqName, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS)
.partition { it is MemberDescriptor && it.isExpect } .partition { it is MemberDescriptor && it.isExpect }
for (descriptors in listOf(expected, actual)) { for (descriptors in listOf(expected, actual)) {
if (descriptors.size > 1) { if (descriptors.size > 1) {
@@ -94,7 +97,11 @@ class DeclarationResolver(
} }
} }
private fun getTopLevelDescriptorsByFqName(topLevelDescriptorProvider: TopLevelDescriptorProvider, fqName: FqName, location: LookupLocation): Set<DeclarationDescriptor> { private fun getTopLevelDescriptorsByFqName(
topLevelDescriptorProvider: TopLevelDescriptorProvider,
fqName: FqName,
location: LookupLocation
): Set<DeclarationDescriptor> {
val descriptors = HashSet<DeclarationDescriptor>() val descriptors = HashSet<DeclarationDescriptor>()
descriptors.addIfNotNull(topLevelDescriptorProvider.getPackageFragment(fqName)) descriptors.addIfNotNull(topLevelDescriptorProvider.getPackageFragment(fqName))
@@ -45,29 +45,29 @@ import org.jetbrains.kotlin.types.typeUtil.*
import java.util.* import java.util.*
internal class DeclarationsCheckerBuilder( internal class DeclarationsCheckerBuilder(
private val descriptorResolver: DescriptorResolver, private val descriptorResolver: DescriptorResolver,
private val originalModifiersChecker: ModifiersChecker, private val originalModifiersChecker: ModifiersChecker,
private val annotationChecker: AnnotationChecker, private val annotationChecker: AnnotationChecker,
private val identifierChecker: IdentifierChecker, private val identifierChecker: IdentifierChecker,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val typeSpecificityComparator: TypeSpecificityComparator, private val typeSpecificityComparator: TypeSpecificityComparator,
private val diagnosticSuppressor: PlatformDiagnosticSuppressor private val diagnosticSuppressor: PlatformDiagnosticSuppressor
) { ) {
fun withTrace(trace: BindingTrace) = DeclarationsChecker( fun withTrace(trace: BindingTrace) = DeclarationsChecker(
descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace, languageVersionSettings, descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace, languageVersionSettings,
typeSpecificityComparator, diagnosticSuppressor typeSpecificityComparator, diagnosticSuppressor
) )
} }
class DeclarationsChecker( class DeclarationsChecker(
private val descriptorResolver: DescriptorResolver, private val descriptorResolver: DescriptorResolver,
modifiersChecker: ModifiersChecker, modifiersChecker: ModifiersChecker,
private val annotationChecker: AnnotationChecker, private val annotationChecker: AnnotationChecker,
private val identifierChecker: IdentifierChecker, private val identifierChecker: IdentifierChecker,
private val trace: BindingTrace, private val trace: BindingTrace,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
typeSpecificityComparator: TypeSpecificityComparator, typeSpecificityComparator: TypeSpecificityComparator,
private val diagnosticSuppressor: PlatformDiagnosticSuppressor private val diagnosticSuppressor: PlatformDiagnosticSuppressor
) { ) {
private val modifiersChecker = modifiersChecker.withTrace(trace) private val modifiersChecker = modifiersChecker.withTrace(trace)
@@ -102,9 +102,9 @@ class DeclarationsChecker(
} }
val destructuringDeclarations = bodiesResolveContext.destructuringDeclarationEntries.entries val destructuringDeclarations = bodiesResolveContext.destructuringDeclarationEntries.entries
.map { (entry, _) -> entry.parent } .map { (entry, _) -> entry.parent }
.filterIsInstance<KtDestructuringDeclaration>() .filterIsInstance<KtDestructuringDeclaration>()
.distinct() .distinct()
for (multiDeclaration in destructuringDeclarations) { for (multiDeclaration in destructuringDeclarations) {
modifiersChecker.checkModifiersForDestructuringDeclaration(multiDeclaration) modifiersChecker.checkModifiersForDestructuringDeclaration(multiDeclaration)
@@ -143,7 +143,7 @@ class DeclarationsChecker(
trace.report(TYPEALIAS_SHOULD_EXPAND_TO_CLASS.on(typeReference, expandedType)) trace.report(TYPEALIAS_SHOULD_EXPAND_TO_CLASS.on(typeReference, expandedType))
} }
if (TypeUtils.contains(expandedType) { it.isArrayOfNothing()} ) { if (TypeUtils.contains(expandedType) { it.isArrayOfNothing() }) {
trace.report(TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE.on(typeReference, expandedType, "Array<Nothing> is illegal")) trace.report(TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE.on(typeReference, expandedType, "Array<Nothing> is illegal"))
} }
@@ -151,7 +151,7 @@ class DeclarationsChecker(
for (typeParameter in typeAliasDescriptor.declaredTypeParameters) { for (typeParameter in typeAliasDescriptor.declaredTypeParameters) {
if (typeParameter !in usedTypeAliasParameters) { if (typeParameter !in usedTypeAliasParameters) {
val source = DescriptorToSourceUtils.descriptorToDeclaration(typeParameter) as? KtTypeParameter val source = DescriptorToSourceUtils.descriptorToDeclaration(typeParameter) as? KtTypeParameter
?: throw AssertionError("No source element for type parameter $typeParameter of $typeAliasDescriptor") ?: throw AssertionError("No source element for type parameter $typeParameter of $typeAliasDescriptor")
trace.report(UNUSED_TYPEALIAS_PARAMETER.on(source, typeParameter, expandedType)) trace.report(UNUSED_TYPEALIAS_PARAMETER.on(source, typeParameter, expandedType))
} }
} }
@@ -180,31 +180,35 @@ class DeclarationsChecker(
} }
if (rhs.arguments.map { it.type.constructor.declarationDescriptor as? TypeParameterDescriptor } != if (rhs.arguments.map { it.type.constructor.declarationDescriptor as? TypeParameterDescriptor } !=
typeAliasDescriptor.declaredTypeParameters) { typeAliasDescriptor.declaredTypeParameters) {
trace.report(ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION.on(declaration)) trace.report(ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION.on(declaration))
return return
} }
} }
private fun getUsedTypeAliasParameters(type: KotlinType, typeAlias: TypeAliasDescriptor): Set<TypeParameterDescriptor> = private fun getUsedTypeAliasParameters(type: KotlinType, typeAlias: TypeAliasDescriptor): Set<TypeParameterDescriptor> =
type.constituentTypes().mapNotNullTo(HashSet()) { type.constituentTypes().mapNotNullTo(HashSet()) {
val descriptor = it.constructor.declarationDescriptor as? TypeParameterDescriptor val descriptor = it.constructor.declarationDescriptor as? TypeParameterDescriptor
descriptor?.takeIf { it.containingDeclaration == typeAlias } descriptor?.takeIf { it.containingDeclaration == typeAlias }
} }
private class TypeAliasDeclarationCheckingReportStrategy( private class TypeAliasDeclarationCheckingReportStrategy(
private val trace: BindingTrace, private val trace: BindingTrace,
typeAliasDescriptor: TypeAliasDescriptor, typeAliasDescriptor: TypeAliasDescriptor,
declaration: KtTypeAlias declaration: KtTypeAlias
) : TypeAliasExpansionReportStrategy { ) : TypeAliasExpansionReportStrategy {
private val typeReference = declaration.getTypeReference() private val typeReference = declaration.getTypeReference()
?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor") ?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor")
override fun wrongNumberOfTypeArguments(typeAlias: TypeAliasDescriptor, numberOfParameters: Int) { override fun wrongNumberOfTypeArguments(typeAlias: TypeAliasDescriptor, numberOfParameters: Int) {
// Do nothing: this should've been reported during type resolution. // Do nothing: this should've been reported during type resolution.
} }
override fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType) { override fun conflictingProjection(
typeAlias: TypeAliasDescriptor,
typeParameter: TypeParameterDescriptor?,
substitutedArgument: KotlinType
) {
trace.report(CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION.on(typeReference, substitutedArgument)) trace.report(CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION.on(typeReference, substitutedArgument))
} }
@@ -212,7 +216,12 @@ class DeclarationsChecker(
trace.report(RECURSIVE_TYPEALIAS_EXPANSION.on(typeReference, typeAlias)) trace.report(RECURSIVE_TYPEALIAS_EXPANSION.on(typeReference, typeAlias))
} }
override fun boundsViolationInSubstitution(bound: KotlinType, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor) { override fun boundsViolationInSubstitution(
bound: KotlinType,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
) {
// TODO more precise diagnostics // TODO more precise diagnostics
if (!argument.containsTypeAliasParameters() && !bound.containsTypeAliasParameters()) { if (!argument.containsTypeAliasParameters() && !bound.containsTypeAliasParameters()) {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(typeReference, bound, argument, typeParameter)) trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(typeReference, bound, argument, typeParameter))
@@ -272,8 +281,7 @@ class DeclarationsChecker(
val classDescriptor = constructorDescriptor.containingDeclaration val classDescriptor = constructorDescriptor.containingDeclaration
if (classDescriptor.kind == ClassKind.ENUM_CLASS) { if (classDescriptor.kind == ClassKind.ENUM_CLASS) {
trace.report(NON_PRIVATE_CONSTRUCTOR_IN_ENUM.on(visibilityModifier)) trace.report(NON_PRIVATE_CONSTRUCTOR_IN_ENUM.on(visibilityModifier))
} } else if (classDescriptor.modality == Modality.SEALED) {
else if (classDescriptor.modality == Modality.SEALED) {
trace.report(NON_PRIVATE_CONSTRUCTOR_IN_SEALED.on(visibilityModifier)) trace.report(NON_PRIVATE_CONSTRUCTOR_IN_SEALED.on(visibilityModifier))
} }
} }
@@ -301,7 +309,8 @@ class DeclarationsChecker(
is KtClass -> { is KtClass -> {
checkClassButNotObject(classOrObject, classDescriptor) checkClassButNotObject(classOrObject, classDescriptor)
descriptorResolver.checkNamesInConstraints( descriptorResolver.checkNamesInConstraints(
classOrObject, classDescriptor, classDescriptor.scopeForClassHeaderResolution, trace) classOrObject, classDescriptor, classDescriptor.scopeForClassHeaderResolution, trace
)
} }
is KtObjectDeclaration -> { is KtObjectDeclaration -> {
checkObject(classOrObject, classDescriptor) checkObject(classOrObject, classDescriptor)
@@ -346,32 +355,31 @@ class DeclarationsChecker(
} }
private fun checkOnlyOneTypeParameterBound( private fun checkOnlyOneTypeParameterBound(
descriptor: TypeParameterDescriptor, declaration: KtTypeParameter, owner: KtTypeParameterListOwner descriptor: TypeParameterDescriptor, declaration: KtTypeParameter, owner: KtTypeParameterListOwner
) { ) {
val upperBounds = descriptor.upperBounds val upperBounds = descriptor.upperBounds
val (boundsWhichAreTypeParameters, otherBounds) = upperBounds val (boundsWhichAreTypeParameters, otherBounds) = upperBounds
.map(KotlinType::constructor) .map(KotlinType::constructor)
.partition { constructor -> constructor.declarationDescriptor is TypeParameterDescriptor } .partition { constructor -> constructor.declarationDescriptor is TypeParameterDescriptor }
.let { pair -> pair.first.toSet() to pair.second.toSet() } .let { pair -> pair.first.toSet() to pair.second.toSet() }
if (boundsWhichAreTypeParameters.size > 1 || (boundsWhichAreTypeParameters.size == 1 && otherBounds.isNotEmpty())) { if (boundsWhichAreTypeParameters.size > 1 || (boundsWhichAreTypeParameters.size == 1 && otherBounds.isNotEmpty())) {
val reportOn = if (boundsWhichAreTypeParameters.size + otherBounds.size == 2) { val reportOn = if (boundsWhichAreTypeParameters.size + otherBounds.size == 2) {
// If there's only one problematic bound (either 2 type parameter bounds, or 1 type parameter bound + 1 other bound), // If there's only one problematic bound (either 2 type parameter bounds, or 1 type parameter bound + 1 other bound),
// report the diagnostic on that bound // report the diagnostic on that bound
val allBounds: List<Pair<KtTypeReference, KotlinType?>> = val allBounds: List<Pair<KtTypeReference, KotlinType?>> =
owner.typeConstraints owner.typeConstraints
.filter { constraint -> .filter { constraint ->
constraint.subjectTypeParameterName?.getReferencedNameAsName() == declaration.nameAsName constraint.subjectTypeParameterName?.getReferencedNameAsName() == declaration.nameAsName
} }
.mapNotNull { constraint -> constraint.boundTypeReference } .mapNotNull { constraint -> constraint.boundTypeReference }
.map { typeReference -> typeReference to trace.bindingContext.get(TYPE, typeReference) } .map { typeReference -> typeReference to trace.bindingContext.get(TYPE, typeReference) }
val problematicBound = val problematicBound =
allBounds.firstOrNull { bound -> bound.second?.constructor != boundsWhichAreTypeParameters.first() } allBounds.firstOrNull { bound -> bound.second?.constructor != boundsWhichAreTypeParameters.first() }
problematicBound?.first ?: declaration problematicBound?.first ?: declaration
} } else {
else {
// Otherwise report the diagnostic on the type parameter declaration // Otherwise report the diagnostic on the type parameter declaration
declaration declaration
} }
@@ -400,17 +408,20 @@ class DeclarationsChecker(
if (conflictingTypes.size <= 1) continue if (conflictingTypes.size <= 1) continue
val containingDeclaration = typeParameterDescriptor.containingDeclaration as? ClassDescriptor val containingDeclaration = typeParameterDescriptor.containingDeclaration as? ClassDescriptor
?: throw AssertionError("Not a class descriptor: " + typeParameterDescriptor.containingDeclaration) ?: throw AssertionError("Not a class descriptor: " + typeParameterDescriptor.containingDeclaration)
if (sourceElement is KtClassOrObject) { if (sourceElement is KtClassOrObject) {
val delegationSpecifierList = sourceElement.getSuperTypeList() ?: continue val delegationSpecifierList = sourceElement.getSuperTypeList() ?: continue
trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES.on( trace.report(
INCONSISTENT_TYPE_PARAMETER_VALUES.on(
delegationSpecifierList, typeParameterDescriptor, containingDeclaration, conflictingTypes delegationSpecifierList, typeParameterDescriptor, containingDeclaration, conflictingTypes
)) )
} )
else if (sourceElement is KtTypeParameter) { } else if (sourceElement is KtTypeParameter) {
trace.report(INCONSISTENT_TYPE_PARAMETER_BOUNDS.on( trace.report(
INCONSISTENT_TYPE_PARAMETER_BOUNDS.on(
sourceElement, typeParameterDescriptor, containingDeclaration, conflictingTypes sourceElement, typeParameterDescriptor, containingDeclaration, conflictingTypes
)) )
)
} }
} }
} }
@@ -521,8 +532,7 @@ class DeclarationsChecker(
for (parameter in aClass.primaryConstructorParameters) { for (parameter in aClass.primaryConstructorParameters) {
if (!parameter.hasValOrVar()) { if (!parameter.hasValOrVar()) {
trace.report(MISSING_VAL_ON_ANNOTATION_PARAMETER.on(parameter)) trace.report(MISSING_VAL_ON_ANNOTATION_PARAMETER.on(parameter))
} } else if (parameter.isMutable) {
else if (parameter.isMutable) {
trace.report(VAR_ANNOTATION_PARAMETER.on(parameter)) trace.report(VAR_ANNOTATION_PARAMETER.on(parameter))
} }
} }
@@ -537,8 +547,7 @@ class DeclarationsChecker(
if (member != null && member.hasModifier(KtTokens.OPEN_KEYWORD)) { if (member != null && member.hasModifier(KtTokens.OPEN_KEYWORD)) {
if (classDescriptor.kind == ClassKind.OBJECT) { if (classDescriptor.kind == ClassKind.OBJECT) {
trace.report(NON_FINAL_MEMBER_IN_OBJECT.on(member)) trace.report(NON_FINAL_MEMBER_IN_OBJECT.on(member))
} } else {
else {
trace.report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member)) trace.report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member))
} }
} }
@@ -594,9 +603,9 @@ class DeclarationsChecker(
} }
private fun checkMemberProperty( private fun checkMemberProperty(
property: KtProperty, property: KtProperty,
propertyDescriptor: PropertyDescriptor, propertyDescriptor: PropertyDescriptor,
classDescriptor: ClassDescriptor classDescriptor: ClassDescriptor
) { ) {
val modifierList = property.modifierList val modifierList = property.modifierList
@@ -607,8 +616,7 @@ class DeclarationsChecker(
trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, property.name ?: "", classDescriptor)) trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, property.name ?: "", classDescriptor))
return return
} }
} } else if (classDescriptor.kind == ClassKind.INTERFACE &&
else if (classDescriptor.kind == ClassKind.INTERFACE &&
modifierList.hasModifier(KtTokens.OPEN_KEYWORD) && modifierList.hasModifier(KtTokens.OPEN_KEYWORD) &&
propertyDescriptor.modality == Modality.ABSTRACT) { propertyDescriptor.modality == Modality.ABSTRACT) {
trace.report(REDUNDANT_OPEN_IN_INTERFACE.on(property)) trace.report(REDUNDANT_OPEN_IN_INTERFACE.on(property))
@@ -659,35 +667,33 @@ class DeclarationsChecker(
!backingFieldRequired -> trace.report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer)) !backingFieldRequired -> trace.report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer))
property.receiverTypeReference != null -> trace.report(EXTENSION_PROPERTY_WITH_BACKING_FIELD.on(initializer)) property.receiverTypeReference != null -> trace.report(EXTENSION_PROPERTY_WITH_BACKING_FIELD.on(initializer))
} }
} } else if (delegate != null) {
else if (delegate != null) {
if (inInterface) { if (inInterface) {
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate)) trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
} } else if (isExpect) {
else if (isExpect) {
trace.report(EXPECTED_DELEGATED_PROPERTY.on(delegate)) trace.report(EXPECTED_DELEGATED_PROPERTY.on(delegate))
} }
} } else {
else {
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
val isExternal = propertyDescriptor.isEffectivelyExternal() val isExternal = propertyDescriptor.isEffectivelyExternal()
if (backingFieldRequired && !inInterface && !propertyDescriptor.isLateInit && !isExpect && isUninitialized && !isExternal) { if (backingFieldRequired && !inInterface && !propertyDescriptor.isLateInit && !isExpect && isUninitialized && !isExternal) {
if (propertyDescriptor.extensionReceiverParameter != null && !hasAccessorImplementation) { if (propertyDescriptor.extensionReceiverParameter != null && !hasAccessorImplementation) {
trace.report(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT.on(property)) trace.report(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT.on(property))
} } else if (diagnosticSuppressor.shouldReportNoBody(propertyDescriptor)) {
else if (diagnosticSuppressor.shouldReportNoBody(propertyDescriptor)) {
if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) { if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) {
trace.report(MUST_BE_INITIALIZED.on(property)) trace.report(MUST_BE_INITIALIZED.on(property))
} } else {
else {
trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property)) trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property))
} }
} }
} } else if (property.typeReference == null && !languageVersionSettings.supportsFeature(LanguageFeature.ShortSyntaxForPropertyGetters)) {
else if (property.typeReference == null && !languageVersionSettings.supportsFeature(LanguageFeature.ShortSyntaxForPropertyGetters)) { trace.report(
trace.report(Errors.UNSUPPORTED_FEATURE.on(property, LanguageFeature.ShortSyntaxForPropertyGetters to languageVersionSettings)) Errors.UNSUPPORTED_FEATURE.on(
} property,
else if (noExplicitTypeOrGetterType(property)) { LanguageFeature.ShortSyntaxForPropertyGetters to languageVersionSettings
)
)
} else if (noExplicitTypeOrGetterType(property)) {
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property)) trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
} }
@@ -703,7 +709,7 @@ class DeclarationsChecker(
} }
private fun noExplicitTypeOrGetterType(property: KtProperty) = private fun noExplicitTypeOrGetterType(property: KtProperty) =
property.typeReference == null property.typeReference == null
&& (property.getter == null || (property.getter!!.hasBlockBody() && property.getter!!.returnTypeReference == null)) && (property.getter == null || (property.getter!!.hasBlockBody() && property.getter!!.returnTypeReference == null))
fun checkFunction(function: KtNamedFunction, functionDescriptor: SimpleFunctionDescriptor) { fun checkFunction(function: KtNamedFunction, functionDescriptor: SimpleFunctionDescriptor) {
@@ -744,8 +750,7 @@ class DeclarationsChecker(
diagnosticSuppressor.shouldReportNoBody(functionDescriptor)) { diagnosticSuppressor.shouldReportNoBody(functionDescriptor)) {
trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor)) trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor))
} }
} } else /* top-level only */ {
else /* top-level only */ {
if (!function.hasBody() && !hasAbstractModifier && !hasExternalModifier && !functionDescriptor.isExpect && if (!function.hasBody() && !hasAbstractModifier && !hasExternalModifier && !functionDescriptor.isExpect &&
diagnosticSuppressor.shouldReportNoBody(functionDescriptor)) { diagnosticSuppressor.shouldReportNoBody(functionDescriptor)) {
trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor)) trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor))
@@ -779,16 +784,15 @@ class DeclarationsChecker(
if (declaration.typeReference == null) { if (declaration.typeReference == null) {
if (it.isNothing() && !declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { if (it.isNothing() && !declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
trace.report( trace.report(
(if (declaration is KtProperty) IMPLICIT_NOTHING_PROPERTY_TYPE else IMPLICIT_NOTHING_RETURN_TYPE).on(target) (if (declaration is KtProperty) IMPLICIT_NOTHING_PROPERTY_TYPE else IMPLICIT_NOTHING_RETURN_TYPE).on(target)
) )
} }
if (it.contains { type -> type.constructor is IntersectionTypeConstructor }) { if (it.contains { type -> type.constructor is IntersectionTypeConstructor }) {
trace.report(IMPLICIT_INTERSECTION_TYPE.on(target, it)) trace.report(IMPLICIT_INTERSECTION_TYPE.on(target, it))
} }
} } else if (it.isNothing() && it is AbbreviatedType) {
else if (it.isNothing() && it is AbbreviatedType) {
trace.report( trace.report(
(if (declaration is KtProperty) ABBREVIATED_NOTHING_PROPERTY_TYPE else ABBREVIATED_NOTHING_RETURN_TYPE).on(target) (if (declaration is KtProperty) ABBREVIATED_NOTHING_PROPERTY_TYPE else ABBREVIATED_NOTHING_RETURN_TYPE).on(target)
) )
} }
} }
@@ -800,8 +804,7 @@ class DeclarationsChecker(
if (accessor != null) { if (accessor != null) {
modifiersChecker.checkModifiersForDeclaration(accessor, accessorDescriptor) modifiersChecker.checkModifiersForDeclaration(accessor, accessorDescriptor)
identifierChecker.checkDeclaration(accessor, trace) identifierChecker.checkDeclaration(accessor, trace)
} } else {
else {
modifiersChecker.runDeclarationCheckers(property, accessorDescriptor) modifiersChecker.runDeclarationCheckers(property, accessorDescriptor)
} }
} }
@@ -816,9 +819,9 @@ class DeclarationsChecker(
} }
private fun checkAccessor( private fun checkAccessor(
propertyDescriptor: PropertyDescriptor, propertyDescriptor: PropertyDescriptor,
accessor: KtPropertyAccessor?, accessor: KtPropertyAccessor?,
accessorDescriptor: PropertyAccessorDescriptor? accessorDescriptor: PropertyAccessorDescriptor?
) { ) {
if (accessor == null || accessorDescriptor == null) return if (accessor == null || accessorDescriptor == null) return
if (propertyDescriptor.isExpect && accessor.hasBody()) { if (propertyDescriptor.isExpect && accessor.hasBody()) {
@@ -827,29 +830,25 @@ class DeclarationsChecker(
val accessorModifierList = accessor.modifierList ?: return val accessorModifierList = accessor.modifierList ?: return
val tokens = modifiersChecker.getTokensCorrespondingToModifiers( val tokens = modifiersChecker.getTokensCorrespondingToModifiers(
accessorModifierList, accessorModifierList,
setOf(KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PRIVATE_KEYWORD, KtTokens.INTERNAL_KEYWORD) setOf(KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PRIVATE_KEYWORD, KtTokens.INTERNAL_KEYWORD)
) )
if (accessor.isGetter) { if (accessor.isGetter) {
if (accessorDescriptor.visibility != propertyDescriptor.visibility) { if (accessorDescriptor.visibility != propertyDescriptor.visibility) {
reportVisibilityModifierDiagnostics(tokens.values, Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY) reportVisibilityModifierDiagnostics(tokens.values, Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY)
} } else {
else {
reportVisibilityModifierDiagnostics(tokens.values, Errors.REDUNDANT_MODIFIER_IN_GETTER) reportVisibilityModifierDiagnostics(tokens.values, Errors.REDUNDANT_MODIFIER_IN_GETTER)
} }
} } else {
else {
if (propertyDescriptor.isOverridable if (propertyDescriptor.isOverridable
&& accessorDescriptor.visibility == Visibilities.PRIVATE && accessorDescriptor.visibility == Visibilities.PRIVATE
&& propertyDescriptor.visibility != Visibilities.PRIVATE) { && propertyDescriptor.visibility != Visibilities.PRIVATE) {
if (propertyDescriptor.modality == Modality.ABSTRACT) { if (propertyDescriptor.modality == Modality.ABSTRACT) {
reportVisibilityModifierDiagnostics(tokens.values, Errors.PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY) reportVisibilityModifierDiagnostics(tokens.values, Errors.PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY)
} } else {
else {
reportVisibilityModifierDiagnostics(tokens.values, Errors.PRIVATE_SETTER_FOR_OPEN_PROPERTY) reportVisibilityModifierDiagnostics(tokens.values, Errors.PRIVATE_SETTER_FOR_OPEN_PROPERTY)
} }
} } else {
else {
val compare = Visibilities.compare(accessorDescriptor.visibility, propertyDescriptor.visibility) val compare = Visibilities.compare(accessorDescriptor.visibility, propertyDescriptor.visibility)
if (compare == null || compare > 0) { if (compare == null || compare > 0) {
reportVisibilityModifierDiagnostics(tokens.values, Errors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY) reportVisibilityModifierDiagnostics(tokens.values, Errors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY)
@@ -866,8 +865,7 @@ class DeclarationsChecker(
trace.report(EXPECTED_ENUM_ENTRY_WITH_BODY.on(enumEntry)) trace.report(EXPECTED_ENUM_ENTRY_WITH_BODY.on(enumEntry))
} }
} }
} } else {
else {
assert(DescriptorUtils.isInterface(enumClass)) { "Enum entry should be declared in enum class: " + enumEntryClass } assert(DescriptorUtils.isInterface(enumClass)) { "Enum entry should be declared in enum class: " + enumEntryClass }
} }
} }
@@ -922,7 +920,10 @@ class DeclarationsChecker(
return isImplementingMethodOfAnyInternal(member, HashSet<ClassDescriptor>()) return isImplementingMethodOfAnyInternal(member, HashSet<ClassDescriptor>())
} }
private fun isImplementingMethodOfAnyInternal(member: CallableMemberDescriptor, visitedClasses: MutableSet<ClassDescriptor>): Boolean { private fun isImplementingMethodOfAnyInternal(
member: CallableMemberDescriptor,
visitedClasses: MutableSet<ClassDescriptor>
): Boolean {
for (overridden in member.overriddenDescriptors) { for (overridden in member.overriddenDescriptors) {
val containingDeclaration = overridden.containingDeclaration val containingDeclaration = overridden.containingDeclaration
if (containingDeclaration !is ClassDescriptor) continue if (containingDeclaration !is ClassDescriptor) continue
@@ -33,14 +33,14 @@ import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.utils.keysToMapExceptNulls import org.jetbrains.kotlin.utils.keysToMapExceptNulls
class DelegationResolver<T : CallableMemberDescriptor> private constructor( class DelegationResolver<T : CallableMemberDescriptor> private constructor(
private val classOrObject: KtPureClassOrObject, private val classOrObject: KtPureClassOrObject,
private val ownerDescriptor: ClassDescriptor, private val ownerDescriptor: ClassDescriptor,
private val existingMembers: Collection<CallableDescriptor>, private val existingMembers: Collection<CallableDescriptor>,
private val trace: BindingTrace, private val trace: BindingTrace,
private val memberExtractor: MemberExtractor<T>, private val memberExtractor: MemberExtractor<T>,
private val typeResolver: TypeResolver, private val typeResolver: TypeResolver,
private val delegationFilter: DelegationFilter, private val delegationFilter: DelegationFilter,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
private fun generateDelegatedMembers(): Collection<T> { private fun generateDelegatedMembers(): Collection<T> {
@@ -61,24 +61,24 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
} }
private fun generateDelegatesForInterface(existingDelegates: Collection<T>, delegatedInterfaceType: KotlinType): Collection<T> = private fun generateDelegatesForInterface(existingDelegates: Collection<T>, delegatedInterfaceType: KotlinType): Collection<T> =
generateDelegationCandidates(delegatedInterfaceType).filter { candidate -> generateDelegationCandidates(delegatedInterfaceType).filter { candidate ->
!isOverridingAnyOf(candidate, existingMembers) && !isOverridingAnyOf(candidate, existingMembers) &&
!checkClashWithOtherDelegatedMember(candidate, existingDelegates) !checkClashWithOtherDelegatedMember(candidate, existingDelegates)
} }
private fun generateDelegationCandidates(delegatedInterfaceType: KotlinType): Collection<T> = private fun generateDelegationCandidates(delegatedInterfaceType: KotlinType): Collection<T> =
getDelegatableMembers(delegatedInterfaceType).map { memberDescriptor -> getDelegatableMembers(delegatedInterfaceType).map { memberDescriptor ->
val newModality = if (memberDescriptor.modality == Modality.ABSTRACT) Modality.OPEN else memberDescriptor.modality val newModality = if (memberDescriptor.modality == Modality.ABSTRACT) Modality.OPEN else memberDescriptor.modality
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
memberDescriptor.newCopyBuilder() memberDescriptor.newCopyBuilder()
.setOwner(ownerDescriptor) .setOwner(ownerDescriptor)
.setDispatchReceiverParameter(ownerDescriptor.thisAsReceiverParameter) .setDispatchReceiverParameter(ownerDescriptor.thisAsReceiverParameter)
.setModality(newModality) .setModality(newModality)
.setVisibility(Visibilities.INHERITED) .setVisibility(Visibilities.INHERITED)
.setKind(DELEGATION) .setKind(DELEGATION)
.setCopyOverrides(false) .setCopyOverrides(false)
.build() as T .build() as T
} }
private fun checkClashWithOtherDelegatedMember(candidate: T, delegatedMembers: Collection<T>): Boolean { private fun checkClashWithOtherDelegatedMember(candidate: T, delegatedMembers: Collection<T>): Boolean {
val alreadyDelegated = delegatedMembers.firstOrNull { isOverridableBy(it, candidate) } val alreadyDelegated = delegatedMembers.firstOrNull { isOverridableBy(it, candidate) }
@@ -92,16 +92,16 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
private fun getDelegatableMembers(interfaceType: KotlinType): Collection<T> = private fun getDelegatableMembers(interfaceType: KotlinType): Collection<T> =
memberExtractor.getMembersByType(interfaceType).filter { descriptor -> memberExtractor.getMembersByType(interfaceType).filter { descriptor ->
descriptor.isOverridable && descriptor.isOverridable &&
(descriptor.kind.isReal || !descriptor.overridesClassMembersOnly()) && (descriptor.kind.isReal || !descriptor.overridesClassMembersOnly()) &&
delegationFilter.filter(descriptor, languageVersionSettings) delegationFilter.filter(descriptor, languageVersionSettings)
} }
private fun T.overridesClassMembersOnly() = private fun T.overridesClassMembersOnly() =
DescriptorUtils.getAllOverriddenDeclarations(this).all { DescriptorUtils.getAllOverriddenDeclarations(this).all {
DescriptorUtils.isClass(it.containingDeclaration) DescriptorUtils.isClass(it.containingDeclaration)
} }
interface MemberExtractor<out T : CallableMemberDescriptor> { interface MemberExtractor<out T : CallableMemberDescriptor> {
fun getMembersByType(type: KotlinType): Collection<T> fun getMembersByType(type: KotlinType): Collection<T>
@@ -113,26 +113,35 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
companion object { companion object {
fun <T : CallableMemberDescriptor> generateDelegatedMembers( fun <T : CallableMemberDescriptor> generateDelegatedMembers(
classOrObject: KtPureClassOrObject, classOrObject: KtPureClassOrObject,
ownerDescriptor: ClassDescriptor, ownerDescriptor: ClassDescriptor,
existingMembers: Collection<CallableDescriptor>, existingMembers: Collection<CallableDescriptor>,
trace: BindingTrace, trace: BindingTrace,
memberExtractor: MemberExtractor<T>, memberExtractor: MemberExtractor<T>,
typeResolver: TypeResolver, typeResolver: TypeResolver,
delegationFilter: DelegationFilter, delegationFilter: DelegationFilter,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): Collection<T> = ): Collection<T> =
DelegationResolver(classOrObject, ownerDescriptor, existingMembers, trace, memberExtractor, typeResolver, delegationFilter, languageVersionSettings) DelegationResolver(
.generateDelegatedMembers() classOrObject,
ownerDescriptor,
existingMembers,
trace,
memberExtractor,
typeResolver,
delegationFilter,
languageVersionSettings
)
.generateDelegatedMembers()
private fun isOverridingAnyOf( private fun isOverridingAnyOf(
candidate: CallableMemberDescriptor, candidate: CallableMemberDescriptor,
possiblyOverriddenBy: Collection<CallableDescriptor> possiblyOverriddenBy: Collection<CallableDescriptor>
): Boolean = ): Boolean =
possiblyOverriddenBy.any { isOverridableBy(it, candidate) } possiblyOverriddenBy.any { isOverridableBy(it, candidate) }
private fun isOverridableBy(memberOne: CallableDescriptor, memberTwo: CallableDescriptor): Boolean = private fun isOverridableBy(memberOne: CallableDescriptor, memberTwo: CallableDescriptor): Boolean =
OverridingUtil.DEFAULT.isOverridableBy(memberOne, memberTwo, null).result == OVERRIDABLE OverridingUtil.DEFAULT.isOverridableBy(memberOne, memberTwo, null).result == OVERRIDABLE
// class Foo : Bar by baz // class Foo : Bar by baz
// descriptor = Foo // descriptor = Foo
@@ -140,34 +149,34 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
// delegateExpressionType = typeof(baz) // delegateExpressionType = typeof(baz)
// return Map<member of Foo, corresponding member of typeOf(baz)> // return Map<member of Foo, corresponding member of typeOf(baz)>
fun getDelegates( fun getDelegates(
descriptor: ClassDescriptor, descriptor: ClassDescriptor,
toInterface: ClassDescriptor, toInterface: ClassDescriptor,
delegateExpressionType: KotlinType? = null delegateExpressionType: KotlinType? = null
): Map<CallableMemberDescriptor, CallableMemberDescriptor> { ): Map<CallableMemberDescriptor, CallableMemberDescriptor> {
if (delegateExpressionType?.isDynamic() ?: false) return emptyMap() if (delegateExpressionType?.isDynamic() ?: false) return emptyMap()
val delegatedMembers = descriptor.defaultType.memberScope.getContributedDescriptors().asSequence() val delegatedMembers = descriptor.defaultType.memberScope.getContributedDescriptors().asSequence()
.filterIsInstance<CallableMemberDescriptor>() .filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.DELEGATION } .filter { it.kind == CallableMemberDescriptor.Kind.DELEGATION }
.asIterable() .asIterable()
.sortedWith(MemberComparator.INSTANCE) .sortedWith(MemberComparator.INSTANCE)
return delegatedMembers return delegatedMembers
.keysToMapExceptNulls { delegatingMember -> .keysToMapExceptNulls { delegatingMember ->
val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember) val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember)
.filter { it.containingDeclaration == toInterface } .filter { it.containingDeclaration == toInterface }
.map { overriddenDescriptor -> .map { overriddenDescriptor ->
val scope = (delegateExpressionType ?: toInterface.defaultType).memberScope val scope = (delegateExpressionType ?: toInterface.defaultType).memberScope
val name = overriddenDescriptor.name val name = overriddenDescriptor.name
// this is the actual member of delegateExpressionType that we are delegating to // this is the actual member of delegateExpressionType that we are delegating to
(scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) + (scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) +
scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES)) scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES))
.firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) } .firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) }
} }
actualDelegates.firstOrNull() actualDelegates.firstOrNull()
} }
} }
} }
} }
@@ -47,14 +47,16 @@ object DescriptorToSourceUtils {
result.add(descriptor) result.add(descriptor)
} }
@JvmStatic fun getEffectiveReferencedDescriptors(descriptor: DeclarationDescriptor): Collection<DeclarationDescriptor> { @JvmStatic
fun getEffectiveReferencedDescriptors(descriptor: DeclarationDescriptor): Collection<DeclarationDescriptor> {
val result = ArrayList<DeclarationDescriptor>() val result = ArrayList<DeclarationDescriptor>()
collectEffectiveReferencedDescriptors(result, descriptor.original) collectEffectiveReferencedDescriptors(result, descriptor.original)
return result return result
} }
// TODO Fix in descriptor // TODO Fix in descriptor
@JvmStatic private fun getSourceForExtensionReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor): PsiElement? { @JvmStatic
private fun getSourceForExtensionReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor): PsiElement? {
// Only for extension receivers // Only for extension receivers
if (descriptor.source != SourceElement.NO_SOURCE || descriptor.value !is ExtensionReceiver) return null if (descriptor.source != SourceElement.NO_SOURCE || descriptor.value !is ExtensionReceiver) return null
val containingDeclaration = descriptor.containingDeclaration as? CallableDescriptor ?: return null val containingDeclaration = descriptor.containingDeclaration as? CallableDescriptor ?: return null
@@ -62,7 +64,8 @@ object DescriptorToSourceUtils {
return psi.receiverTypeReference return psi.receiverTypeReference
} }
@JvmStatic fun getSourceFromDescriptor(descriptor: DeclarationDescriptor): PsiElement? { @JvmStatic
fun getSourceFromDescriptor(descriptor: DeclarationDescriptor): PsiElement? {
if (descriptor is ReceiverParameterDescriptor) { if (descriptor is ReceiverParameterDescriptor) {
getSourceForExtensionReceiverParameterDescriptor(descriptor)?.let { return it } getSourceForExtensionReceiverParameterDescriptor(descriptor)?.let { return it }
} }
@@ -70,7 +73,8 @@ object DescriptorToSourceUtils {
return (descriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() return (descriptor as? DeclarationDescriptorWithSource)?.source?.getPsi()
} }
@JvmStatic fun getSourceFromAnnotation(descriptor: AnnotationDescriptor): KtAnnotationEntry? { @JvmStatic
fun getSourceFromAnnotation(descriptor: AnnotationDescriptor): KtAnnotationEntry? {
return descriptor.source.getPsi() as? KtAnnotationEntry return descriptor.source.getPsi() as? KtAnnotationEntry
} }
@@ -78,12 +82,14 @@ object DescriptorToSourceUtils {
// Returns PSI element for descriptor. If there are many relevant elements (e.g. it is fake override // Returns PSI element for descriptor. If there are many relevant elements (e.g. it is fake override
// with multiple declarations), returns null. It can't find declarations in builtins or decompiled code. // with multiple declarations), returns null. It can't find declarations in builtins or decompiled code.
// In IDE, use DescriptorToSourceUtilsIde instead. // In IDE, use DescriptorToSourceUtilsIde instead.
@JvmStatic fun descriptorToDeclaration(descriptor: DeclarationDescriptor): PsiElement? { @JvmStatic
fun descriptorToDeclaration(descriptor: DeclarationDescriptor): PsiElement? {
val effectiveReferencedDescriptors = getEffectiveReferencedDescriptors(descriptor) val effectiveReferencedDescriptors = getEffectiveReferencedDescriptors(descriptor)
return if (effectiveReferencedDescriptors.size == 1) getSourceFromDescriptor(effectiveReferencedDescriptors.firstOrNull()!!) else null return if (effectiveReferencedDescriptors.size == 1) getSourceFromDescriptor(effectiveReferencedDescriptors.firstOrNull()!!) else null
} }
@JvmStatic fun getContainingFile(declarationDescriptor: DeclarationDescriptor): KtFile? { @JvmStatic
fun getContainingFile(declarationDescriptor: DeclarationDescriptor): KtFile? {
// declarationDescriptor may describe a synthesized element which doesn't have PSI // declarationDescriptor may describe a synthesized element which doesn't have PSI
// To workaround that, we find a top-level parent (which is inside a PackageFragmentDescriptor), which is guaranteed to have PSI // To workaround that, we find a top-level parent (which is inside a PackageFragmentDescriptor), which is guaranteed to have PSI
val descriptor = findTopLevelParent(declarationDescriptor) ?: return null val descriptor = findTopLevelParent(declarationDescriptor) ?: return null
@@ -38,16 +38,17 @@ class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
return result and checkFunction(constructor, constructorDescriptor) return result and checkFunction(constructor, constructorDescriptor)
} }
fun checkDeclarationWithVisibility(modifierListOwner: KtModifierListOwner, fun checkDeclarationWithVisibility(
descriptor: DeclarationDescriptorWithVisibility, modifierListOwner: KtModifierListOwner,
visibility: Visibility descriptor: DeclarationDescriptorWithVisibility,
) : Boolean { visibility: Visibility
): Boolean {
return when { return when {
modifierListOwner is KtFunction && modifierListOwner is KtFunction &&
descriptor is FunctionDescriptor -> checkFunction(modifierListOwner, descriptor, visibility) descriptor is FunctionDescriptor -> checkFunction(modifierListOwner, descriptor, visibility)
modifierListOwner is KtProperty && modifierListOwner is KtProperty &&
descriptor is PropertyDescriptor -> checkProperty(modifierListOwner, descriptor, visibility) descriptor is PropertyDescriptor -> checkProperty(modifierListOwner, descriptor, visibility)
else -> true else -> true
} }
@@ -60,48 +61,66 @@ class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
val typeAliasVisibility = typeAliasDescriptor.effectiveVisibility() val typeAliasVisibility = typeAliasDescriptor.effectiveVisibility()
val restricting = expandedType.leastPermissiveDescriptor(typeAliasVisibility) val restricting = expandedType.leastPermissiveDescriptor(typeAliasVisibility)
if (restricting != null) { if (restricting != null) {
trace.report(Errors.EXPOSED_TYPEALIAS_EXPANDED_TYPE.on(typeAlias.nameIdentifier ?: typeAlias, trace.report(
typeAliasVisibility, restricting, restricting.effectiveVisibility())) Errors.EXPOSED_TYPEALIAS_EXPANDED_TYPE.on(
typeAlias.nameIdentifier ?: typeAlias,
typeAliasVisibility, restricting, restricting.effectiveVisibility()
)
)
} }
} }
fun checkFunction(function: KtFunction, fun checkFunction(
functionDescriptor: FunctionDescriptor, function: KtFunction,
// for checking situation with modified basic visibility functionDescriptor: FunctionDescriptor,
visibility: Visibility = functionDescriptor.visibility // for checking situation with modified basic visibility
visibility: Visibility = functionDescriptor.visibility
): Boolean { ): Boolean {
val functionVisibility = functionDescriptor.effectiveVisibility(visibility) val functionVisibility = functionDescriptor.effectiveVisibility(visibility)
var result = true var result = true
if (function !is KtConstructor<*>) { if (function !is KtConstructor<*>) {
val restricting = functionDescriptor.returnType?.leastPermissiveDescriptor(functionVisibility) val restricting = functionDescriptor.returnType?.leastPermissiveDescriptor(functionVisibility)
if (restricting != null) { if (restricting != null) {
trace.report(Errors.EXPOSED_FUNCTION_RETURN_TYPE.on(function.nameIdentifier ?: function, functionVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_FUNCTION_RETURN_TYPE.on(
function.nameIdentifier ?: function, functionVisibility,
restricting, restricting.effectiveVisibility()
)
)
result = false result = false
} }
} }
functionDescriptor.valueParameters.forEachIndexed { i, parameterDescriptor -> functionDescriptor.valueParameters.forEachIndexed { i, parameterDescriptor ->
val restricting = parameterDescriptor.type.leastPermissiveDescriptor(functionVisibility) val restricting = parameterDescriptor.type.leastPermissiveDescriptor(functionVisibility)
if (restricting != null && i < function.valueParameters.size) { if (restricting != null && i < function.valueParameters.size) {
trace.report(Errors.EXPOSED_PARAMETER_TYPE.on(function.valueParameters[i], functionVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_PARAMETER_TYPE.on(
function.valueParameters[i], functionVisibility,
restricting, restricting.effectiveVisibility()
)
)
result = false result = false
} }
} }
return result and checkMemberReceiver(function.receiverTypeReference, functionDescriptor) return result and checkMemberReceiver(function.receiverTypeReference, functionDescriptor)
} }
fun checkProperty(property: KtProperty, fun checkProperty(
propertyDescriptor: PropertyDescriptor, property: KtProperty,
// for checking situation with modified basic visibility propertyDescriptor: PropertyDescriptor,
visibility: Visibility = propertyDescriptor.visibility // for checking situation with modified basic visibility
visibility: Visibility = propertyDescriptor.visibility
): Boolean { ): Boolean {
val propertyVisibility = propertyDescriptor.effectiveVisibility(visibility) val propertyVisibility = propertyDescriptor.effectiveVisibility(visibility)
val restricting = propertyDescriptor.type.leastPermissiveDescriptor(propertyVisibility) val restricting = propertyDescriptor.type.leastPermissiveDescriptor(propertyVisibility)
var result = true var result = true
if (restricting != null) { if (restricting != null) {
trace.report(Errors.EXPOSED_PROPERTY_TYPE.on(property.nameIdentifier ?: property, propertyVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_PROPERTY_TYPE.on(
property.nameIdentifier ?: property, propertyVisibility,
restricting, restricting.effectiveVisibility()
)
)
result = false result = false
} }
return result and checkMemberReceiver(property.receiverTypeReference, propertyDescriptor) return result and checkMemberReceiver(property.receiverTypeReference, propertyDescriptor)
@@ -113,8 +132,12 @@ class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
val memberVisibility = memberDescriptor.effectiveVisibility() val memberVisibility = memberDescriptor.effectiveVisibility()
val restricting = receiverParameterDescriptor.type.leastPermissiveDescriptor(memberVisibility) val restricting = receiverParameterDescriptor.type.leastPermissiveDescriptor(memberVisibility)
if (restricting != null) { if (restricting != null) {
trace.report(Errors.EXPOSED_RECEIVER_TYPE.on(typeReference, memberVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_RECEIVER_TYPE.on(
typeReference, memberVisibility,
restricting, restricting.effectiveVisibility()
)
)
return false return false
} }
return true return true
@@ -135,12 +158,19 @@ class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
val restricting = superType.leastPermissiveDescriptor(classVisibility) val restricting = superType.leastPermissiveDescriptor(classVisibility)
if (restricting != null) { if (restricting != null) {
if (isInterface) { if (isInterface) {
trace.report(Errors.EXPOSED_SUPER_INTERFACE.on(delegationList[i], classVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_SUPER_INTERFACE.on(
} delegationList[i], classVisibility,
else { restricting, restricting.effectiveVisibility()
trace.report(Errors.EXPOSED_SUPER_CLASS.on(delegationList[i], classVisibility, )
restricting, restricting.effectiveVisibility())) )
} else {
trace.report(
Errors.EXPOSED_SUPER_CLASS.on(
delegationList[i], classVisibility,
restricting, restricting.effectiveVisibility()
)
)
} }
result = false result = false
} }
@@ -157,8 +187,12 @@ class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
for (upperBound in typeParameterDescriptor.upperBounds) { for (upperBound in typeParameterDescriptor.upperBounds) {
val restricting = upperBound.leastPermissiveDescriptor(classVisibility) val restricting = upperBound.leastPermissiveDescriptor(classVisibility)
if (restricting != null) { if (restricting != null) {
trace.report(Errors.EXPOSED_TYPE_PARAMETER_BOUND.on(typeParameterList[i], classVisibility, trace.report(
restricting, restricting.effectiveVisibility())) Errors.EXPOSED_TYPE_PARAMETER_BOUND.on(
typeParameterList[i], classVisibility,
restricting, restricting.effectiveVisibility()
)
)
result = false result = false
break break
} }
@@ -34,27 +34,27 @@ import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import java.util.* import java.util.*
class LazyTopDownAnalyzer( class LazyTopDownAnalyzer(
private val trace: BindingTrace, private val trace: BindingTrace,
private val declarationResolver: DeclarationResolver, private val declarationResolver: DeclarationResolver,
private val overrideResolver: OverrideResolver, private val overrideResolver: OverrideResolver,
private val overloadResolver: OverloadResolver, private val overloadResolver: OverloadResolver,
private val varianceChecker: VarianceChecker, private val varianceChecker: VarianceChecker,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val lazyDeclarationResolver: LazyDeclarationResolver, private val lazyDeclarationResolver: LazyDeclarationResolver,
private val bodyResolver: BodyResolver, private val bodyResolver: BodyResolver,
private val topLevelDescriptorProvider: TopLevelDescriptorProvider, private val topLevelDescriptorProvider: TopLevelDescriptorProvider,
private val fileScopeProvider: FileScopeProvider, private val fileScopeProvider: FileScopeProvider,
private val declarationScopeProvider: DeclarationScopeProvider, private val declarationScopeProvider: DeclarationScopeProvider,
private val qualifiedExpressionResolver: QualifiedExpressionResolver, private val qualifiedExpressionResolver: QualifiedExpressionResolver,
private val identifierChecker: IdentifierChecker, private val identifierChecker: IdentifierChecker,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val classifierUsageCheckers: Iterable<ClassifierUsageChecker> private val classifierUsageCheckers: Iterable<ClassifierUsageChecker>
) { ) {
fun analyzeDeclarations( fun analyzeDeclarations(
topDownAnalysisMode: TopDownAnalysisMode, topDownAnalysisMode: TopDownAnalysisMode,
declarations: Collection<PsiElement>, declarations: Collection<PsiElement>,
outerDataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY outerDataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY
): TopDownAnalysisContext { ): TopDownAnalysisContext {
val c = TopDownAnalysisContext(topDownAnalysisMode, outerDataFlowInfo, declarationScopeProvider) val c = TopDownAnalysisContext(topDownAnalysisMode, outerDataFlowInfo, declarationScopeProvider)
@@ -82,8 +82,8 @@ class LazyTopDownAnalyzer(
override fun visitScript(script: KtScript) { override fun visitScript(script: KtScript) {
c.scripts.put( c.scripts.put(
script, script,
lazyDeclarationResolver.getScriptDescriptor(script, KotlinLookupLocation(script)) as LazyScriptDescriptor lazyDeclarationResolver.getScriptDescriptor(script, KotlinLookupLocation(script)) as LazyScriptDescriptor
) )
registerDeclarations(script.declarations) registerDeclarations(script.declarations)
} }
@@ -109,8 +109,10 @@ class LazyTopDownAnalyzer(
} }
override fun visitClassOrObject(classOrObject: KtClassOrObject) { override fun visitClassOrObject(classOrObject: KtClassOrObject) {
val location = if (classOrObject.isTopLevel()) KotlinLookupLocation(classOrObject) else NoLookupLocation.WHEN_RESOLVE_DECLARATION val location =
val descriptor = lazyDeclarationResolver.getClassDescriptor(classOrObject, location) as ClassDescriptorWithResolutionScopes if (classOrObject.isTopLevel()) KotlinLookupLocation(classOrObject) else NoLookupLocation.WHEN_RESOLVE_DECLARATION
val descriptor =
lazyDeclarationResolver.getClassDescriptor(classOrObject, location) as ClassDescriptorWithResolutionScopes
c.declaredClasses.put(classOrObject, descriptor) c.declaredClasses.put(classOrObject, descriptor)
registerDeclarations(classOrObject.declarations) registerDeclarations(classOrObject.declarations)
@@ -127,12 +129,10 @@ class LazyTopDownAnalyzer(
trace.report(MANY_COMPANION_OBJECTS.on(jetDeclaration)) trace.report(MANY_COMPANION_OBJECTS.on(jetDeclaration))
} }
companionObjectAlreadyFound = true companionObjectAlreadyFound = true
} } else if (jetDeclaration is KtSecondaryConstructor) {
else if (jetDeclaration is KtSecondaryConstructor) {
if (DescriptorUtils.isSingletonOrAnonymousObject(classDescriptor)) { if (DescriptorUtils.isSingletonOrAnonymousObject(classDescriptor)) {
trace.report(CONSTRUCTOR_IN_OBJECT.on(jetDeclaration)) trace.report(CONSTRUCTOR_IN_OBJECT.on(jetDeclaration))
} } else if (classDescriptor.kind == ClassKind.INTERFACE) {
else if (classDescriptor.kind == ClassKind.INTERFACE) {
trace.report(CONSTRUCTOR_IN_INTERFACE.on(jetDeclaration)) trace.report(CONSTRUCTOR_IN_INTERFACE.on(jetDeclaration))
} }
} }
@@ -147,13 +147,19 @@ class LazyTopDownAnalyzer(
private fun registerPrimaryConstructorParameters(klass: KtClass) { private fun registerPrimaryConstructorParameters(klass: KtClass) {
for (jetParameter in klass.primaryConstructorParameters) { for (jetParameter in klass.primaryConstructorParameters) {
if (jetParameter.hasValOrVar()) { if (jetParameter.hasValOrVar()) {
c.primaryConstructorParameterProperties.put(jetParameter, lazyDeclarationResolver.resolveToDescriptor(jetParameter) as PropertyDescriptor) c.primaryConstructorParameterProperties.put(
jetParameter,
lazyDeclarationResolver.resolveToDescriptor(jetParameter) as PropertyDescriptor
)
} }
} }
} }
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) { override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {
c.secondaryConstructors.put(constructor, lazyDeclarationResolver.resolveToDescriptor(constructor) as ClassConstructorDescriptor) c.secondaryConstructors.put(
constructor,
lazyDeclarationResolver.resolveToDescriptor(constructor) as ClassConstructorDescriptor
)
} }
override fun visitEnumEntry(enumEntry: KtEnumEntry) { override fun visitEnumEntry(enumEntry: KtEnumEntry) {
@@ -165,7 +171,8 @@ class LazyTopDownAnalyzer(
} }
override fun visitAnonymousInitializer(initializer: KtAnonymousInitializer) { override fun visitAnonymousInitializer(initializer: KtAnonymousInitializer) {
val containerDescriptor = lazyDeclarationResolver.resolveToDescriptor(initializer.containingDeclaration) as ClassDescriptorWithResolutionScopes val containerDescriptor =
lazyDeclarationResolver.resolveToDescriptor(initializer.containingDeclaration) as ClassDescriptorWithResolutionScopes
c.anonymousInitializers.put(initializer, containerDescriptor) c.anonymousInitializers.put(initializer, containerDescriptor)
} }
@@ -237,7 +244,11 @@ class LazyTopDownAnalyzer(
fileScopeProvider.getImportResolver(file).forceResolveAllImports() fileScopeProvider.getImportResolver(file).forceResolveAllImports()
} }
private fun createTypeAliasDescriptors(c: TopDownAnalysisContext, topLevelFqNames: Multimap<FqName, KtElement>, typeAliases: List<KtTypeAlias>) { private fun createTypeAliasDescriptors(
c: TopDownAnalysisContext,
topLevelFqNames: Multimap<FqName, KtElement>,
typeAliases: List<KtTypeAlias>
) {
for (typeAlias in typeAliases) { for (typeAlias in typeAliases) {
val descriptor = lazyDeclarationResolver.resolveToDescriptor(typeAlias) as TypeAliasDescriptor val descriptor = lazyDeclarationResolver.resolveToDescriptor(typeAlias) as TypeAliasDescriptor
@@ -247,7 +258,11 @@ class LazyTopDownAnalyzer(
} }
} }
private fun createPropertyDescriptors(c: TopDownAnalysisContext, topLevelFqNames: Multimap<FqName, KtElement>, properties: List<KtProperty>) { private fun createPropertyDescriptors(
c: TopDownAnalysisContext,
topLevelFqNames: Multimap<FqName, KtElement>,
properties: List<KtProperty>
) {
for (property in properties) { for (property in properties) {
val descriptor = lazyDeclarationResolver.resolveToDescriptor(property) as PropertyDescriptor val descriptor = lazyDeclarationResolver.resolveToDescriptor(property) as PropertyDescriptor
@@ -269,9 +284,10 @@ class LazyTopDownAnalyzer(
} }
private fun createPropertiesFromDestructuringDeclarations( private fun createPropertiesFromDestructuringDeclarations(
c: TopDownAnalysisContext, c: TopDownAnalysisContext,
topLevelFqNames: Multimap<FqName, KtElement>, topLevelFqNames: Multimap<FqName, KtElement>,
destructuringDeclarations: List<KtDestructuringDeclaration>) { destructuringDeclarations: List<KtDestructuringDeclaration>
) {
for (destructuringDeclaration in destructuringDeclarations) { for (destructuringDeclaration in destructuringDeclarations) {
for (entry in destructuringDeclaration.entries) { for (entry in destructuringDeclaration.entries) {
val descriptor = lazyDeclarationResolver.resolveToDescriptor(entry) as PropertyDescriptor val descriptor = lazyDeclarationResolver.resolveToDescriptor(entry) as PropertyDescriptor
@@ -283,7 +299,11 @@ class LazyTopDownAnalyzer(
} }
} }
private fun registerTopLevelFqName(topLevelFqNames: Multimap<FqName, KtElement>, declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor) { private fun registerTopLevelFqName(
topLevelFqNames: Multimap<FqName, KtElement>,
declaration: KtNamedDeclaration,
descriptor: DeclarationDescriptor
) {
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) { if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
val fqName = declaration.fqName val fqName = declaration.fqName
if (fqName != null) { if (fqName != null) {
@@ -52,93 +52,123 @@ object ModifierCheckerCore {
COMPATIBLE_FOR_CLASSES_ONLY COMPATIBLE_FOR_CLASSES_ONLY
} }
private val defaultVisibilityTargets = EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, private val defaultVisibilityTargets = EnumSet.of(
MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS,
MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, CONSTRUCTOR, TYPEALIAS) MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER,
MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, CONSTRUCTOR, TYPEALIAS
)
val possibleTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>( val possibleTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>(
ENUM_KEYWORD to EnumSet.of(ENUM_CLASS), ENUM_KEYWORD to EnumSet.of(ENUM_CLASS),
ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION), FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION),
SEALED_KEYWORD to EnumSet.of(CLASS_ONLY), SEALED_KEYWORD to EnumSet.of(CLASS_ONLY),
INNER_KEYWORD to EnumSet.of(CLASS_ONLY), INNER_KEYWORD to EnumSet.of(CLASS_ONLY),
OVERRIDE_KEYWORD to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION), OVERRIDE_KEYWORD to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION),
PRIVATE_KEYWORD to defaultVisibilityTargets, PRIVATE_KEYWORD to defaultVisibilityTargets,
PUBLIC_KEYWORD to defaultVisibilityTargets, PUBLIC_KEYWORD to defaultVisibilityTargets,
INTERNAL_KEYWORD to defaultVisibilityTargets, INTERNAL_KEYWORD to defaultVisibilityTargets,
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, PROTECTED_KEYWORD to EnumSet.of(
MEMBER_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, MEMBER_PROPERTY, CONSTRUCTOR, TYPEALIAS), CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS,
IN_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), MEMBER_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, MEMBER_PROPERTY, CONSTRUCTOR, TYPEALIAS
OUT_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), ),
REIFIED_KEYWORD to EnumSet.of(TYPE_PARAMETER), IN_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION),
VARARG_KEYWORD to EnumSet.of(VALUE_PARAMETER, PROPERTY_PARAMETER), OUT_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION),
COMPANION_KEYWORD to EnumSet.of(OBJECT), REIFIED_KEYWORD to EnumSet.of(TYPE_PARAMETER),
LATEINIT_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, LOCAL_VARIABLE), VARARG_KEYWORD to EnumSet.of(VALUE_PARAMETER, PROPERTY_PARAMETER),
DATA_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS), COMPANION_KEYWORD to EnumSet.of(OBJECT),
INLINE_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER), LATEINIT_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, LOCAL_VARIABLE),
NOINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER), DATA_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS),
TAILREC_KEYWORD to EnumSet.of(FUNCTION), INLINE_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER),
SUSPEND_KEYWORD to EnumSet.of(MEMBER_FUNCTION, TOP_LEVEL_FUNCTION), NOINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER),
EXTERNAL_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, CLASS), TAILREC_KEYWORD to EnumSet.of(FUNCTION),
ANNOTATION_KEYWORD to EnumSet.of(ANNOTATION_CLASS), SUSPEND_KEYWORD to EnumSet.of(MEMBER_FUNCTION, TOP_LEVEL_FUNCTION),
CROSSINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER), EXTERNAL_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, CLASS),
CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY), ANNOTATION_KEYWORD to EnumSet.of(ANNOTATION_CLASS),
OPERATOR_KEYWORD to EnumSet.of(FUNCTION), CROSSINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER),
INFIX_KEYWORD to EnumSet.of(FUNCTION), CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY),
HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), OPERATOR_KEYWORD to EnumSet.of(FUNCTION),
IMPL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS), INFIX_KEYWORD to EnumSet.of(FUNCTION),
EXPECT_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
ACTUAL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS) IMPL_KEYWORD to EnumSet.of(
TOP_LEVEL_FUNCTION,
MEMBER_FUNCTION,
TOP_LEVEL_PROPERTY,
MEMBER_PROPERTY,
CONSTRUCTOR,
CLASS_ONLY,
OBJECT,
INTERFACE,
ENUM_CLASS,
ANNOTATION_CLASS,
TYPEALIAS
),
EXPECT_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
ACTUAL_KEYWORD to EnumSet.of(
TOP_LEVEL_FUNCTION,
MEMBER_FUNCTION,
TOP_LEVEL_PROPERTY,
MEMBER_PROPERTY,
CONSTRUCTOR,
CLASS_ONLY,
OBJECT,
INTERFACE,
ENUM_CLASS,
ANNOTATION_CLASS,
TYPEALIAS
)
) )
private val featureDependencies = mapOf( private val featureDependencies = mapOf(
SUSPEND_KEYWORD to listOf(LanguageFeature.Coroutines), SUSPEND_KEYWORD to listOf(LanguageFeature.Coroutines),
INLINE_KEYWORD to listOf(LanguageFeature.InlineProperties), INLINE_KEYWORD to listOf(LanguageFeature.InlineProperties),
HEADER_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), HEADER_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
IMPL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), IMPL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables) LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables)
) )
private val featureDependenciesTargets = mapOf( private val featureDependenciesTargets = mapOf(
LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER), LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER),
LanguageFeature.LateinitLocalVariables to setOf(LOCAL_VARIABLE), LanguageFeature.LateinitLocalVariables to setOf(LOCAL_VARIABLE),
LanguageFeature.LateinitTopLevelProperties to setOf(TOP_LEVEL_PROPERTY) LanguageFeature.LateinitTopLevelProperties to setOf(TOP_LEVEL_PROPERTY)
) )
// NOTE: deprecated targets must be possible! // NOTE: deprecated targets must be possible!
private val deprecatedTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>() private val deprecatedTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>()
private val deprecatedModifierMap = mapOf( private val deprecatedModifierMap = mapOf(
HEADER_KEYWORD to EXPECT_KEYWORD, HEADER_KEYWORD to EXPECT_KEYWORD,
IMPL_KEYWORD to ACTUAL_KEYWORD IMPL_KEYWORD to ACTUAL_KEYWORD
) )
// NOTE: redundant targets must be possible! // NOTE: redundant targets must be possible!
private val redundantTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>( private val redundantTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>(
OPEN_KEYWORD to EnumSet.of(INTERFACE) OPEN_KEYWORD to EnumSet.of(INTERFACE)
) )
private val possibleParentTargetPredicateMap = mapOf<KtModifierKeywordToken, TargetAllowedPredicate>( private val possibleParentTargetPredicateMap = mapOf<KtModifierKeywordToken, TargetAllowedPredicate>(
INNER_KEYWORD to or(always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS), INNER_KEYWORD to or(
ifSupported(LanguageFeature.InnerClassInEnumEntryClass, ENUM_ENTRY)), always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS),
OVERRIDE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY), ifSupported(LanguageFeature.InnerClassInEnumEntryClass, ENUM_ENTRY)
PROTECTED_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT), ),
INTERNAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, FILE), OVERRIDE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY),
PRIVATE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY, FILE), PROTECTED_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT),
COMPANION_KEYWORD to always(CLASS_ONLY, ENUM_CLASS, INTERFACE), INTERNAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, FILE),
FINAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, ANNOTATION_CLASS, FILE), PRIVATE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY, FILE),
VARARG_KEYWORD to always(CONSTRUCTOR, FUNCTION, CLASS) COMPANION_KEYWORD to always(CLASS_ONLY, ENUM_CLASS, INTERFACE),
FINAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, ANNOTATION_CLASS, FILE),
VARARG_KEYWORD to always(CONSTRUCTOR, FUNCTION, CLASS)
) )
private val deprecatedParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>() private val deprecatedParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>()
fun isPossibleParentTarget( fun isPossibleParentTarget(
modifier: KtModifierKeywordToken, modifier: KtModifierKeywordToken,
parentTarget: KotlinTarget, parentTarget: KotlinTarget,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): Boolean { ): Boolean {
deprecatedParentTargetMap[modifier]?.let { deprecatedParentTargetMap[modifier]?.let {
if (parentTarget in it) return false if (parentTarget in it) return false
@@ -198,15 +228,17 @@ object ModifierCheckerCore {
} }
private fun redundantRegister( private fun redundantRegister(
sufficient: KtModifierKeywordToken, sufficient: KtModifierKeywordToken,
redundant: KtModifierKeywordToken redundant: KtModifierKeywordToken
): Map<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility> { ): Map<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility> {
return mapOf(Pair(sufficient, redundant) to Compatibility.REDUNDANT, return mapOf(
Pair(redundant, sufficient) to Compatibility.REVERSE_REDUNDANT) Pair(sufficient, redundant) to Compatibility.REDUNDANT,
Pair(redundant, sufficient) to Compatibility.REVERSE_REDUNDANT
)
} }
private fun compatibilityRegister( private fun compatibilityRegister(
compatibility: Compatibility, vararg list: KtModifierKeywordToken compatibility: Compatibility, vararg list: KtModifierKeywordToken
): Map<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility> { ): Map<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility> {
val result = hashMapOf<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility>() val result = hashMapOf<Pair<KtModifierKeywordToken, KtModifierKeywordToken>, Compatibility>()
for (first in list) { for (first in list) {
@@ -220,7 +252,7 @@ object ModifierCheckerCore {
} }
private fun compatibilityForClassesRegister(vararg list: KtModifierKeywordToken) = private fun compatibilityForClassesRegister(vararg list: KtModifierKeywordToken) =
compatibilityRegister(Compatibility.COMPATIBLE_FOR_CLASSES_ONLY, *list) compatibilityRegister(Compatibility.COMPATIBLE_FOR_CLASSES_ONLY, *list)
private fun incompatibilityRegister(vararg list: KtModifierKeywordToken) = compatibilityRegister(Compatibility.INCOMPATIBLE, *list) private fun incompatibilityRegister(vararg list: KtModifierKeywordToken) = compatibilityRegister(Compatibility.INCOMPATIBLE, *list)
@@ -229,29 +261,31 @@ object ModifierCheckerCore {
private fun compatibility(first: KtModifierKeywordToken, second: KtModifierKeywordToken): Compatibility { private fun compatibility(first: KtModifierKeywordToken, second: KtModifierKeywordToken): Compatibility {
return if (first == second) { return if (first == second) {
Compatibility.REPEATED Compatibility.REPEATED
} } else {
else {
mutualCompatibility[Pair(first, second)] ?: Compatibility.COMPATIBLE mutualCompatibility[Pair(first, second)] ?: Compatibility.COMPATIBLE
} }
} }
private fun checkCompatibility(trace: BindingTrace, private fun checkCompatibility(
firstNode: ASTNode, trace: BindingTrace,
secondNode: ASTNode, firstNode: ASTNode,
owner: PsiElement, secondNode: ASTNode,
incorrectNodes: MutableSet<ASTNode>) { owner: PsiElement,
incorrectNodes: MutableSet<ASTNode>
) {
val first = firstNode.elementType as KtModifierKeywordToken val first = firstNode.elementType as KtModifierKeywordToken
val second = secondNode.elementType as KtModifierKeywordToken val second = secondNode.elementType as KtModifierKeywordToken
val compatibility = compatibility(first, second) val compatibility = compatibility(first, second)
when (compatibility) { when (compatibility) {
Compatibility.COMPATIBLE -> {} Compatibility.COMPATIBLE -> {
}
Compatibility.REPEATED -> if (incorrectNodes.add(secondNode)) { Compatibility.REPEATED -> if (incorrectNodes.add(secondNode)) {
trace.report(Errors.REPEATED_MODIFIER.on (secondNode.psi, first)) trace.report(Errors.REPEATED_MODIFIER.on(secondNode.psi, first))
} }
Compatibility.REDUNDANT -> Compatibility.REDUNDANT ->
trace.report(Errors.REDUNDANT_MODIFIER.on(secondNode.psi, second, first)) trace.report(Errors.REDUNDANT_MODIFIER.on(secondNode.psi, second, first))
Compatibility.REVERSE_REDUNDANT -> Compatibility.REVERSE_REDUNDANT ->
trace.report(Errors.REDUNDANT_MODIFIER.on(firstNode.psi, first, second)) trace.report(Errors.REDUNDANT_MODIFIER.on(firstNode.psi, first, second))
Compatibility.DEPRECATED -> { Compatibility.DEPRECATED -> {
trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(firstNode.psi, first, second)) trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(firstNode.psi, first, second))
trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(secondNode.psi, second, first)) trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(secondNode.psi, second, first))
@@ -285,18 +319,30 @@ object ModifierCheckerCore {
deprecatedModifierReplacement != null -> deprecatedModifierReplacement != null ->
trace.report(Errors.DEPRECATED_MODIFIER.on(node.psi, modifier, deprecatedModifierReplacement)) trace.report(Errors.DEPRECATED_MODIFIER.on(node.psi, modifier, deprecatedModifierReplacement))
actualTargets.any { it in deprecatedTargets } -> actualTargets.any { it in deprecatedTargets } ->
trace.report(Errors.DEPRECATED_MODIFIER_FOR_TARGET.on(node.psi, modifier, actualTargets.firstOrNull()?.description ?: "this")) trace.report(
Errors.DEPRECATED_MODIFIER_FOR_TARGET.on(
node.psi,
modifier,
actualTargets.firstOrNull()?.description ?: "this"
)
)
actualTargets.any { it in redundantTargets } -> actualTargets.any { it in redundantTargets } ->
trace.report(Errors.REDUNDANT_MODIFIER_FOR_TARGET.on(node.psi, modifier, actualTargets.firstOrNull()?.description ?: "this")) trace.report(
Errors.REDUNDANT_MODIFIER_FOR_TARGET.on(
node.psi,
modifier,
actualTargets.firstOrNull()?.description ?: "this"
)
)
} }
return true return true
} }
private fun checkLanguageLevelSupport( private fun checkLanguageLevelSupport(
trace: BindingTrace, trace: BindingTrace,
node: ASTNode, node: ASTNode,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
actualTargets: List<KotlinTarget> actualTargets: List<KotlinTarget>
): Boolean { ): Boolean {
val modifier = node.elementType as KtModifierKeywordToken val modifier = node.elementType as KtModifierKeywordToken
@@ -313,8 +359,7 @@ object ModifierCheckerCore {
if (featureSupport == LanguageFeature.State.DISABLED) { if (featureSupport == LanguageFeature.State.DISABLED) {
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData)) trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
} } else {
else {
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData)) trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData))
} }
return false return false
@@ -330,7 +375,12 @@ object ModifierCheckerCore {
// Should return false if error is reported, true otherwise // Should return false if error is reported, true otherwise
private fun checkParent(trace: BindingTrace, node: ASTNode, parentDescriptor: DeclarationDescriptor?, languageVersionSettings: LanguageVersionSettings): Boolean { private fun checkParent(
trace: BindingTrace,
node: ASTNode,
parentDescriptor: DeclarationDescriptor?,
languageVersionSettings: LanguageVersionSettings
): Boolean {
val modifier = node.elementType as KtModifierKeywordToken val modifier = node.elementType as KtModifierKeywordToken
val actualParents: List<KotlinTarget> = when (parentDescriptor) { val actualParents: List<KotlinTarget> = when (parentDescriptor) {
is ClassDescriptor -> KotlinTarget.classActualTargets(parentDescriptor) is ClassDescriptor -> KotlinTarget.classActualTargets(parentDescriptor)
@@ -341,23 +391,35 @@ object ModifierCheckerCore {
} }
val deprecatedParents = deprecatedParentTargetMap[modifier] val deprecatedParents = deprecatedParentTargetMap[modifier]
if (deprecatedParents != null && actualParents.any { it in deprecatedParents }) { if (deprecatedParents != null && actualParents.any { it in deprecatedParents }) {
trace.report(Errors.DEPRECATED_MODIFIER_CONTAINING_DECLARATION.on(node.psi, modifier, actualParents.firstOrNull()?.description ?: "this scope")) trace.report(
Errors.DEPRECATED_MODIFIER_CONTAINING_DECLARATION.on(
node.psi,
modifier,
actualParents.firstOrNull()?.description ?: "this scope"
)
)
return true return true
} }
val possibleParentPredicate = possibleParentTargetPredicateMap[modifier] ?: return true val possibleParentPredicate = possibleParentTargetPredicateMap[modifier] ?: return true
if (actualParents.any { possibleParentPredicate.isAllowed(it, languageVersionSettings) }) return true if (actualParents.any { possibleParentPredicate.isAllowed(it, languageVersionSettings) }) return true
trace.report(Errors.WRONG_MODIFIER_CONTAINING_DECLARATION.on(node.psi, modifier, actualParents.firstOrNull()?.description ?: "this scope")) trace.report(
Errors.WRONG_MODIFIER_CONTAINING_DECLARATION.on(
node.psi,
modifier,
actualParents.firstOrNull()?.description ?: "this scope"
)
)
return false return false
} }
private val MODIFIER_KEYWORD_SET = TokenSet.orSet(KtTokens.SOFT_KEYWORDS, TokenSet.create(KtTokens.IN_KEYWORD)) private val MODIFIER_KEYWORD_SET = TokenSet.orSet(KtTokens.SOFT_KEYWORDS, TokenSet.create(KtTokens.IN_KEYWORD))
private fun checkModifierList( private fun checkModifierList(
list: KtModifierList, list: KtModifierList,
trace: BindingTrace, trace: BindingTrace,
parentDescriptor: DeclarationDescriptor?, parentDescriptor: DeclarationDescriptor?,
actualTargets: List<KotlinTarget>, actualTargets: List<KotlinTarget>,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
) { ) {
// It's a list of all nodes with error already reported // It's a list of all nodes with error already reported
// General strategy: report no more than one error but any number of warnings // General strategy: report no more than one error but any number of warnings
@@ -381,10 +443,10 @@ object ModifierCheckerCore {
} }
fun check( fun check(
listOwner: KtModifierListOwner, listOwner: KtModifierListOwner,
trace: BindingTrace, trace: BindingTrace,
descriptor: DeclarationDescriptor?, descriptor: DeclarationDescriptor?,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
) { ) {
if (listOwner is KtDeclarationWithBody) { if (listOwner is KtDeclarationWithBody) {
// JetFunction or JetPropertyAccessor // JetFunction or JetPropertyAccessor
@@ -410,18 +472,19 @@ private fun always(target: KotlinTarget, vararg targets: KotlinTarget) = object
private val targetSet = EnumSet.of(target, *targets) private val targetSet = EnumSet.of(target, *targets)
override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) =
target in targetSet target in targetSet
} }
private fun ifSupported(languageFeature: LanguageFeature, target: KotlinTarget, vararg targets: KotlinTarget) = object : TargetAllowedPredicate { private fun ifSupported(languageFeature: LanguageFeature, target: KotlinTarget, vararg targets: KotlinTarget) =
private val targetSet = EnumSet.of(target, *targets) object : TargetAllowedPredicate {
private val targetSet = EnumSet.of(target, *targets)
override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) =
languageVersionSettings.supportsFeature(languageFeature) && target in targetSet languageVersionSettings.supportsFeature(languageFeature) && target in targetSet
} }
private fun or(p1: TargetAllowedPredicate, p2: TargetAllowedPredicate) = object : TargetAllowedPredicate { private fun or(p1: TargetAllowedPredicate, p2: TargetAllowedPredicate) = object : TargetAllowedPredicate {
override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) =
p1.isAllowed(target, languageVersionSettings) || p1.isAllowed(target, languageVersionSettings) ||
p2.isAllowed(target, languageVersionSettings) p2.isAllowed(target, languageVersionSettings)
} }
@@ -31,9 +31,9 @@ import org.jetbrains.kotlin.utils.DFS
object NonExpansiveInheritanceRestrictionChecker { object NonExpansiveInheritanceRestrictionChecker {
@JvmStatic @JvmStatic
fun check( fun check(
declaration: KtClass, declaration: KtClass,
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
diagnosticHolder: DiagnosticSink diagnosticHolder: DiagnosticSink
) { ) {
val typeConstructor = classDescriptor.typeConstructor val typeConstructor = classDescriptor.typeConstructor
if (typeConstructor.parameters.isEmpty()) return if (typeConstructor.parameters.isEmpty()) return
@@ -104,11 +104,14 @@ object NonExpansiveInheritanceRestrictionChecker {
for (typeParameter in typeParameters) { for (typeParameter in typeParameters) {
if (typeParameter.defaultType in constituents || typeParameter.defaultType.makeNullableAsSpecified(true) in constituents) { if (typeParameter.defaultType in constituents || typeParameter.defaultType.makeNullableAsSpecified(true) in constituents) {
addEdge(typeParameter, constituentTypeConstructor.parameters[i], !TypeUtils.isTypeParameter(typeProjection.type)) addEdge(
typeParameter,
constituentTypeConstructor.parameters[i],
!TypeUtils.isTypeParameter(typeProjection.type)
)
} }
} }
} } else {
else {
// Furthermore, if T appears as a constituent type of an element of the B-closure of the set of lower and // Furthermore, if T appears as a constituent type of an element of the B-closure of the set of lower and
// upper bounds of a skolem type variable Q in a skolemization of a projected generic type in ST, add an // upper bounds of a skolem type variable Q in a skolemization of a projected generic type in ST, add an
// expanding edge from T to V, where V is the type parameter corresponding to Q. // expanding edge from T to V, where V is the type parameter corresponding to Q.
@@ -116,7 +119,8 @@ object NonExpansiveInheritanceRestrictionChecker {
val bounds = hashSetOf<KotlinType>() val bounds = hashSetOf<KotlinType>()
val substitutor = TypeConstructorSubstitution.create(constituentType).buildSubstitutor() val substitutor = TypeConstructorSubstitution.create(constituentType).buildSubstitutor()
val adaptedUpperBounds = originalTypeParameter.upperBounds.mapNotNull { substitutor.substitute(it, Variance.INVARIANT) } val adaptedUpperBounds =
originalTypeParameter.upperBounds.mapNotNull { substitutor.substitute(it, Variance.INVARIANT) }
bounds.addAll(adaptedUpperBounds) bounds.addAll(adaptedUpperBounds)
if (!typeProjection.isStarProjection) { if (!typeProjection.isStarProjection) {
@@ -138,7 +142,7 @@ object NonExpansiveInheritanceRestrictionChecker {
private data class ExpansiveEdge<out T>(val from: T, val to: T) private data class ExpansiveEdge<out T>(val from: T, val to: T)
private interface Graph<T> { private interface Graph<T> {
fun getNeighbors(node: T): Collection<T> fun getNeighbors(node: T): Collection<T>
val expansiveEdges: Set<ExpansiveEdge<T>> val expansiveEdges: Set<ExpansiveEdge<T>>
} }
@@ -42,9 +42,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
import java.util.* import java.util.*
class OverrideResolver( class OverrideResolver(
private val trace: BindingTrace, private val trace: BindingTrace,
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper, private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
fun check(c: TopDownAnalysisContext) { fun check(c: TopDownAnalysisContext) {
@@ -106,14 +106,17 @@ class OverrideResolver(
// don't care // don't care
} }
override fun abstractMemberWithMoreSpecificType(abstractMember: CallableMemberDescriptor, concreteMember: CallableMemberDescriptor) { override fun abstractMemberWithMoreSpecificType(
abstractMember: CallableMemberDescriptor,
concreteMember: CallableMemberDescriptor
) {
shouldImplement.add(abstractMember) shouldImplement.add(abstractMember)
} }
} }
private inner class CollectErrorInformationForInheritedMembersStrategy( private inner class CollectErrorInformationForInheritedMembersStrategy(
private val klass: KtClassOrObject, private val klass: KtClassOrObject,
private val classDescriptor: ClassDescriptor private val classDescriptor: ClassDescriptor
) : CheckInheritedSignaturesReportStrategy, CheckOverrideReportStrategy { ) : CheckInheritedSignaturesReportStrategy, CheckOverrideReportStrategy {
private val abstractNoImpl = linkedSetOf<CallableMemberDescriptor>() private val abstractNoImpl = linkedSetOf<CallableMemberDescriptor>()
@@ -147,24 +150,25 @@ class OverrideResolver(
if (descriptor1 is PropertyDescriptor && descriptor2 is PropertyDescriptor) { if (descriptor1 is PropertyDescriptor && descriptor2 is PropertyDescriptor) {
if (descriptor1.isVar || descriptor2.isVar) { if (descriptor1.isVar || descriptor2.isVar) {
reportInheritanceConflictIfRequired(VAR_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2) reportInheritanceConflictIfRequired(VAR_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2)
} } else {
else {
reportInheritanceConflictIfRequired(PROPERTY_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2) reportInheritanceConflictIfRequired(PROPERTY_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2)
} }
} } else {
else {
reportInheritanceConflictIfRequired(RETURN_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2) reportInheritanceConflictIfRequired(RETURN_TYPE_MISMATCH_ON_INHERITANCE, descriptor1, descriptor2)
} }
} }
override fun abstractMemberWithMoreSpecificType(abstractMember: CallableMemberDescriptor, concreteMember: CallableMemberDescriptor) { override fun abstractMemberWithMoreSpecificType(
abstractMember: CallableMemberDescriptor,
concreteMember: CallableMemberDescriptor
) {
typeMismatchOnInheritance(abstractMember, concreteMember) typeMismatchOnInheritance(abstractMember, concreteMember)
} }
private fun reportInheritanceConflictIfRequired( private fun reportInheritanceConflictIfRequired(
diagnosticFactory: DiagnosticFactory2<KtClassOrObject, CallableMemberDescriptor, CallableMemberDescriptor>, diagnosticFactory: DiagnosticFactory2<KtClassOrObject, CallableMemberDescriptor, CallableMemberDescriptor>,
descriptor1: CallableMemberDescriptor, descriptor1: CallableMemberDescriptor,
descriptor2: CallableMemberDescriptor descriptor2: CallableMemberDescriptor
) { ) {
if (!onceErrorsReported.contains(diagnosticFactory)) { if (!onceErrorsReported.contains(diagnosticFactory)) {
onceErrorsReported.add(diagnosticFactory) onceErrorsReported.add(diagnosticFactory)
@@ -177,19 +181,21 @@ class OverrideResolver(
} }
override fun returnTypeMismatchOnOverride( override fun returnTypeMismatchOnOverride(
overriding: CallableMemberDescriptor, overriding: CallableMemberDescriptor,
overridden: CallableMemberDescriptor overridden: CallableMemberDescriptor
) { ) {
reportDelegationProblemIfRequired( reportDelegationProblemIfRequired(
RETURN_TYPE_MISMATCH_BY_DELEGATION, RETURN_TYPE_MISMATCH_ON_INHERITANCE, overriding, overridden) RETURN_TYPE_MISMATCH_BY_DELEGATION, RETURN_TYPE_MISMATCH_ON_INHERITANCE, overriding, overridden
)
} }
override fun propertyTypeMismatchOnOverride( override fun propertyTypeMismatchOnOverride(
overriding: PropertyDescriptor, overriding: PropertyDescriptor,
overridden: PropertyDescriptor overridden: PropertyDescriptor
) { ) {
reportDelegationProblemIfRequired( reportDelegationProblemIfRequired(
PROPERTY_TYPE_MISMATCH_BY_DELEGATION, PROPERTY_TYPE_MISMATCH_ON_INHERITANCE, overriding, overridden) PROPERTY_TYPE_MISMATCH_BY_DELEGATION, PROPERTY_TYPE_MISMATCH_ON_INHERITANCE, overriding, overridden
)
} }
override fun varOverriddenByVal(overriding: CallableMemberDescriptor, overridden: CallableMemberDescriptor) { override fun varOverriddenByVal(overriding: CallableMemberDescriptor, overridden: CallableMemberDescriptor) {
@@ -197,14 +203,16 @@ class OverrideResolver(
} }
private fun reportDelegationProblemIfRequired( private fun reportDelegationProblemIfRequired(
diagnosticFactory: DiagnosticFactory2<KtClassOrObject, CallableMemberDescriptor, CallableMemberDescriptor>, diagnosticFactory: DiagnosticFactory2<KtClassOrObject, CallableMemberDescriptor, CallableMemberDescriptor>,
relevantDiagnosticFromInheritance: DiagnosticFactoryWithPsiElement<*, *>?, relevantDiagnosticFromInheritance: DiagnosticFactoryWithPsiElement<*, *>?,
delegate: CallableMemberDescriptor, delegate: CallableMemberDescriptor,
overridden: CallableMemberDescriptor overridden: CallableMemberDescriptor
) { ) {
assert(delegate.kind == DELEGATION) { "Delegate expected, got " + delegate + " of kind " + delegate.kind } assert(delegate.kind == DELEGATION) { "Delegate expected, got " + delegate + " of kind " + delegate.kind }
if (!onceErrorsReported.contains(diagnosticFactory) && (relevantDiagnosticFromInheritance == null || !onceErrorsReported.contains(relevantDiagnosticFromInheritance))) { if (!onceErrorsReported.contains(diagnosticFactory) && (relevantDiagnosticFromInheritance == null || !onceErrorsReported.contains(
relevantDiagnosticFromInheritance
))) {
onceErrorsReported.add(diagnosticFactory) onceErrorsReported.add(diagnosticFactory)
trace.report(diagnosticFactory.on(klass, delegate, overridden)) trace.report(diagnosticFactory.on(klass, delegate, overridden))
} }
@@ -214,8 +222,7 @@ class OverrideResolver(
val canHaveAbstractMembers = classCanHaveAbstractFakeOverride(classDescriptor) val canHaveAbstractMembers = classCanHaveAbstractFakeOverride(classDescriptor)
if (abstractInBaseClassNoImpl.isNotEmpty() && !canHaveAbstractMembers) { if (abstractInBaseClassNoImpl.isNotEmpty() && !canHaveAbstractMembers) {
trace.report(ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractInBaseClassNoImpl.first())) trace.report(ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractInBaseClassNoImpl.first()))
} } else if (abstractNoImpl.isNotEmpty() && !canHaveAbstractMembers) {
else if (abstractNoImpl.isNotEmpty() && !canHaveAbstractMembers) {
trace.report(ABSTRACT_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractNoImpl.first())) trace.report(ABSTRACT_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractNoImpl.first()))
} }
@@ -223,8 +230,7 @@ class OverrideResolver(
multipleImplementations.removeAll(conflictingReturnTypes) multipleImplementations.removeAll(conflictingReturnTypes)
if (!conflictingInterfaceMembers.isEmpty()) { if (!conflictingInterfaceMembers.isEmpty()) {
trace.report(MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.on(klass, klass, conflictingInterfaceMembers.iterator().next())) trace.report(MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.on(klass, klass, conflictingInterfaceMembers.iterator().next()))
} } else if (!multipleImplementations.isEmpty()) {
else if (!multipleImplementations.isEmpty()) {
trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(klass, klass, multipleImplementations.iterator().next())) trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(klass, klass, multipleImplementations.iterator().next()))
} }
} }
@@ -246,8 +252,7 @@ class OverrideResolver(
if (declared.kind == CallableMemberDescriptor.Kind.SYNTHESIZED) { if (declared.kind == CallableMemberDescriptor.Kind.SYNTHESIZED) {
if (DataClassDescriptorResolver.isComponentLike(declared.name)) { if (DataClassDescriptorResolver.isComponentLike(declared.name)) {
checkOverrideForComponentFunction(declared) checkOverrideForComponentFunction(declared)
} } else if (declared.name == DataClassDescriptorResolver.COPY_METHOD_NAME) {
else if (declared.name == DataClassDescriptorResolver.COPY_METHOD_NAME) {
checkOverrideForCopyFunction(declared) checkOverrideForCopyFunction(declared)
} }
return return
@@ -257,7 +262,8 @@ class OverrideResolver(
return return
} }
val member = DescriptorToSourceUtils.descriptorToDeclaration(declared) as KtNamedDeclaration? ?: throw IllegalStateException("declared descriptor is not resolved to declaration: " + declared) val member = DescriptorToSourceUtils.descriptorToDeclaration(declared) as KtNamedDeclaration?
?: throw IllegalStateException("declared descriptor is not resolved to declaration: " + declared)
val modifierList = member.modifierList val modifierList = member.modifierList
val hasOverrideNode = modifierList != null && modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) val hasOverrideNode = modifierList != null && modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)
@@ -288,8 +294,7 @@ class OverrideResolver(
typeMismatchError = true typeMismatchError = true
if (overridden.isVar) { if (overridden.isVar) {
trace.report(VAR_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden)) trace.report(VAR_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden))
} } else {
else {
trace.report(PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden)) trace.report(PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden))
} }
} }
@@ -302,7 +307,10 @@ class OverrideResolver(
} }
} }
override fun cannotOverrideInvisibleMember(overriding: CallableMemberDescriptor, invisibleOverridden: CallableMemberDescriptor) { override fun cannotOverrideInvisibleMember(
overriding: CallableMemberDescriptor,
invisibleOverridden: CallableMemberDescriptor
) {
trace.report(CANNOT_OVERRIDE_INVISIBLE_MEMBER.on(member, declared, invisibleOverridden)) trace.report(CANNOT_OVERRIDE_INVISIBLE_MEMBER.on(member, declared, invisibleOverridden))
} }
@@ -310,8 +318,7 @@ class OverrideResolver(
trace.report(NOTHING_TO_OVERRIDE.on(member, declared)) trace.report(NOTHING_TO_OVERRIDE.on(member, declared))
} }
}) })
} } else if (!overriddenDescriptors.isEmpty() && !overridesBackwardCompatibilityHelper.overrideCanBeOmitted(declared)) {
else if (!overriddenDescriptors.isEmpty() && !overridesBackwardCompatibilityHelper.overrideCanBeOmitted(declared)) {
val overridden = overriddenDescriptors.iterator().next() val overridden = overriddenDescriptors.iterator().next()
trace.report(VIRTUAL_MEMBER_HIDDEN.on(member, declared, overridden, overridden.containingDeclaration)) trace.report(VIRTUAL_MEMBER_HIDDEN.on(member, declared, overridden, overridden.containingDeclaration))
} }
@@ -354,8 +361,7 @@ class OverrideResolver(
val dataModifier = findDataModifierForDataClass(copyFunction.containingDeclaration) val dataModifier = findDataModifierForDataClass(copyFunction.containingDeclaration)
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDataClassesOverridingCopy)) { if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDataClassesOverridingCopy)) {
trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR.on(dataModifier, copyFunction, baseClassifier)) trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR.on(dataModifier, copyFunction, baseClassifier))
} } else {
else {
trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING.on(dataModifier, copyFunction, baseClassifier)) trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING.on(dataModifier, copyFunction, baseClassifier))
} }
} }
@@ -397,15 +403,15 @@ class OverrideResolver(
if (isDeclaration) { if (isDeclaration) {
checkNameAndDefaultForDeclaredParameter(parameterFromSubclass, multipleDefaultsInSuper) checkNameAndDefaultForDeclaredParameter(parameterFromSubclass, multipleDefaultsInSuper)
} } else {
else {
checkNameAndDefaultForFakeOverrideParameter(declared, parameterFromSubclass, multipleDefaultsInSuper) checkNameAndDefaultForFakeOverrideParameter(declared, parameterFromSubclass, multipleDefaultsInSuper)
} }
} }
} }
private fun checkNameAndDefaultForDeclaredParameter(descriptor: ValueParameterDescriptor, multipleDefaultsInSuper: Boolean) { private fun checkNameAndDefaultForDeclaredParameter(descriptor: ValueParameterDescriptor, multipleDefaultsInSuper: Boolean) {
val parameter = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? KtParameter ?: error("Declaration not found for parameter: " + descriptor) val parameter = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? KtParameter
?: error("Declaration not found for parameter: " + descriptor)
if (descriptor.declaresDefaultValue()) { if (descriptor.declaresDefaultValue()) {
trace.report(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE.on(parameter)) trace.report(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE.on(parameter))
@@ -418,22 +424,25 @@ class OverrideResolver(
for (parameterFromSuperclass in descriptor.overriddenDescriptors) { for (parameterFromSuperclass in descriptor.overriddenDescriptors) {
if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) { if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) {
trace.report(PARAMETER_NAME_CHANGED_ON_OVERRIDE.on( trace.report(
PARAMETER_NAME_CHANGED_ON_OVERRIDE.on(
parameter, parameter,
parameterFromSuperclass.containingDeclaration.containingDeclaration as ClassDescriptor, parameterFromSuperclass.containingDeclaration.containingDeclaration as ClassDescriptor,
parameterFromSuperclass) parameterFromSuperclass
)
) )
} }
} }
} }
private fun checkNameAndDefaultForFakeOverrideParameter( private fun checkNameAndDefaultForFakeOverrideParameter(
containingFunction: CallableMemberDescriptor, containingFunction: CallableMemberDescriptor,
descriptor: ValueParameterDescriptor, descriptor: ValueParameterDescriptor,
multipleDefaultsInSuper: Boolean multipleDefaultsInSuper: Boolean
) { ) {
val containingClass = containingFunction.containingDeclaration val containingClass = containingFunction.containingDeclaration
val classElement = DescriptorToSourceUtils.descriptorToDeclaration(containingClass) as KtClassOrObject? ?: error("Declaration not found for class: " + containingClass) val classElement = DescriptorToSourceUtils.descriptorToDeclaration(containingClass) as KtClassOrObject?
?: error("Declaration not found for class: " + containingClass)
if (multipleDefaultsInSuper) { if (multipleDefaultsInSuper) {
trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE.on(classElement, descriptor)) trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE.on(classElement, descriptor))
@@ -441,10 +450,12 @@ class OverrideResolver(
for (parameterFromSuperclass in descriptor.overriddenDescriptors) { for (parameterFromSuperclass in descriptor.overriddenDescriptors) {
if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) { if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) {
trace.report(DIFFERENT_NAMES_FOR_THE_SAME_PARAMETER_IN_SUPERTYPES.on( trace.report(
DIFFERENT_NAMES_FOR_THE_SAME_PARAMETER_IN_SUPERTYPES.on(
classElement, classElement,
containingFunction.overriddenDescriptors, containingFunction.overriddenDescriptors,
parameterFromSuperclass.index + 1) parameterFromSuperclass.index + 1
)
) )
} }
} }
@@ -468,11 +479,24 @@ class OverrideResolver(
for (descriptor in memberDescriptor.overriddenDescriptors) { for (descriptor in memberDescriptor.overriddenDescriptors) {
val compare = Visibilities.compare(visibility, descriptor.visibility) val compare = Visibilities.compare(visibility, descriptor.visibility)
if (compare == null) { if (compare == null) {
trace.report(CANNOT_CHANGE_ACCESS_PRIVILEGE.on(declaration, descriptor.visibility, descriptor, descriptor.containingDeclaration)) trace.report(
CANNOT_CHANGE_ACCESS_PRIVILEGE.on(
declaration,
descriptor.visibility,
descriptor,
descriptor.containingDeclaration
)
)
return return
} } else if (compare < 0) {
else if (compare < 0) { trace.report(
trace.report(CANNOT_WEAKEN_ACCESS_PRIVILEGE.on(declaration, descriptor.visibility, descriptor, descriptor.containingDeclaration)) CANNOT_WEAKEN_ACCESS_PRIVILEGE.on(
declaration,
descriptor.visibility,
descriptor,
descriptor.containingDeclaration
)
)
return return
} }
} }
@@ -481,8 +505,8 @@ class OverrideResolver(
companion object { companion object {
fun resolveUnknownVisibilities( fun resolveUnknownVisibilities(
descriptors: Collection<CallableMemberDescriptor>, descriptors: Collection<CallableMemberDescriptor>,
trace: BindingTrace trace: BindingTrace
) { ) {
for (descriptor in descriptors) { for (descriptor in descriptors) {
OverridingUtil.resolveUnknownVisibilityForMember(descriptor, createCannotInferVisibilityReporter(trace)) OverridingUtil.resolveUnknownVisibilityForMember(descriptor, createCannotInferVisibilityReporter(trace))
@@ -515,9 +539,9 @@ class OverrideResolver(
} }
private fun checkInheritedAndDelegatedSignatures( private fun checkInheritedAndDelegatedSignatures(
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
inheritedReportStrategy: CheckInheritedSignaturesReportStrategy, inheritedReportStrategy: CheckInheritedSignaturesReportStrategy,
overrideReportStrategyForDelegates: CheckOverrideReportStrategy? overrideReportStrategyForDelegates: CheckOverrideReportStrategy?
) { ) {
for (member in DescriptorUtils.getAllDescriptors(classDescriptor.defaultType.memberScope)) { for (member in DescriptorUtils.getAllDescriptors(classDescriptor.defaultType.memberScope)) {
if (member is CallableMemberDescriptor) { if (member is CallableMemberDescriptor) {
@@ -527,9 +551,9 @@ class OverrideResolver(
} }
private fun checkInheritedAndDelegatedSignatures( private fun checkInheritedAndDelegatedSignatures(
descriptor: CallableMemberDescriptor, descriptor: CallableMemberDescriptor,
reportingStrategy: CheckInheritedSignaturesReportStrategy, reportingStrategy: CheckInheritedSignaturesReportStrategy,
overrideReportStrategyForDelegates: CheckOverrideReportStrategy? overrideReportStrategyForDelegates: CheckOverrideReportStrategy?
) { ) {
val kind = descriptor.kind val kind = descriptor.kind
if (kind != FAKE_OVERRIDE && kind != DELEGATION) return if (kind != FAKE_OVERRIDE && kind != DELEGATION) return
@@ -545,9 +569,11 @@ class OverrideResolver(
val allOverriddenDeclarations = ContainerUtil.flatten(overriddenDeclarationsByDirectParent.values) val allOverriddenDeclarations = ContainerUtil.flatten(overriddenDeclarationsByDirectParent.values)
val allFilteredOverriddenDeclarations = OverridingUtil.filterOutOverridden( val allFilteredOverriddenDeclarations = OverridingUtil.filterOutOverridden(
Sets.newLinkedHashSet(allOverriddenDeclarations)) Sets.newLinkedHashSet(allOverriddenDeclarations)
)
val relevantDirectlyOverridden = getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations) val relevantDirectlyOverridden =
getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations)
checkInheritedDescriptorsGroup(descriptor, relevantDirectlyOverridden, reportingStrategy) checkInheritedDescriptorsGroup(descriptor, relevantDirectlyOverridden, reportingStrategy)
@@ -560,8 +586,8 @@ class OverrideResolver(
} }
val (concreteOverridden, abstractOverridden) = relevantDirectlyOverridden val (concreteOverridden, abstractOverridden) = relevantDirectlyOverridden
.filter { !isOrOverridesSynthesized(it) } .filter { !isOrOverridesSynthesized(it) }
.partition { it.modality != Modality.ABSTRACT } .partition { it.modality != Modality.ABSTRACT }
val numImplementations = concreteOverridden.size val numImplementations = concreteOverridden.size
@@ -587,8 +613,8 @@ class OverrideResolver(
} }
private fun checkMissingOverridesByJava8Restrictions( private fun checkMissingOverridesByJava8Restrictions(
relevantDirectlyOverridden: Set<CallableMemberDescriptor>, relevantDirectlyOverridden: Set<CallableMemberDescriptor>,
reportingStrategy: CheckInheritedSignaturesReportStrategy reportingStrategy: CheckInheritedSignaturesReportStrategy
) { ) {
// Java 8: // Java 8:
// -- class should implement an abstract member of a super-class, // -- class should implement an abstract member of a super-class,
@@ -608,8 +634,7 @@ class OverrideResolver(
if (overridden.modality === Modality.ABSTRACT) { if (overridden.modality === Modality.ABSTRACT) {
overridesAbstractInBaseClass = overridden overridesAbstractInBaseClass = overridden
} }
} } else if (containingDeclaration.kind == ClassKind.INTERFACE) {
else if (containingDeclaration.kind == ClassKind.INTERFACE) {
overriddenInterfaceMembers.add(overridden) overriddenInterfaceMembers.add(overridden)
if (overridden.modality !== Modality.ABSTRACT) { if (overridden.modality !== Modality.ABSTRACT) {
overridesNonAbstractInterfaceMember = true overridesNonAbstractInterfaceMember = true
@@ -629,14 +654,14 @@ class OverrideResolver(
} }
private fun collectAbstractMethodsWithMoreSpecificReturnType( private fun collectAbstractMethodsWithMoreSpecificReturnType(
abstractOverridden: List<CallableMemberDescriptor>, abstractOverridden: List<CallableMemberDescriptor>,
implementation: CallableMemberDescriptor implementation: CallableMemberDescriptor
): List<CallableMemberDescriptor> = ): List<CallableMemberDescriptor> =
abstractOverridden.filter { abstractMember -> !isReturnTypeOkForOverride(abstractMember, implementation) } abstractOverridden.filter { abstractMember -> !isReturnTypeOkForOverride(abstractMember, implementation) }
private fun getRelevantDirectlyOverridden( private fun getRelevantDirectlyOverridden(
overriddenByParent: MutableMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>>, overriddenByParent: MutableMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>>,
allFilteredOverriddenDeclarations: Set<CallableMemberDescriptor> allFilteredOverriddenDeclarations: Set<CallableMemberDescriptor>
): Set<CallableMemberDescriptor> { ): Set<CallableMemberDescriptor> {
/* Let the following class hierarchy is declared: /* Let the following class hierarchy is declared:
@@ -678,9 +703,9 @@ class OverrideResolver(
} }
private fun isRelevant( private fun isRelevant(
declarationSet: Set<CallableMemberDescriptor>, declarationSet: Set<CallableMemberDescriptor>,
allDeclarationSets: Collection<Set<CallableMemberDescriptor>>, allDeclarationSets: Collection<Set<CallableMemberDescriptor>>,
allFilteredOverriddenDeclarations: Set<CallableMemberDescriptor> allFilteredOverriddenDeclarations: Set<CallableMemberDescriptor>
): Boolean { ): Boolean {
for (otherSet in allDeclarationSets) { for (otherSet in allDeclarationSets) {
if (otherSet === declarationSet) continue if (otherSet === declarationSet) continue
@@ -691,7 +716,7 @@ class OverrideResolver(
} }
private fun collectOverriddenDeclarations( private fun collectOverriddenDeclarations(
directOverriddenDescriptors: Collection<CallableMemberDescriptor> directOverriddenDescriptors: Collection<CallableMemberDescriptor>
): MutableMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>> { ): MutableMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>> {
val overriddenDeclarationsByDirectParent = Maps.newLinkedHashMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>>() val overriddenDeclarationsByDirectParent = Maps.newLinkedHashMap<CallableMemberDescriptor, Set<CallableMemberDescriptor>>()
for (descriptor in directOverriddenDescriptors) { for (descriptor in directOverriddenDescriptors) {
@@ -703,9 +728,9 @@ class OverrideResolver(
} }
private fun checkInheritedDescriptorsGroup( private fun checkInheritedDescriptorsGroup(
descriptor: CallableMemberDescriptor, descriptor: CallableMemberDescriptor,
overriddenDescriptors: Collection<CallableMemberDescriptor>, overriddenDescriptors: Collection<CallableMemberDescriptor>,
reportingStrategy: CheckInheritedSignaturesReportStrategy reportingStrategy: CheckInheritedSignaturesReportStrategy
) { ) {
if (overriddenDescriptors.size <= 1) return if (overriddenDescriptors.size <= 1) return
@@ -713,12 +738,12 @@ class OverrideResolver(
for (overriddenDescriptor in overriddenDescriptors) { for (overriddenDescriptor in overriddenDescriptors) {
if (propertyDescriptor != null) { if (propertyDescriptor != null) {
val overriddenPropertyDescriptor = overriddenDescriptor.assertedCast<PropertyDescriptor> { "$overriddenDescriptor is not a property" } val overriddenPropertyDescriptor =
overriddenDescriptor.assertedCast<PropertyDescriptor> { "$overriddenDescriptor is not a property" }
if (!isPropertyTypeOkForOverride(overriddenPropertyDescriptor, propertyDescriptor)) { if (!isPropertyTypeOkForOverride(overriddenPropertyDescriptor, propertyDescriptor)) {
reportingStrategy.typeMismatchOnInheritance(propertyDescriptor, overriddenPropertyDescriptor) reportingStrategy.typeMismatchOnInheritance(propertyDescriptor, overriddenPropertyDescriptor)
} }
} } else {
else {
if (!isReturnTypeOkForOverride(overriddenDescriptor, descriptor)) { if (!isReturnTypeOkForOverride(overriddenDescriptor, descriptor)) {
reportingStrategy.typeMismatchOnInheritance(descriptor, overriddenDescriptor) reportingStrategy.typeMismatchOnInheritance(descriptor, overriddenDescriptor)
} }
@@ -727,8 +752,8 @@ class OverrideResolver(
} }
private fun checkOverridesForMemberMarkedOverride( private fun checkOverridesForMemberMarkedOverride(
declared: CallableMemberDescriptor, declared: CallableMemberDescriptor,
reportError: CheckOverrideReportForDeclaredMemberStrategy reportError: CheckOverrideReportForDeclaredMemberStrategy
) { ) {
val overriddenDescriptors = declared.overriddenDescriptors val overriddenDescriptors = declared.overriddenDescriptors
@@ -743,17 +768,16 @@ class OverrideResolver(
val invisibleOverriddenDescriptor = findInvisibleOverriddenDescriptor(declared, declaringClass) val invisibleOverriddenDescriptor = findInvisibleOverriddenDescriptor(declared, declaringClass)
if (invisibleOverriddenDescriptor != null) { if (invisibleOverriddenDescriptor != null) {
reportError.cannotOverrideInvisibleMember(declared, invisibleOverriddenDescriptor) reportError.cannotOverrideInvisibleMember(declared, invisibleOverriddenDescriptor)
} } else {
else {
reportError.nothingToOverride(declared) reportError.nothingToOverride(declared)
} }
} }
} }
private fun checkOverridesForMember( private fun checkOverridesForMember(
memberDescriptor: CallableMemberDescriptor, memberDescriptor: CallableMemberDescriptor,
overriddenDescriptors: Collection<CallableMemberDescriptor>, overriddenDescriptors: Collection<CallableMemberDescriptor>,
reportError: CheckOverrideReportStrategy reportError: CheckOverrideReportStrategy
) { ) {
val propertyMemberDescriptor = if (memberDescriptor is PropertyDescriptor) memberDescriptor else null val propertyMemberDescriptor = if (memberDescriptor is PropertyDescriptor) memberDescriptor else null
@@ -769,8 +793,7 @@ class OverrideResolver(
if (!isPropertyTypeOkForOverride(overriddenProperty, propertyMemberDescriptor)) { if (!isPropertyTypeOkForOverride(overriddenProperty, propertyMemberDescriptor)) {
reportError.propertyTypeMismatchOnOverride(propertyMemberDescriptor, overriddenProperty) reportError.propertyTypeMismatchOnOverride(propertyMemberDescriptor, overriddenProperty)
} }
} } else if (!isReturnTypeOkForOverride(overridden, memberDescriptor)) {
else if (!isReturnTypeOkForOverride(overridden, memberDescriptor)) {
reportError.returnTypeMismatchOnOverride(memberDescriptor, overridden) reportError.returnTypeMismatchOnOverride(memberDescriptor, overridden)
} }
@@ -781,8 +804,8 @@ class OverrideResolver(
} }
private fun isReturnTypeOkForOverride( private fun isReturnTypeOkForOverride(
superDescriptor: CallableDescriptor, superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor subDescriptor: CallableDescriptor
): Boolean { ): Boolean {
val typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor) ?: return false val typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor) ?: return false
@@ -796,8 +819,8 @@ class OverrideResolver(
} }
private fun prepareTypeSubstitutor( private fun prepareTypeSubstitutor(
superDescriptor: CallableDescriptor, superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor subDescriptor: CallableDescriptor
): TypeSubstitutor? { ): TypeSubstitutor? {
val superTypeParameters = superDescriptor.typeParameters val superTypeParameters = superDescriptor.typeParameters
val subTypeParameters = subDescriptor.typeParameters val subTypeParameters = subDescriptor.typeParameters
@@ -812,8 +835,8 @@ class OverrideResolver(
} }
private fun isPropertyTypeOkForOverride( private fun isPropertyTypeOkForOverride(
superDescriptor: PropertyDescriptor, superDescriptor: PropertyDescriptor,
subDescriptor: PropertyDescriptor subDescriptor: PropertyDescriptor
): Boolean { ): Boolean {
val typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor) ?: return false val typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor) ?: return false
@@ -821,8 +844,7 @@ class OverrideResolver(
return if (superDescriptor.isVar) { return if (superDescriptor.isVar) {
KotlinTypeChecker.DEFAULT.equalTypes(subDescriptor.type, substitutedSuperReturnType) KotlinTypeChecker.DEFAULT.equalTypes(subDescriptor.type, substitutedSuperReturnType)
} } else {
else {
KotlinTypeChecker.DEFAULT.isSubtypeOf(subDescriptor.type, substitutedSuperReturnType) KotlinTypeChecker.DEFAULT.isSubtypeOf(subDescriptor.type, substitutedSuperReturnType)
} }
} }
@@ -840,8 +862,8 @@ class OverrideResolver(
} }
private fun findInvisibleOverriddenDescriptor( private fun findInvisibleOverriddenDescriptor(
declared: CallableMemberDescriptor, declared: CallableMemberDescriptor,
declaringClass: ClassDescriptor declaringClass: ClassDescriptor
): CallableMemberDescriptor? { ): CallableMemberDescriptor? {
for (supertype in declaringClass.typeConstructor.supertypes) { for (supertype in declaringClass.typeConstructor.supertypes) {
val all = Sets.newLinkedHashSet<CallableMemberDescriptor>() val all = Sets.newLinkedHashSet<CallableMemberDescriptor>()
@@ -850,8 +872,10 @@ class OverrideResolver(
for (fromSuper in all) { for (fromSuper in all) {
if (OverridingUtil.DEFAULT.isOverridableBy(fromSuper, declared, null).result == OVERRIDABLE) { if (OverridingUtil.DEFAULT.isOverridableBy(fromSuper, declared, null).result == OVERRIDABLE) {
if (OverridingUtil.isVisibleForOverride(declared, fromSuper)) { if (OverridingUtil.isVisibleForOverride(declared, fromSuper)) {
throw IllegalStateException("Descriptor " + fromSuper + " is overridable by " + declared + throw IllegalStateException(
" and visible but does not appear in its getOverriddenDescriptors()") "Descriptor " + fromSuper + " is overridable by " + declared +
" and visible but does not appear in its getOverriddenDescriptors()"
)
} }
return fromSuper return fromSuper
} }
@@ -861,12 +885,12 @@ class OverrideResolver(
} }
fun shouldReportParameterNameOverrideWarning( fun shouldReportParameterNameOverrideWarning(
parameterFromSubclass: ValueParameterDescriptor, parameterFromSubclass: ValueParameterDescriptor,
parameterFromSuperclass: ValueParameterDescriptor parameterFromSuperclass: ValueParameterDescriptor
): Boolean { ): Boolean {
return parameterFromSubclass.containingDeclaration.hasStableParameterNames() && return parameterFromSubclass.containingDeclaration.hasStableParameterNames() &&
parameterFromSuperclass.containingDeclaration.hasStableParameterNames() && parameterFromSuperclass.containingDeclaration.hasStableParameterNames() &&
parameterFromSuperclass.name != parameterFromSubclass.name parameterFromSuperclass.name != parameterFromSubclass.name
} }
private fun checkPropertyKind(descriptor: CallableMemberDescriptor, isVar: Boolean): Boolean { private fun checkPropertyKind(descriptor: CallableMemberDescriptor, isVar: Boolean): Boolean {
@@ -32,7 +32,7 @@ interface TypeBinding<out P : PsiElement> : TypeHolder<TypeBinding<P>> {
override val arguments: List<TypeArgumentBinding<P>?> override val arguments: List<TypeArgumentBinding<P>?>
} }
interface TypeArgumentBinding<out P: PsiElement> : TypeHolderArgument<TypeBinding<P>> interface TypeArgumentBinding<out P : PsiElement> : TypeHolderArgument<TypeBinding<P>>
fun KtTypeReference.createTypeBinding(trace: BindingContext): TypeBinding<KtTypeElement>? { fun KtTypeReference.createTypeBinding(trace: BindingContext): TypeBinding<KtTypeElement>? {
val type = trace[BindingContext.TYPE, this] val type = trace[BindingContext.TYPE, this]
@@ -51,7 +51,7 @@ private fun createTypeBindingFromPsi(trace: BindingContext, psiElement: KtTypeEl
ExplicitTypeBinding(trace, psiElement, type) ExplicitTypeBinding(trace, psiElement, type)
} }
fun KtCallableDeclaration.createTypeBindingForReturnType(trace: BindingContext): TypeBinding<PsiElement>? { fun KtCallableDeclaration.createTypeBindingForReturnType(trace: BindingContext): TypeBinding<PsiElement>? {
val jetTypeReference = typeReference val jetTypeReference = typeReference
if (jetTypeReference != null) return jetTypeReference.createTypeBinding(trace) if (jetTypeReference != null) return jetTypeReference.createTypeBinding(trace)
@@ -61,16 +61,16 @@ fun KtCallableDeclaration.createTypeBindingForReturnType(trace: BindingContext):
return descriptor.returnType?.let { NoTypeElementBinding(trace, this, it) } return descriptor.returnType?.let { NoTypeElementBinding(trace, this, it) }
} }
private class TypeArgumentBindingImpl<out P: PsiElement>( private class TypeArgumentBindingImpl<out P : PsiElement>(
override val projection: TypeProjection, override val projection: TypeProjection,
override val typeParameter: TypeParameterDescriptor?, override val typeParameter: TypeParameterDescriptor?,
override val holder: TypeBinding<P> override val holder: TypeBinding<P>
) : TypeArgumentBinding<P> ) : TypeArgumentBinding<P>
private class ExplicitTypeBinding( private class ExplicitTypeBinding(
private val trace: BindingContext, private val trace: BindingContext,
override val psiElement: KtTypeElement, override val psiElement: KtTypeElement,
override val type: KotlinType override val type: KotlinType
) : TypeBinding<KtTypeElement> { ) : TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = false override val isInAbbreviation: Boolean get() = false
@@ -80,7 +80,7 @@ private class ExplicitTypeBinding(
assert(type.getAbbreviatedType() == null) { "Non-abbreviated type expected: $type" } assert(type.getAbbreviatedType() == null) { "Non-abbreviated type expected: $type" }
val isErrorBinding = run { val isErrorBinding = run {
val sizeIsEqual = psiTypeArguments.size == type.arguments.size val sizeIsEqual = psiTypeArguments.size == type.arguments.size
&& psiTypeArguments.size == type.constructor.parameters.size && psiTypeArguments.size == type.constructor.parameters.size
type.isError || !sizeIsEqual type.isError || !sizeIsEqual
} }
@@ -93,59 +93,57 @@ private class ExplicitTypeBinding(
val nextJetType = trace[BindingContext.TYPE, jetTypeReference] ?: return@map null val nextJetType = trace[BindingContext.TYPE, jetTypeReference] ?: return@map null
return@map TypeArgumentBindingImpl( return@map TypeArgumentBindingImpl(
TypeProjectionImpl(nextJetType), TypeProjectionImpl(nextJetType),
null, null,
createTypeBindingFromPsi(trace, jetTypeElement, nextJetType) createTypeBindingFromPsi(trace, jetTypeElement, nextJetType)
) )
} }
val typeProjection = type.arguments[index] val typeProjection = type.arguments[index]
return@map TypeArgumentBindingImpl( return@map TypeArgumentBindingImpl(
typeProjection, typeProjection,
type.constructor.parameters[index], type.constructor.parameters[index],
createTypeBindingFromPsi(trace, jetTypeElement, typeProjection.type) createTypeBindingFromPsi(trace, jetTypeElement, typeProjection.type)
) )
} }
} }
} }
private class AbbreviatedTypeBinding( private class AbbreviatedTypeBinding(
override val type: KotlinType, override val type: KotlinType,
override val psiElement: KtTypeElement override val psiElement: KtTypeElement
): TypeBinding<KtTypeElement> { ) : TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = true override val isInAbbreviation: Boolean get() = true
override val arguments: List<TypeArgumentBinding<KtTypeElement>?> override val arguments: List<TypeArgumentBinding<KtTypeElement>?>
get() = createTypeArgumentBindingsWithSinglePsiElement(type) { get() = createTypeArgumentBindingsWithSinglePsiElement(type) { argumentType ->
argumentType ->
AbbreviatedTypeBinding(argumentType, psiElement) AbbreviatedTypeBinding(argumentType, psiElement)
} }
} }
private class NoTypeElementBinding<out P : PsiElement>( private class NoTypeElementBinding<out P : PsiElement>(
private val trace: BindingContext, private val trace: BindingContext,
override val psiElement: P, override val psiElement: P,
override val type: KotlinType override val type: KotlinType
): TypeBinding<P> { ) : TypeBinding<P> {
override val isInAbbreviation: Boolean get() = false override val isInAbbreviation: Boolean get() = false
override val arguments: List<TypeArgumentBinding<P>?> override val arguments: List<TypeArgumentBinding<P>?>
get() = createTypeArgumentBindingsWithSinglePsiElement(type) { get() = createTypeArgumentBindingsWithSinglePsiElement(type) { argumentType ->
argumentType ->
NoTypeElementBinding(trace, psiElement, argumentType) NoTypeElementBinding(trace, psiElement, argumentType)
} }
} }
internal fun <P : PsiElement> createTypeArgumentBindingsWithSinglePsiElement( internal fun <P : PsiElement> createTypeArgumentBindingsWithSinglePsiElement(
type: KotlinType, type: KotlinType,
createBinding: (KotlinType) -> TypeBinding<P> createBinding: (KotlinType) -> TypeBinding<P>
) : List<TypeArgumentBinding<P>> { ): List<TypeArgumentBinding<P>> {
val isErrorBinding = type.isError || type.constructor.parameters.size != type.arguments.size val isErrorBinding = type.isError || type.constructor.parameters.size != type.arguments.size
return type.arguments.mapIndexed { index, typeProjection -> return type.arguments.mapIndexed { index, typeProjection ->
TypeArgumentBindingImpl( TypeArgumentBindingImpl(
typeProjection, typeProjection,
if (isErrorBinding) null else type.constructor.parameters[index], if (isErrorBinding) null else type.constructor.parameters[index],
createBinding(typeProjection.type) createBinding(typeProjection.type)
) )
} }
} }
@@ -21,11 +21,11 @@ import org.jetbrains.kotlin.psi.KtPropertyAccessor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
class VariableAsPropertyInfo( class VariableAsPropertyInfo(
val propertyGetter: KtPropertyAccessor?, val propertyGetter: KtPropertyAccessor?,
val propertySetter: KtPropertyAccessor?, val propertySetter: KtPropertyAccessor?,
val variableType: KotlinType?, val variableType: KotlinType?,
val hasBody: Boolean, val hasBody: Boolean,
val hasDelegate: Boolean val hasDelegate: Boolean
) { ) {
companion object { companion object {
fun createFromDestructuringDeclarationEntry(type: KotlinType): VariableAsPropertyInfo { fun createFromDestructuringDeclarationEntry(type: KotlinType): VariableAsPropertyInfo {
@@ -114,7 +114,7 @@ class VariableTypeAndInitializerResolver(
if (!variable.hasInitializer() || variable.isVar) return if (!variable.hasInitializer() || variable.isVar) return
variableDescriptor.setCompileTimeInitializer( variableDescriptor.setCompileTimeInitializer(
storageManager.createRecursionTolerantNullableLazyValue( storageManager.createRecursionTolerantNullableLazyValue(
computeInitializer@{ computeInitializer@ {
if (!DescriptorUtils.shouldRecordInitializerForProperty( if (!DescriptorUtils.shouldRecordInitializerForProperty(
variableDescriptor, variableDescriptor,
variableType variableType
@@ -57,19 +57,19 @@ import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
import java.util.* import java.util.*
class CallCompleter( class CallCompleter(
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val candidateResolver: CandidateResolver, private val candidateResolver: CandidateResolver,
private val dataFlowAnalyzer: DataFlowAnalyzer, private val dataFlowAnalyzer: DataFlowAnalyzer,
private val callCheckers: Iterable<CallChecker>, private val callCheckers: Iterable<CallChecker>,
private val builtIns: KotlinBuiltIns, private val builtIns: KotlinBuiltIns,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val effectSystem: EffectSystem private val effectSystem: EffectSystem
) { ) {
fun <D : CallableDescriptor> completeCall( fun <D : CallableDescriptor> completeCall(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
results: OverloadResolutionResultsImpl<D>, results: OverloadResolutionResultsImpl<D>,
tracing: TracingStrategy tracing: TracingStrategy
): OverloadResolutionResultsImpl<D> { ): OverloadResolutionResultsImpl<D> {
val resolvedCall = if (results.isSingleResult) results.resultingCall else null val resolvedCall = if (results.isSingleResult) results.resultingCall else null
@@ -87,8 +87,8 @@ class CallCompleter(
else else
resolvedCall.call.calleeExpression resolvedCall.call.calleeExpression
val reportOn = val reportOn =
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
else resolvedCall.call.callElement else resolvedCall.call.callElement
val callCheckerContext = CallCheckerContext(context, languageVersionSettings, deprecationResolver) val callCheckerContext = CallCheckerContext(context, languageVersionSettings, deprecationResolver)
for (callChecker in callCheckers) { for (callChecker in callCheckers) {
@@ -107,30 +107,34 @@ class CallCompleter(
} }
private fun <D : CallableDescriptor> completeAllCandidates( private fun <D : CallableDescriptor> completeAllCandidates(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
results: OverloadResolutionResultsImpl<D> results: OverloadResolutionResultsImpl<D>
) { ) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val candidates = (if (context.collectAllCandidates) { val candidates = (if (context.collectAllCandidates) {
results.allCandidates!! results.allCandidates!!
} } else {
else {
results.resultingCalls results.resultingCalls
}) as Collection<MutableResolvedCall<D>> }) as Collection<MutableResolvedCall<D>>
val temporaryBindingTrace = TemporaryBindingTrace.create(context.trace, "Trace to complete a candidate that is not a resulting call") val temporaryBindingTrace =
candidates.filterNot { resolvedCall -> resolvedCall.isCompleted }.forEach { TemporaryBindingTrace.create(context.trace, "Trace to complete a candidate that is not a resulting call")
resolvedCall -> candidates.filterNot { resolvedCall -> resolvedCall.isCompleted }.forEach { resolvedCall ->
completeResolvedCallAndArguments(resolvedCall, results, context.replaceBindingTrace(temporaryBindingTrace), TracingStrategy.EMPTY) completeResolvedCallAndArguments(
resolvedCall,
results,
context.replaceBindingTrace(temporaryBindingTrace),
TracingStrategy.EMPTY
)
} }
} }
private fun <D : CallableDescriptor> completeResolvedCallAndArguments( private fun <D : CallableDescriptor> completeResolvedCallAndArguments(
resolvedCall: MutableResolvedCall<D>?, resolvedCall: MutableResolvedCall<D>?,
results: OverloadResolutionResultsImpl<D>, results: OverloadResolutionResultsImpl<D>,
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
tracing: TracingStrategy tracing: TracingStrategy
) { ) {
if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) { if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) {
completeArguments(context, results) completeArguments(context, results)
@@ -149,24 +153,23 @@ class CallCompleter(
} }
private fun <D : CallableDescriptor> MutableResolvedCall<D>.completeConstraintSystem( private fun <D : CallableDescriptor> MutableResolvedCall<D>.completeConstraintSystem(
expectedType: KotlinType, expectedType: KotlinType,
trace: BindingTrace trace: BindingTrace
) { ) {
val returnType = candidateDescriptor.returnType val returnType = candidateDescriptor.returnType
val expectedReturnType = val expectedReturnType =
if (call.isCallableReference()) { if (call.isCallableReference()) {
// TODO: compute generic type argument for R in the kotlin.Function<R> supertype (KT-12963) // TODO: compute generic type argument for R in the kotlin.Function<R> supertype (KT-12963)
if (!TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) expectedType.getReturnTypeFromFunctionType() if (!TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) expectedType.getReturnTypeFromFunctionType()
else TypeUtils.NO_EXPECTED_TYPE else TypeUtils.NO_EXPECTED_TYPE
} } else expectedType
else expectedType
fun ConstraintSystem.Builder.typeInSystem(type: KotlinType?): KotlinType? = fun ConstraintSystem.Builder.typeInSystem(type: KotlinType?): KotlinType? =
type?.let { type?.let {
val substitutor = typeVariableSubstitutors[call.toHandle()] ?: error("No substitutor for call: $call") val substitutor = typeVariableSubstitutors[call.toHandle()] ?: error("No substitutor for call: $call")
substitutor.substitute(it, Variance.INVARIANT) substitutor.substitute(it, Variance.INVARIANT)
} }
fun updateSystemIfNeeded(buildSystemWithAdditionalConstraints: (ConstraintSystem.Builder) -> ConstraintSystem?) { fun updateSystemIfNeeded(buildSystemWithAdditionalConstraints: (ConstraintSystem.Builder) -> ConstraintSystem?) {
val system = buildSystemWithAdditionalConstraints(constraintSystem!!.toBuilder()) val system = buildSystemWithAdditionalConstraints(constraintSystem!!.toBuilder())
@@ -181,8 +184,7 @@ class CallCompleter(
if (returnTypeInSystem != null) { if (returnTypeInSystem != null) {
builder.addSubtypeConstraint(returnTypeInSystem, expectedReturnType, EXPECTED_TYPE_POSITION.position()) builder.addSubtypeConstraint(returnTypeInSystem, expectedReturnType, EXPECTED_TYPE_POSITION.position())
builder.build() builder.build()
} } else null
else null
} }
} }
@@ -206,17 +208,21 @@ class CallCompleter(
builder.addSubtypeConstraint(returnTypeInSystem, builtIns.unitType, EXPECTED_TYPE_POSITION.position()) builder.addSubtypeConstraint(returnTypeInSystem, builtIns.unitType, EXPECTED_TYPE_POSITION.position())
val system = builder.build() val system = builder.build()
if (system.status.isSuccessful()) system else null if (system.status.isSuccessful()) system else null
} } else null
else null
} }
} }
if (call.isCallableReference() && !TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) { if (call.isCallableReference() && !TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) {
updateSystemIfNeeded { builder -> updateSystemIfNeeded { builder ->
candidateDescriptor.valueParameters.zip(expectedType.getValueParameterTypesFromFunctionType()).forEach { (parameter, argument) -> candidateDescriptor.valueParameters.zip(expectedType.getValueParameterTypesFromFunctionType())
val valueParameterInSystem = builder.typeInSystem(parameter.type) .forEach { (parameter, argument) ->
builder.addSubtypeConstraint(valueParameterInSystem, argument.type, VALUE_PARAMETER_POSITION.position(parameter.index)) val valueParameterInSystem = builder.typeInSystem(parameter.type)
} builder.addSubtypeConstraint(
valueParameterInSystem,
argument.type,
VALUE_PARAMETER_POSITION.position(parameter.index)
)
}
builder.build() builder.build()
} }
@@ -231,8 +237,8 @@ class CallCompleter(
} }
private fun <D : CallableDescriptor> MutableResolvedCall<D>.updateResolutionStatusFromConstraintSystem( private fun <D : CallableDescriptor> MutableResolvedCall<D>.updateResolutionStatusFromConstraintSystem(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
tracing: TracingStrategy tracing: TracingStrategy
) { ) {
val contextWithResolvedCall = CallCandidateResolutionContext.createForCallBeingAnalyzed(this, context, tracing) val contextWithResolvedCall = CallCandidateResolutionContext.createForCallBeingAnalyzed(this, context, tracing)
val valueArgumentsCheckingResult = candidateResolver.checkAllValueArguments(contextWithResolvedCall, RESOLVE_FUNCTION_ARGUMENTS) val valueArgumentsCheckingResult = candidateResolver.checkAllValueArguments(contextWithResolvedCall, RESOLVE_FUNCTION_ARGUMENTS)
@@ -248,8 +254,8 @@ class CallCompleter(
val receiverType = extensionReceiver?.type val receiverType = extensionReceiver?.type
val errorData = InferenceErrorData.create( val errorData = InferenceErrorData.create(
candidateDescriptor, constraintSystem!!, valueArgumentsCheckingResult.argumentTypes, candidateDescriptor, constraintSystem!!, valueArgumentsCheckingResult.argumentTypes,
receiverType, context.expectedType, context.call receiverType, context.expectedType, context.call
) )
tracing.typeInferenceFailed(context, errorData) tracing.typeInferenceFailed(context, errorData)
@@ -257,8 +263,8 @@ class CallCompleter(
} }
private fun <D : CallableDescriptor> completeArguments( private fun <D : CallableDescriptor> completeArguments(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
results: OverloadResolutionResultsImpl<D> results: OverloadResolutionResultsImpl<D>
) { ) {
if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) return if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) return
@@ -268,8 +274,7 @@ class CallCompleter(
val resolvedCall = results.resultingCall val resolvedCall = results.resultingCall
getArgumentMapping = { argument -> resolvedCall.getArgumentMapping(argument) } getArgumentMapping = { argument -> resolvedCall.getArgumentMapping(argument) }
getDataFlowInfoForArgument = { argument -> resolvedCall.dataFlowInfoForArguments.getInfo(argument) } getDataFlowInfoForArgument = { argument -> resolvedCall.dataFlowInfoForArguments.getInfo(argument) }
} } else {
else {
getArgumentMapping = { ArgumentUnmapped } getArgumentMapping = { ArgumentUnmapped }
getDataFlowInfoForArgument = { context.dataFlowInfo } getDataFlowInfoForArgument = { context.dataFlowInfo }
} }
@@ -278,21 +283,22 @@ class CallCompleter(
val argumentMapping = getArgumentMapping(valueArgument!!) val argumentMapping = getArgumentMapping(valueArgument!!)
val (expectedType, callPosition) = when (argumentMapping) { val (expectedType, callPosition) = when (argumentMapping) {
is ArgumentMatch -> Pair( is ArgumentMatch -> Pair(
getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context), getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context),
CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)) CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)
)
else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown) else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown)
} }
val newContext = val newContext =
context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument)) context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument))
.replaceExpectedType(expectedType) .replaceExpectedType(expectedType)
.replaceCallPosition(callPosition) .replaceCallPosition(callPosition)
completeOneArgument(valueArgument, newContext) completeOneArgument(valueArgument, newContext)
} }
} }
private fun completeOneArgument( private fun completeOneArgument(
valueArgument: ValueArgument, valueArgument: ValueArgument,
context: BasicCallResolutionContext context: BasicCallResolutionContext
) { ) {
if (valueArgument.isExternal()) return if (valueArgument.isExternal()) return
@@ -307,8 +313,7 @@ class CallCompleter(
val resolvedCall = results.resultingCall val resolvedCall = results.resultingCall
updatedType = if (resolvedCall.hasInferredReturnType()) { updatedType = if (resolvedCall.hasInferredReturnType()) {
resolvedCall.makeNullableTypeIfSafeReceiver(resolvedCall.resultingDescriptor?.returnType, context) resolvedCall.makeNullableTypeIfSafeReceiver(resolvedCall.resultingDescriptor?.returnType, context)
} } else null
else null
} }
// For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.), // For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.),
@@ -336,24 +341,24 @@ class CallCompleter(
} }
private fun completeCallForArgument( private fun completeCallForArgument(
expression: KtExpression, expression: KtExpression,
context: BasicCallResolutionContext context: BasicCallResolutionContext
): OverloadResolutionResultsImpl<*>? { ): OverloadResolutionResultsImpl<*>? {
val cachedData = getResolutionResultsCachedData(expression, context) ?: return null val cachedData = getResolutionResultsCachedData(expression, context) ?: return null
val (cachedResolutionResults, cachedContext, tracing) = cachedData val (cachedResolutionResults, cachedContext, tracing) = cachedData
val contextForArgument = cachedContext.replaceBindingTrace(context.trace) val contextForArgument = cachedContext.replaceBindingTrace(context.trace)
.replaceExpectedType(context.expectedType).replaceCollectAllCandidates(false).replaceCallPosition(context.callPosition) .replaceExpectedType(context.expectedType).replaceCollectAllCandidates(false).replaceCallPosition(context.callPosition)
return completeCall(contextForArgument, cachedResolutionResults, tracing) return completeCall(contextForArgument, cachedResolutionResults, tracing)
} }
private fun updateRecordedTypeForArgument( private fun updateRecordedTypeForArgument(
updatedType: KotlinType?, updatedType: KotlinType?,
recordedType: KotlinType?, recordedType: KotlinType?,
argumentExpression: KtExpression, argumentExpression: KtExpression,
statementFilter: StatementFilter, statementFilter: StatementFilter,
trace: BindingTrace trace: BindingTrace
): KotlinType? { ): KotlinType? {
//workaround for KT-8218 //workaround for KT-8218
if ((!ErrorUtils.containsErrorType(recordedType) && recordedType == updatedType) || updatedType == null) return updatedType if ((!ErrorUtils.containsErrorType(recordedType) && recordedType == updatedType) || updatedType == null) return updatedType
@@ -67,13 +67,13 @@ import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
import javax.inject.Inject import javax.inject.Inject
class CallExpressionResolver( class CallExpressionResolver(
private val callResolver: CallResolver, private val callResolver: CallResolver,
private val constantExpressionEvaluator: ConstantExpressionEvaluator, private val constantExpressionEvaluator: ConstantExpressionEvaluator,
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val dataFlowAnalyzer: DataFlowAnalyzer, private val dataFlowAnalyzer: DataFlowAnalyzer,
private val builtIns: KotlinBuiltIns, private val builtIns: KotlinBuiltIns,
private val qualifiedExpressionResolver: QualifiedExpressionResolver, private val qualifiedExpressionResolver: QualifiedExpressionResolver,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
private lateinit var expressionTypingServices: ExpressionTypingServices private lateinit var expressionTypingServices: ExpressionTypingServices
@@ -84,13 +84,16 @@ class CallExpressionResolver(
} }
private fun getResolvedCallForFunction( private fun getResolvedCallForFunction(
call: Call, call: Call,
context: ResolutionContext<*>, context: ResolutionContext<*>,
checkArguments: CheckArgumentTypesMode, checkArguments: CheckArgumentTypesMode,
initialDataFlowInfoForArguments: DataFlowInfo initialDataFlowInfoForArguments: DataFlowInfo
): Pair<Boolean, ResolvedCall<FunctionDescriptor>?> { ): Pair<Boolean, ResolvedCall<FunctionDescriptor>?> {
val results = callResolver.resolveFunctionCall(BasicCallResolutionContext.create( val results = callResolver.resolveFunctionCall(
context, call, checkArguments, DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call))) BasicCallResolutionContext.create(
context, call, checkArguments, DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call)
)
)
return if (!results.isNothing) return if (!results.isNothing)
Pair(true, OverloadResolutionResultsUtil.getResultingCall(results, context)) Pair(true, OverloadResolutionResultsUtil.getResultingCall(results, context))
else else
@@ -98,23 +101,25 @@ class CallExpressionResolver(
} }
private fun getVariableType( private fun getVariableType(
nameExpression: KtSimpleNameExpression, receiver: Receiver?, nameExpression: KtSimpleNameExpression, receiver: Receiver?,
callOperationNode: ASTNode?, context: ExpressionTypingContext callOperationNode: ASTNode?, context: ExpressionTypingContext
): Pair<Boolean, KotlinType?> { ): Pair<Boolean, KotlinType?> {
val temporaryForVariable = TemporaryTraceAndCache.create( val temporaryForVariable = TemporaryTraceAndCache.create(
context, "trace to resolve as local variable or property", nameExpression) context, "trace to resolve as local variable or property", nameExpression
)
val call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression) val call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression)
val contextForVariable = BasicCallResolutionContext.create( val contextForVariable = BasicCallResolutionContext.create(
context.replaceTraceAndCache(temporaryForVariable), context.replaceTraceAndCache(temporaryForVariable),
call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS
)
val resolutionResult = callResolver.resolveSimpleProperty(contextForVariable) val resolutionResult = callResolver.resolveSimpleProperty(contextForVariable)
// if the expression is a receiver in a qualified expression, it should be resolved after the selector is resolved // if the expression is a receiver in a qualified expression, it should be resolved after the selector is resolved
val isLHSOfDot = KtPsiUtil.isLHSOfDot(nameExpression) val isLHSOfDot = KtPsiUtil.isLHSOfDot(nameExpression)
if (!resolutionResult.isNothing && resolutionResult.resultCode != CANDIDATES_WITH_WRONG_RECEIVER) { if (!resolutionResult.isNothing && resolutionResult.resultCode != CANDIDATES_WITH_WRONG_RECEIVER) {
val isQualifier = isLHSOfDot && val isQualifier = isLHSOfDot &&
resolutionResult.isSingleResult && resolutionResult.isSingleResult &&
resolutionResult.resultingDescriptor is FakeCallableDescriptorForObject resolutionResult.resultingDescriptor is FakeCallableDescriptorForObject
if (!isQualifier) { if (!isQualifier) {
temporaryForVariable.commit() temporaryForVariable.commit()
return Pair(true, if (resolutionResult.isSingleResult) resolutionResult.resultingDescriptor.returnType else null) return Pair(true, if (resolutionResult.isSingleResult) resolutionResult.resultingDescriptor.returnType else null)
@@ -122,25 +127,30 @@ class CallExpressionResolver(
} }
temporaryForVariable.commit() temporaryForVariable.commit()
return Pair(!resolutionResult.isNothing, return Pair(
if (resolutionResult.isSingleResult) resolutionResult.resultingDescriptor.returnType else null) !resolutionResult.isNothing,
if (resolutionResult.isSingleResult) resolutionResult.resultingDescriptor.returnType else null
)
} }
fun getSimpleNameExpressionTypeInfo( fun getSimpleNameExpressionTypeInfo(
nameExpression: KtSimpleNameExpression, receiver: Receiver?, nameExpression: KtSimpleNameExpression, receiver: Receiver?,
callOperationNode: ASTNode?, context: ExpressionTypingContext callOperationNode: ASTNode?, context: ExpressionTypingContext
) = getSimpleNameExpressionTypeInfo(nameExpression, receiver, callOperationNode, context, context.dataFlowInfo) ) = getSimpleNameExpressionTypeInfo(nameExpression, receiver, callOperationNode, context, context.dataFlowInfo)
private fun getSimpleNameExpressionTypeInfo( private fun getSimpleNameExpressionTypeInfo(
nameExpression: KtSimpleNameExpression, receiver: Receiver?, nameExpression: KtSimpleNameExpression, receiver: Receiver?,
callOperationNode: ASTNode?, context: ExpressionTypingContext, callOperationNode: ASTNode?, context: ExpressionTypingContext,
initialDataFlowInfoForArguments: DataFlowInfo initialDataFlowInfoForArguments: DataFlowInfo
): KotlinTypeInfo { ): KotlinTypeInfo {
val temporaryForVariable = TemporaryTraceAndCache.create( val temporaryForVariable = TemporaryTraceAndCache.create(
context, "trace to resolve as variable", nameExpression) context, "trace to resolve as variable", nameExpression
val (notNothing, type) = getVariableType(nameExpression, receiver, callOperationNode, )
context.replaceTraceAndCache(temporaryForVariable)) val (notNothing, type) = getVariableType(
nameExpression, receiver, callOperationNode,
context.replaceTraceAndCache(temporaryForVariable)
)
if (notNothing) { if (notNothing) {
temporaryForVariable.commit() temporaryForVariable.commit()
@@ -149,10 +159,12 @@ class CallExpressionResolver(
val call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, emptyList()) val call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, emptyList())
val temporaryForFunction = TemporaryTraceAndCache.create( val temporaryForFunction = TemporaryTraceAndCache.create(
context, "trace to resolve as function", nameExpression) context, "trace to resolve as function", nameExpression
)
val newContext = context.replaceTraceAndCache(temporaryForFunction) val newContext = context.replaceTraceAndCache(temporaryForFunction)
val (resolveResult, resolvedCall) = getResolvedCallForFunction( val (resolveResult, resolvedCall) = getResolvedCallForFunction(
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, initialDataFlowInfoForArguments) call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, initialDataFlowInfoForArguments
)
if (resolveResult) { if (resolveResult) {
val functionDescriptor = resolvedCall?.resultingDescriptor val functionDescriptor = resolvedCall?.resultingDescriptor
if (functionDescriptor !is ConstructorDescriptor) { if (functionDescriptor !is ConstructorDescriptor) {
@@ -173,11 +185,12 @@ class CallExpressionResolver(
} }
fun getCallExpressionTypeInfo( fun getCallExpressionTypeInfo(
callExpression: KtCallExpression, receiver: ReceiverValue?, callExpression: KtCallExpression, receiver: ReceiverValue?,
callOperationNode: ASTNode?, context: ExpressionTypingContext callOperationNode: ASTNode?, context: ExpressionTypingContext
): KotlinTypeInfo { ): KotlinTypeInfo {
val typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck( val typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(
callExpression, receiver, callOperationNode, context, context.dataFlowInfo) callExpression, receiver, callOperationNode, context, context.dataFlowInfo
)
if (context.contextDependency == INDEPENDENT) { if (context.contextDependency == INDEPENDENT) {
dataFlowAnalyzer.checkType(typeInfo.type, callExpression, context) dataFlowAnalyzer.checkType(typeInfo.type, callExpression, context)
} }
@@ -189,19 +202,21 @@ class CallExpressionResolver(
* Determines the result type and data flow information after the call. * Determines the result type and data flow information after the call.
*/ */
private fun getCallExpressionTypeInfoWithoutFinalTypeCheck( private fun getCallExpressionTypeInfoWithoutFinalTypeCheck(
callExpression: KtCallExpression, receiver: Receiver?, callExpression: KtCallExpression, receiver: Receiver?,
callOperationNode: ASTNode?, context: ExpressionTypingContext, callOperationNode: ASTNode?, context: ExpressionTypingContext,
initialDataFlowInfoForArguments: DataFlowInfo initialDataFlowInfoForArguments: DataFlowInfo
): KotlinTypeInfo { ): KotlinTypeInfo {
val call = CallMaker.makeCall(receiver, callOperationNode, callExpression) val call = CallMaker.makeCall(receiver, callOperationNode, callExpression)
val temporaryForFunction = TemporaryTraceAndCache.create( val temporaryForFunction = TemporaryTraceAndCache.create(
context, "trace to resolve as function call", callExpression) context, "trace to resolve as function call", callExpression
)
val (resolveResult, resolvedCall) = getResolvedCallForFunction( val (resolveResult, resolvedCall) = getResolvedCallForFunction(
call, call,
context.replaceTraceAndCache(temporaryForFunction), context.replaceTraceAndCache(temporaryForFunction),
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
initialDataFlowInfoForArguments) initialDataFlowInfoForArguments
)
if (resolveResult) { if (resolveResult) {
val functionDescriptor = resolvedCall?.resultingDescriptor val functionDescriptor = resolvedCall?.resultingDescriptor
temporaryForFunction.commit() temporaryForFunction.commit()
@@ -246,9 +261,12 @@ class CallExpressionResolver(
val calleeExpression = callExpression.calleeExpression val calleeExpression = callExpression.calleeExpression
if (calleeExpression is KtSimpleNameExpression && callExpression.typeArgumentList == null) { if (calleeExpression is KtSimpleNameExpression && callExpression.typeArgumentList == null) {
val temporaryForVariable = TemporaryTraceAndCache.create( val temporaryForVariable = TemporaryTraceAndCache.create(
context, "trace to resolve as variable with 'invoke' call", callExpression) context, "trace to resolve as variable with 'invoke' call", callExpression
val (notNothing, type) = getVariableType(calleeExpression, receiver, callOperationNode, )
context.replaceTraceAndCache(temporaryForVariable)) val (notNothing, type) = getVariableType(
calleeExpression, receiver, callOperationNode,
context.replaceTraceAndCache(temporaryForVariable)
)
val qualifier = temporaryForVariable.trace.get(BindingContext.QUALIFIER, calleeExpression) val qualifier = temporaryForVariable.trace.get(BindingContext.QUALIFIER, calleeExpression)
if (notNothing && (qualifier == null || qualifier !is PackageQualifier)) { if (notNothing && (qualifier == null || qualifier !is PackageQualifier)) {
@@ -258,14 +276,18 @@ class CallExpressionResolver(
} }
temporaryForVariable.commit() temporaryForVariable.commit()
context.trace.report(FUNCTION_EXPECTED.on(calleeExpression, calleeExpression, context.trace.report(
type ?: ErrorUtils.createErrorType(""))) FUNCTION_EXPECTED.on(
calleeExpression, calleeExpression,
type ?: ErrorUtils.createErrorType("")
)
)
argumentTypeResolver.analyzeArgumentsAndRecordTypes( argumentTypeResolver.analyzeArgumentsAndRecordTypes(
BasicCallResolutionContext.create( BasicCallResolutionContext.create(
context, call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, context, call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call) DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call)
), ),
ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
) )
return noTypeInfo(context) return noTypeInfo(context)
} }
@@ -275,41 +297,41 @@ class CallExpressionResolver(
} }
private fun KtQualifiedExpression.elementChain(context: ExpressionTypingContext) = private fun KtQualifiedExpression.elementChain(context: ExpressionTypingContext) =
qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(this, context) { qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(this, context) { nameExpression ->
nameExpression -> val resolutionResult = resolveSimpleName(context, nameExpression)
val resolutionResult = resolveSimpleName(context, nameExpression)
if (resolutionResult.isSingleResult && resolutionResult.resultingDescriptor is FakeCallableDescriptorForObject) { if (resolutionResult.isSingleResult && resolutionResult.resultingDescriptor is FakeCallableDescriptorForObject) {
false false
} } else when (resolutionResult.resultCode) {
else when (resolutionResult.resultCode) { NAME_NOT_FOUND, CANDIDATES_WITH_WRONG_RECEIVER -> false
NAME_NOT_FOUND, CANDIDATES_WITH_WRONG_RECEIVER -> false else -> !context.languageVersionSettings.supportsFeature(LanguageFeature.NewInference) || resolutionResult.isSuccess
else -> !context.languageVersionSettings.supportsFeature(LanguageFeature.NewInference) || resolutionResult.isSuccess
}
} }
}
private fun resolveSimpleName( private fun resolveSimpleName(
context: ExpressionTypingContext, expression: KtSimpleNameExpression context: ExpressionTypingContext, expression: KtSimpleNameExpression
): OverloadResolutionResults<VariableDescriptor> { ): OverloadResolutionResults<VariableDescriptor> {
val temporaryForVariable = TemporaryTraceAndCache.create(context, "trace to resolve as local variable or property", expression) val temporaryForVariable = TemporaryTraceAndCache.create(context, "trace to resolve as local variable or property", expression)
val call = CallMaker.makePropertyCall(null, null, expression) val call = CallMaker.makePropertyCall(null, null, expression)
val contextForVariable = BasicCallResolutionContext.create( val contextForVariable = BasicCallResolutionContext.create(
context.replaceTraceAndCache(temporaryForVariable), call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS context.replaceTraceAndCache(temporaryForVariable), call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS
) )
return callResolver.resolveSimpleProperty(contextForVariable) return callResolver.resolveSimpleProperty(contextForVariable)
} }
private fun getUnsafeSelectorTypeInfo( private fun getUnsafeSelectorTypeInfo(
receiver: Receiver, receiver: Receiver,
callOperationNode: ASTNode?, callOperationNode: ASTNode?,
selectorExpression: KtExpression?, selectorExpression: KtExpression?,
context: ExpressionTypingContext, context: ExpressionTypingContext,
initialDataFlowInfoForArguments: DataFlowInfo initialDataFlowInfoForArguments: DataFlowInfo
): KotlinTypeInfo = when (selectorExpression) { ): KotlinTypeInfo = when (selectorExpression) {
is KtCallExpression -> getCallExpressionTypeInfoWithoutFinalTypeCheck( is KtCallExpression -> getCallExpressionTypeInfoWithoutFinalTypeCheck(
selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments) selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments
)
is KtSimpleNameExpression -> getSimpleNameExpressionTypeInfo( is KtSimpleNameExpression -> getSimpleNameExpressionTypeInfo(
selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments) selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments
)
is KtExpression -> { is KtExpression -> {
expressionTypingServices.getTypeInfo(selectorExpression, context) expressionTypingServices.getTypeInfo(selectorExpression, context)
context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression)) context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression))
@@ -323,14 +345,14 @@ class CallExpressionResolver(
var initialDataFlowInfoForArguments = context.dataFlowInfo var initialDataFlowInfoForArguments = context.dataFlowInfo
val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) } val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) }
val receiverCanBeNull = receiverDataFlowValue != null && val receiverCanBeNull = receiverDataFlowValue != null &&
initialDataFlowInfoForArguments.getStableNullability(receiverDataFlowValue).canBeNull() initialDataFlowInfoForArguments.getStableNullability(receiverDataFlowValue).canBeNull()
if (receiverDataFlowValue != null && element.safe) { if (receiverDataFlowValue != null && element.safe) {
// Additional "receiver != null" information should be applied if we consider a safe call // Additional "receiver != null" information should be applied if we consider a safe call
if (receiverCanBeNull) { if (receiverCanBeNull) {
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate( initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings) receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings
} )
else if (receiver is ReceiverValue) { } else if (receiver is ReceiverValue) {
reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver) reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver)
} }
} }
@@ -361,8 +383,7 @@ class CallExpressionResolver(
val value = constantExpressionEvaluator.evaluateExpression(qualified, context.trace, context.expectedType) val value = constantExpressionEvaluator.evaluateExpression(qualified, context.trace, context.expectedType)
return if (value != null && value.isPure) { return if (value != null && value.isPure) {
dataFlowAnalyzer.createCompileTimeConstantTypeInfo(value, qualified, context) dataFlowAnalyzer.createCompileTimeConstantTypeInfo(value, qualified, context)
} } else {
else {
if (context.contextDependency == INDEPENDENT) { if (context.contextDependency == INDEPENDENT) {
dataFlowAnalyzer.checkType(selectorTypeInfo.type, qualified, context) dataFlowAnalyzer.checkType(selectorTypeInfo.type, qualified, context)
} }
@@ -409,20 +430,19 @@ class CallExpressionResolver(
val receiverType = receiverTypeInfo.type ?: ErrorUtils.createErrorType("Type for " + element.receiver.text) val receiverType = receiverTypeInfo.type ?: ErrorUtils.createErrorType("Type for " + element.receiver.text)
val receiver = trace.get(BindingContext.QUALIFIER, element.receiver) val receiver = trace.get(BindingContext.QUALIFIER, element.receiver)
?: ExpressionReceiver.create(element.receiver, receiverType, trace.bindingContext) ?: ExpressionReceiver.create(element.receiver, receiverType, trace.bindingContext)
val qualifiedExpression = element.qualified val qualifiedExpression = element.qualified
val lastStage = qualifiedExpression === expression val lastStage = qualifiedExpression === expression
// Drop NO_EXPECTED_TYPE / INDEPENDENT at last stage // Drop NO_EXPECTED_TYPE / INDEPENDENT at last stage
val contextForSelector = (if (lastStage) context else currentContext).replaceDataFlowInfo( val contextForSelector = (if (lastStage) context else currentContext).replaceDataFlowInfo(
if (receiver is ReceiverValue && TypeUtils.isNullableType(receiver.type) && !element.safe) { if (receiver is ReceiverValue && TypeUtils.isNullableType(receiver.type) && !element.safe) {
// Call with nullable receiver: take data flow info from branch point // Call with nullable receiver: take data flow info from branch point
branchPointDataFlowInfo branchPointDataFlowInfo
} } else {
else { // Take data flow info from the current receiver
// Take data flow info from the current receiver receiverTypeInfo.dataFlowInfo
receiverTypeInfo.dataFlowInfo }
}
) )
val selectorTypeInfo = getSafeOrUnsafeSelectorTypeInfo(receiver, element, contextForSelector) val selectorTypeInfo = getSafeOrUnsafeSelectorTypeInfo(receiver, element, contextForSelector)
@@ -432,8 +452,9 @@ class CallExpressionResolver(
branchPointDataFlowInfo = selectorTypeInfo.dataFlowInfo branchPointDataFlowInfo = selectorTypeInfo.dataFlowInfo
} }
resultTypeInfo = checkSelectorTypeInfo(qualifiedExpression, selectorTypeInfo, contextForSelector). resultTypeInfo = checkSelectorTypeInfo(qualifiedExpression, selectorTypeInfo, contextForSelector).replaceDataFlowInfo(
replaceDataFlowInfo(branchPointDataFlowInfo) branchPointDataFlowInfo
)
if (!lastStage) { if (!lastStage) {
recordResultTypeInfo(qualifiedExpression, resultTypeInfo, contextForSelector) recordResultTypeInfo(qualifiedExpression, resultTypeInfo, contextForSelector)
} }
@@ -444,9 +465,9 @@ class CallExpressionResolver(
} }
private fun resolveDeferredReceiverInQualifiedExpression( private fun resolveDeferredReceiverInQualifiedExpression(
qualifier: Qualifier, qualifier: Qualifier,
selectorExpression: KtExpression?, selectorExpression: KtExpression?,
context: ExpressionTypingContext context: ExpressionTypingContext
) { ) {
val calleeExpression = KtPsiUtil.deparenthesize(selectorExpression.getCalleeExpressionIfAny()) val calleeExpression = KtPsiUtil.deparenthesize(selectorExpression.getCalleeExpressionIfAny())
val selectorDescriptor = (calleeExpression as? KtReferenceExpression)?.let { val selectorDescriptor = (calleeExpression as? KtReferenceExpression)?.let {
@@ -469,33 +490,31 @@ class CallExpressionResolver(
if (parent != null) { if (parent != null) {
return isUnderAnnotationClassDeclaration(trace, parent) return isUnderAnnotationClassDeclaration(trace, parent)
} }
} } else if (parent is KtParameter) {
else if (parent is KtParameter) {
return isUnderAnnotationClassDeclaration(trace, parent) return isUnderAnnotationClassDeclaration(trace, parent)
} }
return false return false
} }
private fun isUnderAnnotationClassDeclaration(trace: BindingTrace, parent: PsiElement) = private fun isUnderAnnotationClassDeclaration(trace: BindingTrace, parent: PsiElement) =
parent.getParentOfType<KtClass>(true)?.let { parent.getParentOfType<KtClass>(true)?.let {
DescriptorUtils.isAnnotationClass(trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, it)) DescriptorUtils.isAnnotationClass(trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, it))
} ?: false } ?: false
fun reportUnnecessarySafeCall( fun reportUnnecessarySafeCall(
trace: BindingTrace, type: KotlinType, trace: BindingTrace, type: KotlinType,
callOperationNode: ASTNode, explicitReceiver: Receiver? callOperationNode: ASTNode, explicitReceiver: Receiver?
) = trace.report( ) = trace.report(
if (explicitReceiver is ExpressionReceiver && explicitReceiver.expression is KtSuperExpression) { if (explicitReceiver is ExpressionReceiver && explicitReceiver.expression is KtSuperExpression) {
UNEXPECTED_SAFE_CALL.on(callOperationNode.psi) UNEXPECTED_SAFE_CALL.on(callOperationNode.psi)
} } else {
else {
UNNECESSARY_SAFE_CALL.on(callOperationNode.psi, type) UNNECESSARY_SAFE_CALL.on(callOperationNode.psi, type)
} }
) )
private fun checkNestedClassAccess( private fun checkNestedClassAccess(
expression: KtQualifiedExpression, expression: KtQualifiedExpression,
context: ExpressionTypingContext context: ExpressionTypingContext
) { ) {
val selectorExpression = expression.selectorExpression ?: return val selectorExpression = expression.selectorExpression ?: return
@@ -80,13 +80,13 @@ fun replaceReturnTypeForCallable(type: KotlinType, given: KotlinType): KotlinTyp
fun replaceReturnTypeByUnknown(type: KotlinType) = replaceReturnTypeForCallable(type, DONT_CARE) fun replaceReturnTypeByUnknown(type: KotlinType) = replaceReturnTypeForCallable(type, DONT_CARE)
private fun replaceTypeArguments(type: KotlinType, newArguments: List<TypeProjection>) = private fun replaceTypeArguments(type: KotlinType, newArguments: List<TypeProjection>) =
KotlinTypeFactory.simpleType(type.annotations, type.constructor, newArguments, type.isMarkedNullable) KotlinTypeFactory.simpleType(type.annotations, type.constructor, newArguments, type.isMarkedNullable)
private fun getParameterArgumentsOfCallableType(type: KotlinType) = private fun getParameterArgumentsOfCallableType(type: KotlinType) =
type.arguments.dropLast(1) type.arguments.dropLast(1)
fun getReturnTypeForCallable(type: KotlinType) = fun getReturnTypeForCallable(type: KotlinType) =
type.arguments.last().type type.arguments.last().type
private fun CallableDescriptor.hasReturnTypeDependentOnUninferredParams(constraintSystem: ConstraintSystem): Boolean { private fun CallableDescriptor.hasReturnTypeDependentOnUninferredParams(constraintSystem: ConstraintSystem): Boolean {
val returnType = returnType ?: return false val returnType = returnType ?: return false
@@ -125,8 +125,10 @@ fun getErasedReceiverType(receiverParameterDescriptor: ReceiverParameterDescript
receiverType.constructor receiverType.constructor
} }
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(receiverType.annotations, receiverTypeConstructor, fakeTypeArguments, return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
receiverType.isMarkedNullable, ErrorUtils.createErrorScope("Error scope for erased receiver type", /*throwExceptions=*/true)) receiverType.annotations, receiverTypeConstructor, fakeTypeArguments,
receiverType.isMarkedNullable, ErrorUtils.createErrorScope("Error scope for erased receiver type", /*throwExceptions=*/true)
)
} }
fun isOrOverridesSynthesized(descriptor: CallableMemberDescriptor): Boolean { fun isOrOverridesSynthesized(descriptor: CallableMemberDescriptor): Boolean {
@@ -163,7 +165,7 @@ fun isInfixCall(call: Call): Boolean {
} }
fun isSuperOrDelegatingConstructorCall(call: Call): Boolean = fun isSuperOrDelegatingConstructorCall(call: Call): Boolean =
call.calleeExpression.let { it is KtConstructorCalleeExpression || it is KtConstructorDelegationReferenceExpression } call.calleeExpression.let { it is KtConstructorCalleeExpression || it is KtConstructorDelegationReferenceExpression }
fun isInvokeCallOnVariable(call: Call): Boolean { fun isInvokeCallOnVariable(call: Call): Boolean {
if (call.callType !== Call.CallType.INVOKE) return false if (call.callType !== Call.CallType.INVOKE) return false
@@ -183,9 +185,9 @@ fun getSuperCallExpression(call: Call): KtSuperExpression? {
} }
fun getEffectiveExpectedType( fun getEffectiveExpectedType(
parameterDescriptor: ValueParameterDescriptor, parameterDescriptor: ValueParameterDescriptor,
argument: ValueArgument, argument: ValueArgument,
context: ResolutionContext<*> context: ResolutionContext<*>
): KotlinType { ): KotlinType {
if (argument.getSpreadElement() != null || shouldCheckAsArray(parameterDescriptor, argument, context)) { if (argument.getSpreadElement() != null || shouldCheckAsArray(parameterDescriptor, argument, context)) {
if (parameterDescriptor.varargElementType == null) { if (parameterDescriptor.varargElementType == null) {
@@ -203,9 +205,9 @@ fun getEffectiveExpectedType(
} }
private fun shouldCheckAsArray( private fun shouldCheckAsArray(
parameterDescriptor: ValueParameterDescriptor, parameterDescriptor: ValueParameterDescriptor,
argument: ValueArgument, argument: ValueArgument,
context: ResolutionContext<*> context: ResolutionContext<*>
): Boolean { ): Boolean {
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.AssigningArraysToVarargsInNamedFormInAnnotations)) return false if (!context.languageVersionSettings.supportsFeature(LanguageFeature.AssigningArraysToVarargsInNamedFormInAnnotations)) return false
@@ -223,29 +225,30 @@ fun isArrayOrArrayLiteral(argument: ValueArgument, context: ResolutionContext<*>
} }
fun createResolutionCandidatesForConstructors( fun createResolutionCandidatesForConstructors(
lexicalScope: LexicalScope, lexicalScope: LexicalScope,
call: Call, call: Call,
typeWithConstructors: KotlinType, typeWithConstructors: KotlinType,
useKnownTypeSubstitutor: Boolean, useKnownTypeSubstitutor: Boolean,
syntheticScopes: SyntheticScopes syntheticScopes: SyntheticScopes
): List<ResolutionCandidate<ConstructorDescriptor>> { ): List<ResolutionCandidate<ConstructorDescriptor>> {
val classWithConstructors = typeWithConstructors.constructor.declarationDescriptor as ClassDescriptor val classWithConstructors = typeWithConstructors.constructor.declarationDescriptor as ClassDescriptor
val unwrappedType = typeWithConstructors.unwrap() val unwrappedType = typeWithConstructors.unwrap()
val knownSubstitutor = val knownSubstitutor =
if (useKnownTypeSubstitutor) if (useKnownTypeSubstitutor)
TypeSubstitutor.create( TypeSubstitutor.create(
(unwrappedType as? AbbreviatedType)?.abbreviation ?: unwrappedType (unwrappedType as? AbbreviatedType)?.abbreviation ?: unwrappedType
) )
else null else null
val typeAliasDescriptor = val typeAliasDescriptor =
if (unwrappedType is AbbreviatedType) if (unwrappedType is AbbreviatedType)
unwrappedType.abbreviation.constructor.declarationDescriptor as? TypeAliasDescriptor unwrappedType.abbreviation.constructor.declarationDescriptor as? TypeAliasDescriptor
else else
null null
val constructors = typeAliasDescriptor?.constructors?.mapNotNull(TypeAliasConstructorDescriptor::withDispatchReceiver) ?: classWithConstructors.constructors val constructors = typeAliasDescriptor?.constructors?.mapNotNull(TypeAliasConstructorDescriptor::withDispatchReceiver)
?: classWithConstructors.constructors
if (constructors.isEmpty()) return emptyList() if (constructors.isEmpty()) return emptyList()
@@ -262,8 +265,7 @@ fun createResolutionCandidatesForConstructors(
receiverKind = ExplicitReceiverKind.DISPATCH_RECEIVER receiverKind = ExplicitReceiverKind.DISPATCH_RECEIVER
dispatchReceiver = receiver.value dispatchReceiver = receiver.value
} } else {
else {
receiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER receiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER
dispatchReceiver = null dispatchReceiver = null
} }
@@ -276,12 +278,12 @@ fun createResolutionCandidatesForConstructors(
} }
fun KtLambdaExpression.getCorrespondingParameterForFunctionArgument( fun KtLambdaExpression.getCorrespondingParameterForFunctionArgument(
bindingContext: BindingContext bindingContext: BindingContext
): ValueParameterDescriptor? { ): ValueParameterDescriptor? {
val resolvedCall = KtPsiUtil.getParentCallIfPresent(this)?.getResolvedCall(bindingContext) ?: return null val resolvedCall = KtPsiUtil.getParentCallIfPresent(this)?.getResolvedCall(bindingContext) ?: return null
val valueArgument = val valueArgument =
resolvedCall.call.getValueArgumentForExpression(this) resolvedCall.call.getValueArgumentForExpression(this)
?: return null ?: return null
val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return null val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return null
return mapping.valueParameter return mapping.valueParameter
@@ -57,15 +57,15 @@ import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.* import java.util.*
class CandidateResolver( class CandidateResolver(
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val genericCandidateResolver: GenericCandidateResolver, private val genericCandidateResolver: GenericCandidateResolver,
private val reflectionTypes: ReflectionTypes, private val reflectionTypes: ReflectionTypes,
private val additionalTypeCheckers: Iterable<AdditionalTypeChecker>, private val additionalTypeCheckers: Iterable<AdditionalTypeChecker>,
private val smartCastManager: SmartCastManager private val smartCastManager: SmartCastManager
) { ) {
fun <D : CallableDescriptor> performResolutionForCandidateCall( fun <D : CallableDescriptor> performResolutionForCandidateCall(
context: CallCandidateResolutionContext<D>, context: CallCandidateResolutionContext<D>,
checkArguments: CheckArgumentTypesMode checkArguments: CheckArgumentTypesMode
): Unit = with(context) { ): Unit = with(context) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
@@ -107,8 +107,7 @@ class CandidateResolver(
&& candidateCall.knownTypeParametersSubstitutor == null && candidateCall.knownTypeParametersSubstitutor == null
) { ) {
genericCandidateResolver.inferTypeArguments(this) genericCandidateResolver.inferTypeArguments(this)
} } else {
else {
checkAllValueArguments(this, SHAPE_FUNCTION_ARGUMENTS).status checkAllValueArguments(this, SHAPE_FUNCTION_ARGUMENTS).status
} }
} }
@@ -117,8 +116,7 @@ class CandidateResolver(
val ktTypeArguments = call.typeArguments val ktTypeArguments = call.typeArguments
if (candidateCall.knownTypeParametersSubstitutor != null) { if (candidateCall.knownTypeParametersSubstitutor != null) {
candidateCall.setResultingSubstitutor(candidateCall.knownTypeParametersSubstitutor!!) candidateCall.setResultingSubstitutor(candidateCall.knownTypeParametersSubstitutor!!)
} } else if (ktTypeArguments.isNotEmpty()) {
else if (ktTypeArguments.isNotEmpty()) {
// Explicit type arguments passed // Explicit type arguments passed
val typeArguments = ArrayList<KotlinType>() val typeArguments = ArrayList<KotlinType>()
@@ -130,9 +128,11 @@ class CandidateResolver(
val expectedTypeArgumentCount = candidateDescriptor.typeParameters.size val expectedTypeArgumentCount = candidateDescriptor.typeParameters.size
for (index in ktTypeArguments.size..expectedTypeArgumentCount - 1) { for (index in ktTypeArguments.size..expectedTypeArgumentCount - 1) {
typeArguments.add(ErrorUtils.createErrorType( typeArguments.add(
ErrorUtils.createErrorType(
"Explicit type argument expected for " + candidateDescriptor.typeParameters[index].name "Explicit type argument expected for " + candidateDescriptor.typeParameters[index].name
)) )
)
} }
val substitution = FunctionDescriptorUtil.createSubstitution(candidateDescriptor as FunctionDescriptor, typeArguments) val substitution = FunctionDescriptorUtil.createSubstitution(candidateDescriptor as FunctionDescriptor, typeArguments)
val substitutor = TypeSubstitutor.create(SubstitutionFilteringInternalResolveAnnotations(substitution)) val substitutor = TypeSubstitutor.create(SubstitutionFilteringInternalResolveAnnotations(substitution))
@@ -140,8 +140,7 @@ class CandidateResolver(
if (expectedTypeArgumentCount != ktTypeArguments.size) { if (expectedTypeArgumentCount != ktTypeArguments.size) {
candidateCall.addStatus(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR) candidateCall.addStatus(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR)
tracing.wrongNumberOfTypeArguments(trace, expectedTypeArgumentCount, candidateDescriptor) tracing.wrongNumberOfTypeArguments(trace, expectedTypeArgumentCount, candidateDescriptor)
} } else {
else {
checkGenericBoundsInAFunctionCall(ktTypeArguments, typeArguments, candidateDescriptor, substitutor, trace) checkGenericBoundsInAFunctionCall(ktTypeArguments, typeArguments, candidateDescriptor, substitutor, trace)
} }
@@ -149,31 +148,30 @@ class CandidateResolver(
} }
} }
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.mapArguments() private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.mapArguments() = check {
= check { val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters( call, tracing, candidateCall
call, tracing, candidateCall) )
if (!argumentMappingStatus.isSuccess) { if (!argumentMappingStatus.isSuccess) {
candidateCall.addStatus(ARGUMENTS_MAPPING_ERROR) candidateCall.addStatus(ARGUMENTS_MAPPING_ERROR)
} }
} }
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkExpectedCallableType() private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkExpectedCallableType() = check {
= check { if (!noExpectedType(expectedType)) {
if (!noExpectedType(expectedType)) { val candidateKCallableType = DoubleColonExpressionResolver.createKCallableTypeForReference(
val candidateKCallableType = DoubleColonExpressionResolver.createKCallableTypeForReference( candidateCall.candidateDescriptor,
candidateCall.candidateDescriptor, (call.callElement.parent as? KtCallableReferenceExpression)?.receiverExpression?.let {
(call.callElement.parent as? KtCallableReferenceExpression)?.receiverExpression?.let { trace.bindingContext.get(BindingContext.DOUBLE_COLON_LHS, it)
trace.bindingContext.get(BindingContext.DOUBLE_COLON_LHS, it) },
}, reflectionTypes, scope.ownerDescriptor
reflectionTypes, scope.ownerDescriptor )
) if (candidateKCallableType == null ||
if (candidateKCallableType == null || !canBeSubtype(candidateKCallableType, expectedType, candidateCall.candidateDescriptor.typeParameters)) {
!canBeSubtype(candidateKCallableType, expectedType, candidateCall.candidateDescriptor.typeParameters)) { candidateCall.addStatus(OTHER_ERROR)
candidateCall.addStatus(OTHER_ERROR)
}
}
} }
}
}
private fun canBeSubtype(subType: KotlinType, superType: KotlinType, candidateTypeParameters: List<TypeParameterDescriptor>): Boolean { private fun canBeSubtype(subType: KotlinType, superType: KotlinType, candidateTypeParameters: List<TypeParameterDescriptor>): Boolean {
// Here we need to check that there exists a substitution from type parameters (used in types in candidate signature) // Here we need to check that there exists a substitution from type parameters (used in types in candidate signature)
@@ -191,11 +189,12 @@ class CandidateResolver(
} }
private fun CallCandidateResolutionContext<*>.checkVisibilityWithDispatchReceiver( private fun CallCandidateResolutionContext<*>.checkVisibilityWithDispatchReceiver(
receiverArgument: ReceiverValue?, receiverArgument: ReceiverValue?,
smartCastType: KotlinType? smartCastType: KotlinType?
): ResolutionStatus { ): ResolutionStatus {
val invisibleMember = Visibilities.findInvisibleMember( val invisibleMember = Visibilities.findInvisibleMember(
getReceiverValueWithSmartCast(receiverArgument, smartCastType), candidateDescriptor, scope.ownerDescriptor) getReceiverValueWithSmartCast(receiverArgument, smartCastType), candidateDescriptor, scope.ownerDescriptor
)
return if (invisibleMember != null) { return if (invisibleMember != null) {
tracing.invisibleMember(trace, invisibleMember) tracing.invisibleMember(trace, invisibleMember)
INVISIBLE_MEMBER_ERROR INVISIBLE_MEMBER_ERROR
@@ -205,17 +204,17 @@ class CandidateResolver(
} }
private fun CallCandidateResolutionContext<*>.isCandidateVisibleOrExtensionReceiver( private fun CallCandidateResolutionContext<*>.isCandidateVisibleOrExtensionReceiver(
receiverArgument: ReceiverValue?, receiverArgument: ReceiverValue?,
smartCastType: KotlinType?, smartCastType: KotlinType?,
isDispatchReceiver: Boolean isDispatchReceiver: Boolean
) = !isDispatchReceiver || isCandidateVisible(receiverArgument, smartCastType) ) = !isDispatchReceiver || isCandidateVisible(receiverArgument, smartCastType)
private fun CallCandidateResolutionContext<*>.isCandidateVisible( private fun CallCandidateResolutionContext<*>.isCandidateVisible(
receiverArgument: ReceiverValue?, receiverArgument: ReceiverValue?,
smartCastType: KotlinType? smartCastType: KotlinType?
) = Visibilities.findInvisibleMember( ) = Visibilities.findInvisibleMember(
getReceiverValueWithSmartCast(receiverArgument, smartCastType), getReceiverValueWithSmartCast(receiverArgument, smartCastType),
candidateDescriptor, scope.ownerDescriptor candidateDescriptor, scope.ownerDescriptor
) == null ) == null
private fun CallCandidateResolutionContext<*>.checkExtensionReceiver() = checkAndReport { private fun CallCandidateResolutionContext<*>.checkExtensionReceiver() = checkAndReport {
@@ -224,17 +223,14 @@ class CandidateResolver(
if (receiverParameter != null && receiverArgument == null) { if (receiverParameter != null && receiverArgument == null) {
tracing.missingReceiver(candidateCall.trace, receiverParameter) tracing.missingReceiver(candidateCall.trace, receiverParameter)
OTHER_ERROR OTHER_ERROR
} } else if (receiverParameter == null && receiverArgument != null) {
else if (receiverParameter == null && receiverArgument != null) {
tracing.noReceiverAllowed(candidateCall.trace) tracing.noReceiverAllowed(candidateCall.trace)
if (call.calleeExpression is KtSimpleNameExpression) { if (call.calleeExpression is KtSimpleNameExpression) {
RECEIVER_PRESENCE_ERROR RECEIVER_PRESENCE_ERROR
} } else {
else {
OTHER_ERROR OTHER_ERROR
} }
} } else {
else {
SUCCESS SUCCESS
} }
} }
@@ -248,8 +244,7 @@ class CandidateResolver(
&& DescriptorUtils.isStaticNestedClass(candidateDescriptor.containingDeclaration) && DescriptorUtils.isStaticNestedClass(candidateDescriptor.containingDeclaration)
) { ) {
nestedClass = candidateDescriptor.containingDeclaration nestedClass = candidateDescriptor.containingDeclaration
} } else if (candidateDescriptor is FakeCallableDescriptorForObject) {
else if (candidateDescriptor is FakeCallableDescriptorForObject) {
nestedClass = candidateDescriptor.getReferencedObject() nestedClass = candidateDescriptor.getReferencedObject()
} }
if (nestedClass != null) { if (nestedClass != null) {
@@ -341,8 +336,9 @@ class CandidateResolver(
} }
fun <D : CallableDescriptor> checkAllValueArguments( fun <D : CallableDescriptor> checkAllValueArguments(
context: CallCandidateResolutionContext<D>, context: CallCandidateResolutionContext<D>,
resolveFunctionArgumentBodies: ResolveArgumentsMode): ValueArgumentsCheckingResult { resolveFunctionArgumentBodies: ResolveArgumentsMode
): ValueArgumentsCheckingResult {
val checkingResult = checkValueArgumentTypes(context, context.candidateCall, resolveFunctionArgumentBodies) val checkingResult = checkValueArgumentTypes(context, context.candidateCall, resolveFunctionArgumentBodies)
var resultStatus = checkingResult.status var resultStatus = checkingResult.status
resultStatus = resultStatus.combine(checkReceivers(context)) resultStatus = resultStatus.combine(checkReceivers(context))
@@ -351,9 +347,9 @@ class CandidateResolver(
} }
private fun <D : CallableDescriptor, C : CallResolutionContext<C>> checkValueArgumentTypes( private fun <D : CallableDescriptor, C : CallResolutionContext<C>> checkValueArgumentTypes(
context: CallResolutionContext<C>, context: CallResolutionContext<C>,
candidateCall: MutableResolvedCall<D>, candidateCall: MutableResolvedCall<D>,
resolveFunctionArgumentBodies: ResolveArgumentsMode resolveFunctionArgumentBodies: ResolveArgumentsMode
): ValueArgumentsCheckingResult { ): ValueArgumentsCheckingResult {
var resultStatus = SUCCESS var resultStatus = SUCCESS
val argumentTypes = Lists.newArrayList<KotlinType>() val argumentTypes = Lists.newArrayList<KotlinType>()
@@ -373,27 +369,26 @@ class CandidateResolver(
var resultingType: KotlinType? = type var resultingType: KotlinType? = type
if (type == null || (type.isError && !type.isFunctionPlaceholder)) { if (type == null || (type.isError && !type.isFunctionPlaceholder)) {
matchStatus = ArgumentMatchStatus.ARGUMENT_HAS_NO_TYPE matchStatus = ArgumentMatchStatus.ARGUMENT_HAS_NO_TYPE
} } else if (!noExpectedType(expectedType)) {
else if (!noExpectedType(expectedType)) {
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) { if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) {
val smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext) val smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext)
if (smartCast == null) { if (smartCast == null) {
resultStatus = tryNotNullableArgument(type, expectedType) ?: OTHER_ERROR resultStatus = tryNotNullableArgument(type, expectedType) ?: OTHER_ERROR
matchStatus = ArgumentMatchStatus.TYPE_MISMATCH matchStatus = ArgumentMatchStatus.TYPE_MISMATCH
} } else {
else {
resultingType = smartCast resultingType = smartCast
} }
} } else if (ErrorUtils.containsUninferredParameter(expectedType)) {
else if (ErrorUtils.containsUninferredParameter(expectedType)) {
matchStatus = ArgumentMatchStatus.MATCH_MODULO_UNINFERRED_TYPES matchStatus = ArgumentMatchStatus.MATCH_MODULO_UNINFERRED_TYPES
} }
val spreadElement = argument.getSpreadElement() val spreadElement = argument.getSpreadElement()
if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) { if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) {
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context)
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, expectedType, expression, context, val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
call = null, recordExpressionType = false) dataFlowValue, expectedType, expression, context,
call = null, recordExpressionType = false
)
if (smartCastResult == null || !smartCastResult.isCorrect) { if (smartCastResult == null || !smartCastResult.isCorrect) {
context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement)) context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement))
} }
@@ -407,10 +402,10 @@ class CandidateResolver(
} }
private fun smartCastValueArgumentTypeIfPossible( private fun smartCastValueArgumentTypeIfPossible(
expression: KtExpression, expression: KtExpression,
expectedType: KotlinType, expectedType: KotlinType,
actualType: KotlinType, actualType: KotlinType,
context: ResolutionContext<*> context: ResolutionContext<*>
): KotlinType? { ): KotlinType? {
val receiverToCast = ExpressionReceiver.create(KtPsiUtil.safeDeparenthesize(expression), actualType, context.trace.bindingContext) val receiverToCast = ExpressionReceiver.create(KtPsiUtil.safeDeparenthesize(expression), actualType, context.trace.bindingContext)
val variants = smartCastManager.getSmartCastVariantsExcludingReceiver(context, receiverToCast) val variants = smartCastManager.getSmartCastVariantsExcludingReceiver(context, receiverToCast)
@@ -440,8 +435,8 @@ class CandidateResolver(
} }
private fun CallCandidateResolutionContext<*>.checkReceiverTypeError( private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(
receiverParameterDescriptor: ReceiverParameterDescriptor?, receiverParameterDescriptor: ReceiverParameterDescriptor?,
receiverArgument: ReceiverValue? receiverArgument: ReceiverValue?
) = checkAndReport { ) = checkAndReport {
if (receiverParameterDescriptor == null || receiverArgument == null) return@checkAndReport SUCCESS if (receiverParameterDescriptor == null || receiverArgument == null) return@checkAndReport SUCCESS
@@ -463,42 +458,48 @@ class CandidateResolver(
// both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'. // both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
// Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object. // Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object.
resultStatus = resultStatus.combine(context.checkReceiver( resultStatus = resultStatus.combine(
context.checkReceiver(
candidateCall, candidateCall,
candidateCall.resultingDescriptor.extensionReceiverParameter, candidateCall.resultingDescriptor.extensionReceiverParameter,
candidateCall.extensionReceiver, candidateCall.extensionReceiver,
candidateCall.explicitReceiverKind.isExtensionReceiver, candidateCall.explicitReceiverKind.isExtensionReceiver,
implicitInvokeCheck = false, isDispatchReceiver = false implicitInvokeCheck = false, isDispatchReceiver = false
)) )
)
resultStatus = resultStatus.combine(context.checkReceiver( resultStatus = resultStatus.combine(
context.checkReceiver(
candidateCall, candidateCall,
candidateCall.resultingDescriptor.dispatchReceiverParameter, candidateCall.dispatchReceiver, candidateCall.resultingDescriptor.dispatchReceiverParameter, candidateCall.dispatchReceiver,
candidateCall.explicitReceiverKind.isDispatchReceiver, candidateCall.explicitReceiverKind.isDispatchReceiver,
// for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error
implicitInvokeCheck = context.call is CallForImplicitInvoke, implicitInvokeCheck = context.call is CallForImplicitInvoke,
isDispatchReceiver = true isDispatchReceiver = true
)) )
)
if (!context.isDebuggerContext if (!context.isDebuggerContext
&& candidateCall.dispatchReceiver != null && candidateCall.dispatchReceiver != null
// Do not report error if it's already reported when checked without receiver // Do not report error if it's already reported when checked without receiver
&& context.isCandidateVisible(receiverArgument = Visibilities.ALWAYS_SUITABLE_RECEIVER, smartCastType = null)) { && context.isCandidateVisible(receiverArgument = Visibilities.ALWAYS_SUITABLE_RECEIVER, smartCastType = null)) {
resultStatus = resultStatus.combine( resultStatus = resultStatus.combine(
context.checkVisibilityWithDispatchReceiver( context.checkVisibilityWithDispatchReceiver(
candidateCall.dispatchReceiver, candidateCall.smartCastDispatchReceiverType)) candidateCall.dispatchReceiver, candidateCall.smartCastDispatchReceiverType
)
)
} }
return resultStatus return resultStatus
} }
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkReceiver( private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkReceiver(
candidateCall: MutableResolvedCall<D>, candidateCall: MutableResolvedCall<D>,
receiverParameter: ReceiverParameterDescriptor?, receiverParameter: ReceiverParameterDescriptor?,
receiverArgument: ReceiverValue?, receiverArgument: ReceiverValue?,
isExplicitReceiver: Boolean, isExplicitReceiver: Boolean,
implicitInvokeCheck: Boolean, implicitInvokeCheck: Boolean,
isDispatchReceiver: Boolean isDispatchReceiver: Boolean
): ResolutionStatus { ): ResolutionStatus {
if (receiverParameter == null || receiverArgument == null) return SUCCESS if (receiverParameter == null || receiverArgument == null) return SUCCESS
val candidateDescriptor = candidateCall.candidateDescriptor val candidateDescriptor = candidateCall.candidateDescriptor
@@ -513,14 +514,15 @@ class CandidateResolver(
val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this) val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this)
if (smartCastSubtypingResult == null) { if (smartCastSubtypingResult == null) {
tracing.wrongReceiverType( tracing.wrongReceiverType(
trace, receiverParameter, receiverArgument, trace, receiverParameter, receiverArgument,
this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall))) this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall))
)
return OTHER_ERROR return OTHER_ERROR
} }
val notNullReceiverExpected = smartCastSubtypingResult != SmartCastManager.ReceiverSmartCastResult.OK val notNullReceiverExpected = smartCastSubtypingResult != SmartCastManager.ReceiverSmartCastResult.OK
val smartCastNeeded = val smartCastNeeded =
notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver) notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver)
var reportUnsafeCall = false var reportUnsafeCall = false
var nullableImplicitInvokeReceiver = false var nullableImplicitInvokeReceiver = false
@@ -547,26 +549,23 @@ class CandidateResolver(
if (dataFlowValue.immanentNullability.canBeNonNull()) { if (dataFlowValue.immanentNullability.canBeNonNull()) {
expression?.let { trace.record(BindingContext.SMARTCAST_NULL, it) } expression?.let { trace.record(BindingContext.SMARTCAST_NULL, it) }
} }
} } else if (!nullableImplicitInvokeReceiver && smartCastNeeded) {
else if (!nullableImplicitInvokeReceiver && smartCastNeeded) {
// Look if smart cast has some useful nullability info // Look if smart cast has some useful nullability info
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast( val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
dataFlowValue, expectedReceiverParameterType, dataFlowValue, expectedReceiverParameterType,
{ possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) }, { possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) },
expression, this, candidateCall.call, recordExpressionType = true expression, this, candidateCall.call, recordExpressionType = true
) )
if (smartCastResult == null) { if (smartCastResult == null) {
if (notNullReceiverExpected) { if (notNullReceiverExpected) {
reportUnsafeCall = true reportUnsafeCall = true
} }
} } else {
else {
if (isDispatchReceiver) { if (isDispatchReceiver) {
candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType) candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType)
} } else {
else {
candidateCall.updateExtensionReceiverWithSmartCastIfNeeded(smartCastResult.resultType) candidateCall.updateExtensionReceiverWithSmartCastIfNeeded(smartCastResult.resultType)
} }
if (!smartCastResult.isCorrect) { if (!smartCastResult.isCorrect) {
@@ -589,11 +588,11 @@ class CandidateResolver(
inner class ValueArgumentsCheckingResult(val status: ResolutionStatus, val argumentTypes: List<KotlinType>) inner class ValueArgumentsCheckingResult(val status: ResolutionStatus, val argumentTypes: List<KotlinType>)
private fun CallCandidateResolutionContext<*>.checkGenericBoundsInAFunctionCall( private fun CallCandidateResolutionContext<*>.checkGenericBoundsInAFunctionCall(
ktTypeArguments: List<KtTypeProjection>, ktTypeArguments: List<KtTypeProjection>,
typeArguments: List<KotlinType>, typeArguments: List<KotlinType>,
functionDescriptor: CallableDescriptor, functionDescriptor: CallableDescriptor,
substitutor: TypeSubstitutor, substitutor: TypeSubstitutor,
trace: BindingTrace trace: BindingTrace
) { ) {
if (functionDescriptor is TypeAliasConstructorDescriptor) { if (functionDescriptor is TypeAliasConstructorDescriptor) {
checkGenericBoundsInTypeAliasConstructorCall(ktTypeArguments, functionDescriptor, substitutor, trace) checkGenericBoundsInTypeAliasConstructorCall(ktTypeArguments, functionDescriptor, substitutor, trace)
@@ -612,10 +611,10 @@ class CandidateResolver(
} }
private class TypeAliasSingleStepExpansionReportStrategy( private class TypeAliasSingleStepExpansionReportStrategy(
private val callElement: KtElement, private val callElement: KtElement,
typeAlias: TypeAliasDescriptor, typeAlias: TypeAliasDescriptor,
ktTypeArguments: List<KtTypeProjection>, ktTypeArguments: List<KtTypeProjection>,
private val trace: BindingTrace private val trace: BindingTrace
) : TypeAliasExpansionReportStrategy { ) : TypeAliasExpansionReportStrategy {
init { init {
assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" } assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" }
@@ -627,7 +626,11 @@ class CandidateResolver(
// can't happen in single-step expansion // can't happen in single-step expansion
} }
override fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType) { override fun conflictingProjection(
typeAlias: TypeAliasDescriptor,
typeParameter: TypeParameterDescriptor?,
substitutedArgument: KotlinType
) {
// can't happen in single-step expansion // can't happen in single-step expansion
} }
@@ -639,24 +642,28 @@ class CandidateResolver(
// can't happen in single-step expansion // can't happen in single-step expansion
} }
override fun boundsViolationInSubstitution(bound: KotlinType, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor) { override fun boundsViolationInSubstitution(
bound: KotlinType,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
) {
val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor
val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument] val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument]
val argumentTypeReferenceElement = argumentElement?.typeReference val argumentTypeReferenceElement = argumentElement?.typeReference
if (argumentTypeReferenceElement != null) { if (argumentTypeReferenceElement != null) {
trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument)) trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument))
} } else {
else {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(callElement, bound, argument, typeParameter)) trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(callElement, bound, argument, typeParameter))
} }
} }
} }
private fun CallCandidateResolutionContext<*>.checkGenericBoundsInTypeAliasConstructorCall( private fun CallCandidateResolutionContext<*>.checkGenericBoundsInTypeAliasConstructorCall(
ktTypeArguments: List<KtTypeProjection>, ktTypeArguments: List<KtTypeProjection>,
typeAliasConstructorDescriptor: TypeAliasConstructorDescriptor, typeAliasConstructorDescriptor: TypeAliasConstructorDescriptor,
typeAliasParametersSubstitutor: TypeSubstitutor, typeAliasParametersSubstitutor: TypeSubstitutor,
trace: BindingTrace trace: BindingTrace
) { ) {
val substitutedType = typeAliasParametersSubstitutor.substitute(typeAliasConstructorDescriptor.returnType, Variance.INVARIANT)!! val substitutedType = typeAliasParametersSubstitutor.substitute(typeAliasConstructorDescriptor.returnType, Variance.INVARIANT)!!
val boundsSubstitutor = TypeSubstitutor.create(substitutedType) val boundsSubstitutor = TypeSubstitutor.create(substitutedType)
@@ -677,10 +684,10 @@ class CandidateResolver(
private fun checkTypeInTypeAliasSubstitutionRec( private fun checkTypeInTypeAliasSubstitutionRec(
reportStrategy: TypeAliasExpansionReportStrategy, reportStrategy: TypeAliasExpansionReportStrategy,
unsubstitutedType: KotlinType, unsubstitutedType: KotlinType,
typeAliasParametersSubstitutor: TypeSubstitutor, typeAliasParametersSubstitutor: TypeSubstitutor,
boundsSubstitutor: TypeSubstitutor boundsSubstitutor: TypeSubstitutor
) { ) {
// TODO refactor TypeResolver // TODO refactor TypeResolver
val typeParameters = unsubstitutedType.constructor.parameters val typeParameters = unsubstitutedType.constructor.parameters
@@ -688,30 +695,41 @@ class CandidateResolver(
// TODO do not perform substitution for type arguments multiple times // TODO do not perform substitution for type arguments multiple times
val substitutedTypeArguments = typeAliasParametersSubstitutor.safeSubstitute(unsubstitutedType, Variance.INVARIANT).arguments val substitutedTypeArguments = typeAliasParametersSubstitutor.safeSubstitute(unsubstitutedType, Variance.INVARIANT).arguments
for (i in 0 ..Math.min(typeParameters.size, substitutedTypeArguments.size) - 1) { for (i in 0..Math.min(typeParameters.size, substitutedTypeArguments.size) - 1) {
val substitutedTypeProjection = substitutedTypeArguments[i] val substitutedTypeProjection = substitutedTypeArguments[i]
if (substitutedTypeProjection.isStarProjection) continue if (substitutedTypeProjection.isStarProjection) continue
val typeParameter = typeParameters[i] val typeParameter = typeParameters[i]
val substitutedTypeArgument = substitutedTypeProjection.type val substitutedTypeArgument = substitutedTypeProjection.type
val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type
DescriptorResolver.checkBoundsInTypeAlias(reportStrategy, unsubstitutedTypeArgument, substitutedTypeArgument, typeParameter, boundsSubstitutor) DescriptorResolver.checkBoundsInTypeAlias(
reportStrategy,
unsubstitutedTypeArgument,
substitutedTypeArgument,
typeParameter,
boundsSubstitutor
)
checkTypeInTypeAliasSubstitutionRec(reportStrategy, unsubstitutedTypeArgument, typeAliasParametersSubstitutor, boundsSubstitutor) checkTypeInTypeAliasSubstitutionRec(
reportStrategy,
unsubstitutedTypeArgument,
typeAliasParametersSubstitutor,
boundsSubstitutor
)
} }
} }
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.shouldContinue() = private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.shouldContinue() =
candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.status.possibleTransformToSuccess() candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.status.possibleTransformToSuccess()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.check( private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.check(
crossinline checker: CallCandidateResolutionContext<D>.() -> Unit crossinline checker: CallCandidateResolutionContext<D>.() -> Unit
) { ) {
if (shouldContinue()) checker() else candidateCall.addRemainingTasks { checker() } if (shouldContinue()) checker() else candidateCall.addRemainingTasks { checker() }
} }
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkAndReport( private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkAndReport(
crossinline checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus crossinline checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus
) { ) {
check { check {
candidateCall.addStatus(checker()) candidateCall.addStatus(checker())
@@ -38,10 +38,10 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class DiagnosticReporterByTrackingStrategy( class DiagnosticReporterByTrackingStrategy(
val constantExpressionEvaluator: ConstantExpressionEvaluator, val constantExpressionEvaluator: ConstantExpressionEvaluator,
val context: BasicCallResolutionContext, val context: BasicCallResolutionContext,
val psiKotlinCall: PSIKotlinCall val psiKotlinCall: PSIKotlinCall
): DiagnosticReporter { ) : DiagnosticReporter {
private val trace = context.trace as TrackingBindingTrace private val trace = context.trace as TrackingBindingTrace
private val tracingStrategy: TracingStrategy get() = psiKotlinCall.tracingStrategy private val tracingStrategy: TracingStrategy get() = psiKotlinCall.tracingStrategy
private val call: Call get() = psiKotlinCall.psiCall private val call: Call get() = psiKotlinCall.psiCall
@@ -53,7 +53,10 @@ class DiagnosticReporterByTrackingStrategy(
override fun onCall(diagnostic: KotlinCallDiagnostic) { override fun onCall(diagnostic: KotlinCallDiagnostic) {
when (diagnostic.javaClass) { when (diagnostic.javaClass) {
VisibilityError::class.java -> tracingStrategy.invisibleMember(trace, (diagnostic as VisibilityError).invisibleMember) VisibilityError::class.java -> tracingStrategy.invisibleMember(trace, (diagnostic as VisibilityError).invisibleMember)
NoValueForParameter::class.java -> tracingStrategy.noValueForParameter(trace, (diagnostic as NoValueForParameter).parameterDescriptor) NoValueForParameter::class.java -> tracingStrategy.noValueForParameter(
trace,
(diagnostic as NoValueForParameter).parameterDescriptor
)
InstantiationOfAbstractClass::class.java -> tracingStrategy.instantiationOfAbstractClass(trace) InstantiationOfAbstractClass::class.java -> tracingStrategy.instantiationOfAbstractClass(trace)
AbstractSuperCall::class.java -> tracingStrategy.abstractSuperCall(trace) AbstractSuperCall::class.java -> tracingStrategy.abstractSuperCall(trace)
} }
@@ -62,10 +65,10 @@ class DiagnosticReporterByTrackingStrategy(
override fun onTypeArguments(diagnostic: KotlinCallDiagnostic) { override fun onTypeArguments(diagnostic: KotlinCallDiagnostic) {
val psiCallElement = psiKotlinCall.psiCall.callElement val psiCallElement = psiKotlinCall.psiCall.callElement
val reportElement = val reportElement =
if (psiCallElement is KtCallExpression) if (psiCallElement is KtCallExpression)
psiCallElement.typeArgumentList ?: psiCallElement.calleeExpression ?: psiCallElement psiCallElement.typeArgumentList ?: psiCallElement.calleeExpression ?: psiCallElement
else else
psiCallElement psiCallElement
when (diagnostic) { when (diagnostic) {
is WrongCountOfTypeArguments -> { is WrongCountOfTypeArguments -> {
@@ -123,8 +126,8 @@ class DiagnosticReporterByTrackingStrategy(
} }
override fun onCallArgumentName(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) { override fun onCallArgumentName(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) {
val nameReference = callArgument.psiCallArgument.valueArgument.getArgumentName()?.referenceExpression ?: val nameReference = callArgument.psiCallArgument.valueArgument.getArgumentName()?.referenceExpression
error("Argument name should be not null for argument: $callArgument") ?: error("Argument name should be not null for argument: $callArgument")
when (diagnostic.javaClass) { when (diagnostic.javaClass) {
NamedArgumentReference::class.java -> { NamedArgumentReference::class.java -> {
trace.record(BindingContext.REFERENCE_TARGET, nameReference, (diagnostic as NamedArgumentReference).parameterDescriptor) trace.record(BindingContext.REFERENCE_TARGET, nameReference, (diagnostic as NamedArgumentReference).parameterDescriptor)
@@ -133,10 +136,12 @@ class DiagnosticReporterByTrackingStrategy(
NameForAmbiguousParameter::class.java -> trace.report(NAME_FOR_AMBIGUOUS_PARAMETER.on(nameReference)) NameForAmbiguousParameter::class.java -> trace.report(NAME_FOR_AMBIGUOUS_PARAMETER.on(nameReference))
NameNotFound::class.java -> trace.report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference)) NameNotFound::class.java -> trace.report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference))
NamedArgumentNotAllowed::class.java -> trace.report(NAMED_ARGUMENTS_NOT_ALLOWED.on( NamedArgumentNotAllowed::class.java -> trace.report(
NAMED_ARGUMENTS_NOT_ALLOWED.on(
nameReference, nameReference,
if ((diagnostic as NamedArgumentNotAllowed).descriptor is FunctionInvokeDescriptor) INVOKE_ON_FUNCTION_TYPE else NON_KOTLIN_FUNCTION if ((diagnostic as NamedArgumentNotAllowed).descriptor is FunctionInvokeDescriptor) INVOKE_ON_FUNCTION_TYPE else NON_KOTLIN_FUNCTION
)) )
)
ArgumentPassedTwice::class.java -> trace.report(ARGUMENT_PASSED_TWICE.on(nameReference)) ArgumentPassedTwice::class.java -> trace.report(ARGUMENT_PASSED_TWICE.on(nameReference))
} }
} }
@@ -156,23 +161,29 @@ class DiagnosticReporterByTrackingStrategy(
is ExpressionKotlinCallArgumentImpl -> { is ExpressionKotlinCallArgumentImpl -> {
trace.markAsReported() trace.markAsReported()
val context = context.replaceDataFlowInfo(expressionArgument.dataFlowInfoBeforeThisArgument) val context = context.replaceDataFlowInfo(expressionArgument.dataFlowInfoBeforeThisArgument)
val argumentExpression = KtPsiUtil.getLastElementDeparenthesized(expressionArgument.valueArgument.getArgumentExpression (), context.statementFilter) val argumentExpression = KtPsiUtil.getLastElementDeparenthesized(
expressionArgument.valueArgument.getArgumentExpression(),
context.statementFilter
)
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context)
SmartCastManager.checkAndRecordPossibleCast( SmartCastManager.checkAndRecordPossibleCast(
dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call, dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call,
recordExpressionType = true) recordExpressionType = true
)
} }
is ReceiverExpressionKotlinCallArgument -> { is ReceiverExpressionKotlinCallArgument -> {
trace.markAsReported() trace.markAsReported()
val receiverValue = expressionArgument.receiver.receiverValue val receiverValue = expressionArgument.receiver.receiverValue
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, context) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, context)
SmartCastManager.checkAndRecordPossibleCast( SmartCastManager.checkAndRecordPossibleCast(
dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call, dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call,
recordExpressionType = true) recordExpressionType = true
)
} }
else -> null else -> null
} }
val resolvedCall = smartCastDiagnostic.kotlinCall?.psiKotlinCall?.psiCall?.getResolvedCall(trace.bindingContext) as? NewResolvedCallImpl<*> val resolvedCall =
smartCastDiagnostic.kotlinCall?.psiKotlinCall?.psiCall?.getResolvedCall(trace.bindingContext) as? NewResolvedCallImpl<*>
if (resolvedCall != null && smartCastResult != null) { if (resolvedCall != null && smartCastResult != null) {
if (resolvedCall.extensionReceiver == expressionArgument.receiver.receiverValue) { if (resolvedCall.extensionReceiver == expressionArgument.receiver.receiverValue) {
resolvedCall.updateExtensionReceiverWithSmartCastIfNeeded(smartCastResult.resultType) resolvedCall.updateExtensionReceiverWithSmartCastIfNeeded(smartCastResult.resultType)
@@ -194,7 +205,7 @@ class DiagnosticReporterByTrackingStrategy(
val constraintError = diagnostic as NewConstraintError val constraintError = diagnostic as NewConstraintError
val position = constraintError.position.from val position = constraintError.position.from
val argument = (position as? ArgumentConstraintPosition)?.argument val argument = (position as? ArgumentConstraintPosition)?.argument
?: (position as? ReceiverConstraintPosition)?.argument ?: (position as? ReceiverConstraintPosition)?.argument
argument?.let { argument?.let {
val expression = it.psiExpression ?: return val expression = it.psiExpression ?: return
val deparenthesized = KtPsiUtil.safeDeparenthesize(expression) val deparenthesized = KtPsiUtil.safeDeparenthesize(expression)
@@ -218,7 +229,12 @@ class DiagnosticReporterByTrackingStrategy(
val capturedError = diagnostic as CapturedTypeFromSubtyping val capturedError = diagnostic as CapturedTypeFromSubtyping
(capturedError.position as? ArgumentConstraintPosition)?.let { (capturedError.position as? ArgumentConstraintPosition)?.let {
val expression = it.argument.psiExpression ?: return val expression = it.argument.psiExpression ?: return
trace.report(NEW_INFERENCE_ERROR.on(expression, "Capture type from subtyping ${capturedError.constraintType} for variable ${capturedError.typeVariable}")) trace.report(
NEW_INFERENCE_ERROR.on(
expression,
"Capture type from subtyping ${capturedError.constraintType} for variable ${capturedError.typeVariable}"
)
)
} }
} }
} }
@@ -229,7 +245,7 @@ class DiagnosticReporterByTrackingStrategy(
val builtIns = context.scope.ownerDescriptor.builtIns val builtIns = context.scope.ownerDescriptor.builtIns
val constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, trace, context.expectedType) val constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, trace, context.expectedType)
val hasConstantTypeError = CompileTimeConstantChecker(context, builtIns, true) val hasConstantTypeError = CompileTimeConstantChecker(context, builtIns, true)
.checkConstantExpressionType(constantValue, expression, constraintError.upperType) .checkConstantExpressionType(constantValue, expression, constraintError.upperType)
if (hasConstantTypeError) return true if (hasConstantTypeError) return true
} }
return false return false
@@ -66,9 +66,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val SPECIAL_FUNCTION_NAMES = ResolveConstruct.values().map { it.specialFunctionName }.toSet() private val SPECIAL_FUNCTION_NAMES = ResolveConstruct.values().map { it.specialFunctionName }.toSet()
class GenericCandidateResolver( class GenericCandidateResolver(
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val coroutineInferenceSupport: CoroutineInferenceSupport, private val coroutineInferenceSupport: CoroutineInferenceSupport,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
fun <D : CallableDescriptor> inferTypeArguments(context: CallCandidateResolutionContext<D>): ResolutionStatus { fun <D : CallableDescriptor> inferTypeArguments(context: CallCandidateResolutionContext<D>): ResolutionStatus {
val candidateCall = context.candidateCall val candidateCall = context.candidateCall
@@ -90,7 +90,7 @@ class GenericCandidateResolver(
// and throw the results away // and throw the results away
// We'll type check the arguments later, with the inferred types expected // We'll type check the arguments later, with the inferred types expected
addConstraintForValueArgument( addConstraintForValueArgument(
valueArgument, valueParameterDescriptor, substituteDontCare, builder, context, SHAPE_FUNCTION_ARGUMENTS valueArgument, valueParameterDescriptor, substituteDontCare, builder, context, SHAPE_FUNCTION_ARGUMENTS
) )
} }
} }
@@ -114,9 +114,9 @@ class GenericCandidateResolver(
receiverType = updateResultTypeForSmartCasts(receiverType, receiverArgument.expression, context) receiverType = updateResultTypeForSmartCasts(receiverType, receiverArgument.expression, context)
} }
builder.addSubtypeConstraint( builder.addSubtypeConstraint(
receiverType, receiverType,
builder.compositeSubstitutor().substitute(receiverParameter.type, Variance.INVARIANT), builder.compositeSubstitutor().substitute(receiverParameter.type, Variance.INVARIANT),
RECEIVER_POSITION.position() RECEIVER_POSITION.position()
) )
} }
@@ -133,9 +133,9 @@ class GenericCandidateResolver(
} }
private fun ConstraintSystem.Builder.typeInSystem(call: Call, type: KotlinType?): KotlinType? = private fun ConstraintSystem.Builder.typeInSystem(call: Call, type: KotlinType?): KotlinType? =
type?.let { type?.let {
typeVariableSubstitutors[call.toHandle()]?.substitute(it, Variance.INVARIANT) typeVariableSubstitutors[call.toHandle()]?.substitute(it, Variance.INVARIANT)
} }
private fun FunctionDescriptor.isFunctionForExpectTypeFromCastFeature(): Boolean { private fun FunctionDescriptor.isFunctionForExpectTypeFromCastFeature(): Boolean {
val typeParameter = typeParameters.singleOrNull() ?: return false val typeParameter = typeParameters.singleOrNull() ?: return false
@@ -153,8 +153,8 @@ class GenericCandidateResolver(
} }
private fun addExpectedTypeForExplicitCast( private fun addExpectedTypeForExplicitCast(
context: CallCandidateResolutionContext<*>, context: CallCandidateResolutionContext<*>,
builder: ConstraintSystem.Builder builder: ConstraintSystem.Builder
) { ) {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ExpectedTypeFromCast)) return if (!languageVersionSettings.supportsFeature(LanguageFeature.ExpectedTypeFromCast)) return
@@ -185,8 +185,7 @@ class GenericCandidateResolver(
val targetExpression = if (possibleQualifiedExpression is KtQualifiedExpression) { val targetExpression = if (possibleQualifiedExpression is KtQualifiedExpression) {
if (possibleQualifiedExpression.selectorExpression != callExpression) return null if (possibleQualifiedExpression.selectorExpression != callExpression) return null
possibleQualifiedExpression possibleQualifiedExpression
} } else {
else {
callExpression callExpression
} }
@@ -207,7 +206,7 @@ class GenericCandidateResolver(
val boundsSubstitutor = TypeSubstitutor.create(type) val boundsSubstitutor = TypeSubstitutor.create(type)
type.arguments.forEachIndexed forEachArgument@{ i, typeProjection -> type.arguments.forEachIndexed forEachArgument@ { i, typeProjection ->
if (typeProjection.isStarProjection) return@forEachArgument // continue if (typeProjection.isStarProjection) return@forEachArgument // continue
val typeParameter = typeConstructor.parameters[i] val typeParameter = typeConstructor.parameters[i]
@@ -218,10 +217,10 @@ class GenericCandidateResolver(
} }
private fun addValidityConstraintsForTypeArgument( private fun addValidityConstraintsForTypeArgument(
builder: ConstraintSystem.Builder, builder: ConstraintSystem.Builder,
substitutedArgument: TypeProjection, substitutedArgument: TypeProjection,
typeParameter: TypeParameterDescriptor, typeParameter: TypeParameterDescriptor,
boundsSubstitutor: TypeSubstitutor boundsSubstitutor: TypeSubstitutor
) { ) {
val substitutedType = substitutedArgument.type val substitutedType = substitutedArgument.type
for (upperBound in typeParameter.upperBounds) { for (upperBound in typeParameter.upperBounds) {
@@ -247,12 +246,12 @@ class GenericCandidateResolver(
} }
private fun addConstraintForValueArgument( private fun addConstraintForValueArgument(
valueArgument: ValueArgument, valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor, valueParameterDescriptor: ValueParameterDescriptor,
substitutor: TypeSubstitutor, substitutor: TypeSubstitutor,
builder: ConstraintSystem.Builder, builder: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<*>, context: CallCandidateResolutionContext<*>,
resolveFunctionArgumentBodies: ResolveArgumentsMode resolveFunctionArgumentBodies: ResolveArgumentsMode
) { ) {
val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument, context) val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument, context)
val argumentExpression = valueArgument.getArgumentExpression() val argumentExpression = valueArgument.getArgumentExpression()
@@ -268,23 +267,24 @@ class GenericCandidateResolver(
if (addConstraintForNestedCall(argumentExpression, constraintPosition, builder, newContext, effectiveExpectedType)) return if (addConstraintForNestedCall(argumentExpression, constraintPosition, builder, newContext, effectiveExpectedType)) return
val type = updateResultTypeForSmartCasts(typeInfoForCall.type, argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument)) val type =
updateResultTypeForSmartCasts(typeInfoForCall.type, argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument))
if (argumentExpression is KtCallableReferenceExpression && type == null) return if (argumentExpression is KtCallableReferenceExpression && type == null) return
builder.addSubtypeConstraint( builder.addSubtypeConstraint(
type, type,
builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT), builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT),
constraintPosition constraintPosition
) )
} }
private fun addConstraintForNestedCall( private fun addConstraintForNestedCall(
argumentExpression: KtExpression?, argumentExpression: KtExpression?,
constraintPosition: ConstraintPosition, constraintPosition: ConstraintPosition,
builder: ConstraintSystem.Builder, builder: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<*>, context: CallCandidateResolutionContext<*>,
effectiveExpectedType: KotlinType effectiveExpectedType: KotlinType
): Boolean { ): Boolean {
val resolutionResults = getResolutionResultsCachedData(argumentExpression, context)?.resolutionResults val resolutionResults = getResolutionResultsCachedData(argumentExpression, context)?.resolutionResults
if (resolutionResults == null || !resolutionResults.isSingleResult) return false if (resolutionResults == null || !resolutionResults.isSingleResult) return false
@@ -312,18 +312,18 @@ class GenericCandidateResolver(
val argumentExpressionType = nestedCall.makeNullableTypeIfSafeReceiver(candidateWithFreshVariables.returnType, context) val argumentExpressionType = nestedCall.makeNullableTypeIfSafeReceiver(candidateWithFreshVariables.returnType, context)
builder.addSubtypeConstraint( builder.addSubtypeConstraint(
argumentExpressionType, argumentExpressionType,
builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT), builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT),
constraintPosition constraintPosition
) )
return true return true
} }
private fun updateResultTypeForSmartCasts( private fun updateResultTypeForSmartCasts(
type: KotlinType?, type: KotlinType?,
argumentExpression: KtExpression?, argumentExpression: KtExpression?,
context: ResolutionContext<*> context: ResolutionContext<*>
): KotlinType? { ): KotlinType? {
val deparenthesizedArgument = KtPsiUtil.getLastElementDeparenthesized(argumentExpression, context.statementFilter) val deparenthesizedArgument = KtPsiUtil.getLastElementDeparenthesized(argumentExpression, context.statementFilter)
if (deparenthesizedArgument == null || type == null) return type if (deparenthesizedArgument == null || type == null) return type
@@ -345,7 +345,8 @@ class GenericCandidateResolver(
// to inconsistency and errors in inference. See definition of `effectiveExpectedTypeInSystem` in `addConstraintForFunctionLiteralArgument` // to inconsistency and errors in inference. See definition of `effectiveExpectedTypeInSystem` in `addConstraintForFunctionLiteralArgument`
val newContext = if (resolvedCall is VariableAsFunctionResolvedCall) { val newContext = if (resolvedCall is VariableAsFunctionResolvedCall) {
CallCandidateResolutionContext.create( CallCandidateResolutionContext.create(
resolvedCall, context, context.trace, context.tracing, resolvedCall.functionCall.call, context.candidateResolveMode) resolvedCall, context, context.trace, context.tracing, resolvedCall.functionCall.call, context.candidateResolveMode
)
} else { } else {
context context
} }
@@ -356,8 +357,10 @@ class GenericCandidateResolver(
for (valueArgument in resolvedValueArgument.arguments) { for (valueArgument in resolvedValueArgument.arguments) {
valueArgument.getArgumentExpression()?.let { argumentExpression -> valueArgument.getArgumentExpression()?.let { argumentExpression ->
ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(argumentExpression, newContext)?.let { functionLiteral -> ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(argumentExpression, newContext)?.let { functionLiteral ->
addConstraintForFunctionLiteralArgument(functionLiteral, valueArgument, valueParameterDescriptor, constraintSystem, newContext, addConstraintForFunctionLiteralArgument(
resolvedCall.candidateDescriptor.returnType) functionLiteral, valueArgument, valueParameterDescriptor, constraintSystem, newContext,
resolvedCall.candidateDescriptor.returnType
)
} }
// as inference for callable references depends on expected type, // as inference for callable references depends on expected type,
@@ -367,20 +370,21 @@ class GenericCandidateResolver(
// For example, type info for arguments is needed before call will be completed (See ControlStructureTypingVisitor.visitIfExpression) // For example, type info for arguments is needed before call will be completed (See ControlStructureTypingVisitor.visitIfExpression)
val temporaryContextForCall = if (resolvedCall.candidateDescriptor.name in SPECIAL_FUNCTION_NAMES) { val temporaryContextForCall = if (resolvedCall.candidateDescriptor.name in SPECIAL_FUNCTION_NAMES) {
newContext newContext
} } else {
else {
val temporaryBindingTrace = TemporaryBindingTrace.create( val temporaryBindingTrace = TemporaryBindingTrace.create(
newContext.trace, "Trace to complete argument for call that might be not resulting call") newContext.trace, "Trace to complete argument for call that might be not resulting call"
)
newContext.replaceBindingTrace(temporaryBindingTrace) newContext.replaceBindingTrace(temporaryBindingTrace)
} }
ArgumentTypeResolver.getCallableReferenceExpressionIfAny(argumentExpression, newContext)?.let { callableReference -> ArgumentTypeResolver.getCallableReferenceExpressionIfAny(argumentExpression, newContext)?.let { callableReference ->
addConstraintForCallableReference( addConstraintForCallableReference(
callableReference, callableReference,
valueArgument, valueArgument,
valueParameterDescriptor, valueParameterDescriptor,
constraintSystem, constraintSystem,
temporaryContextForCall) temporaryContextForCall
)
} }
} }
} }
@@ -395,24 +399,24 @@ class GenericCandidateResolver(
// and we have some expected type Type, we can expected from literal to return Type // and we have some expected type Type, we can expected from literal to return Type
// Otherwise we do not care about literal's exact return type // Otherwise we do not care about literal's exact return type
private fun estimateLiteralReturnType( private fun estimateLiteralReturnType(
context: CallCandidateResolutionContext<*>, context: CallCandidateResolutionContext<*>,
literalExpectedType: KotlinType, literalExpectedType: KotlinType,
ownerReturnType: KotlinType? ownerReturnType: KotlinType?
) = if (!TypeUtils.noExpectedType(context.expectedType) && ) = if (!TypeUtils.noExpectedType(context.expectedType) &&
ownerReturnType != null && ownerReturnType != null &&
TypeUtils.isTypeParameter(ownerReturnType) && TypeUtils.isTypeParameter(ownerReturnType) &&
literalExpectedType.isFunctionTypeOrSubtype && literalExpectedType.isFunctionTypeOrSubtype &&
getReturnTypeForCallable(literalExpectedType) == ownerReturnType) getReturnTypeForCallable(literalExpectedType) == ownerReturnType)
context.expectedType context.expectedType
else DONT_CARE else DONT_CARE
private fun <D : CallableDescriptor> addConstraintForFunctionLiteralArgument( private fun <D : CallableDescriptor> addConstraintForFunctionLiteralArgument(
functionLiteral: KtFunction, functionLiteral: KtFunction,
valueArgument: ValueArgument, valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor, valueParameterDescriptor: ValueParameterDescriptor,
constraintSystem: ConstraintSystem.Builder, constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>, context: CallCandidateResolutionContext<D>,
argumentOwnerReturnType: KotlinType? argumentOwnerReturnType: KotlinType?
) { ) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return val argumentExpression = valueArgument.getArgumentExpression() ?: return
@@ -439,24 +443,27 @@ class GenericCandidateResolver(
val dataFlowInfoForArgument = dataFlowInfoForArguments.getInfo(valueArgument) val dataFlowInfoForArgument = dataFlowInfoForArguments.getInfo(valueArgument)
val effectiveExpectedTypeInSystem = val effectiveExpectedTypeInSystem =
constraintSystem.typeVariableSubstitutors[context.call.toHandle()]?.substitute(effectiveExpectedType, Variance.INVARIANT) constraintSystem.typeVariableSubstitutors[context.call.toHandle()]?.substitute(effectiveExpectedType, Variance.INVARIANT)
//todo analyze function literal body once in 'dependent' mode, then complete it with respect to expected type //todo analyze function literal body once in 'dependent' mode, then complete it with respect to expected type
val hasExpectedReturnType = !hasUnknownReturnType(expectedType) val hasExpectedReturnType = !hasUnknownReturnType(expectedType)
val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index) val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index)
if (hasExpectedReturnType) { if (hasExpectedReturnType) {
val temporaryToResolveFunctionLiteral = TemporaryTraceAndCache.create( val temporaryToResolveFunctionLiteral = TemporaryTraceAndCache.create(
context, "trace to resolve function literal with expected return type", argumentExpression) context, "trace to resolve function literal with expected return type", argumentExpression
)
val statementExpression = KtPsiUtil.getExpressionOrLastStatementInBlock(functionLiteral.bodyExpression) ?: return val statementExpression = KtPsiUtil.getExpressionOrLastStatementInBlock(functionLiteral.bodyExpression) ?: return
val mismatch = BooleanArray(1) val mismatch = BooleanArray(1)
val errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch( val errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch(
temporaryToResolveFunctionLiteral.trace, statementExpression, mismatch) temporaryToResolveFunctionLiteral.trace, statementExpression, mismatch
)
val newContext = context.replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType) val newContext = context.replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType)
.replaceDataFlowInfo(dataFlowInfoForArgument).replaceResolutionResultsCache(temporaryToResolveFunctionLiteral.cache) .replaceDataFlowInfo(dataFlowInfoForArgument).replaceResolutionResultsCache(temporaryToResolveFunctionLiteral.cache)
.replaceContextDependency(INDEPENDENT) .replaceContextDependency(INDEPENDENT)
val type = argumentTypeResolver.getFunctionLiteralTypeInfo( val type = argumentTypeResolver.getFunctionLiteralTypeInfo(
argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).type argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS
).type
if (!mismatch[0]) { if (!mismatch[0]) {
constraintSystem.addSubtypeConstraint(type, effectiveExpectedTypeInSystem, position) constraintSystem.addSubtypeConstraint(type, effectiveExpectedTypeInSystem, position)
temporaryToResolveFunctionLiteral.commit() temporaryToResolveFunctionLiteral.commit()
@@ -466,36 +473,38 @@ class GenericCandidateResolver(
val estimatedReturnType = estimateLiteralReturnType(context, effectiveExpectedType, argumentOwnerReturnType) val estimatedReturnType = estimateLiteralReturnType(context, effectiveExpectedType, argumentOwnerReturnType)
val expectedTypeWithEstimatedReturnType = replaceReturnTypeForCallable(expectedType, estimatedReturnType) val expectedTypeWithEstimatedReturnType = replaceReturnTypeForCallable(expectedType, estimatedReturnType)
val newContext = context.replaceExpectedType(expectedTypeWithEstimatedReturnType).replaceDataFlowInfo(dataFlowInfoForArgument) val newContext = context.replaceExpectedType(expectedTypeWithEstimatedReturnType).replaceDataFlowInfo(dataFlowInfoForArgument)
.replaceContextDependency(INDEPENDENT) .replaceContextDependency(INDEPENDENT)
val type = argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).type val type =
argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS)
.type
constraintSystem.addSubtypeConstraint(type, effectiveExpectedTypeInSystem, position) constraintSystem.addSubtypeConstraint(type, effectiveExpectedTypeInSystem, position)
} }
private fun <D : CallableDescriptor> addConstraintForCallableReference( private fun <D : CallableDescriptor> addConstraintForCallableReference(
callableReference: KtCallableReferenceExpression, callableReference: KtCallableReferenceExpression,
valueArgument: ValueArgument, valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor, valueParameterDescriptor: ValueParameterDescriptor,
constraintSystem: ConstraintSystem.Builder, constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D> context: CallCandidateResolutionContext<D>
) { ) {
val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument, context) val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument, context)
val expectedType = getExpectedTypeForCallableReference(callableReference, constraintSystem, context, effectiveExpectedType) val expectedType = getExpectedTypeForCallableReference(callableReference, constraintSystem, context, effectiveExpectedType)
?: return ?: return
if (!ReflectionTypes.isCallableType(expectedType)) return if (!ReflectionTypes.isCallableType(expectedType)) return
val resolvedType = getResolvedTypeForCallableReference(callableReference, context, expectedType, valueArgument) ?: return val resolvedType = getResolvedTypeForCallableReference(callableReference, context, expectedType, valueArgument) ?: return
val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index) val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index)
constraintSystem.addSubtypeConstraint( constraintSystem.addSubtypeConstraint(
resolvedType, resolvedType,
constraintSystem.typeVariableSubstitutors[context.call.toHandle()]?.substitute(effectiveExpectedType, Variance.INVARIANT), constraintSystem.typeVariableSubstitutors[context.call.toHandle()]?.substitute(effectiveExpectedType, Variance.INVARIANT),
position position
) )
} }
private fun <D : CallableDescriptor> getExpectedTypeForCallableReference( private fun <D : CallableDescriptor> getExpectedTypeForCallableReference(
callableReference: KtCallableReferenceExpression, callableReference: KtCallableReferenceExpression,
constraintSystem: ConstraintSystem.Builder, constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>, context: CallCandidateResolutionContext<D>,
effectiveExpectedType: KotlinType effectiveExpectedType: KotlinType
): KotlinType? { ): KotlinType? {
val substitutedType = constraintSystem.build().currentSubstitutor.substitute(effectiveExpectedType, Variance.INVARIANT) val substitutedType = constraintSystem.build().currentSubstitutor.substitute(effectiveExpectedType, Variance.INVARIANT)
if (substitutedType != null && !TypeUtils.isDontCarePlaceholder(substitutedType)) if (substitutedType != null && !TypeUtils.isDontCarePlaceholder(substitutedType))
@@ -509,19 +518,20 @@ class GenericCandidateResolver(
} }
private fun <D : CallableDescriptor> getResolvedTypeForCallableReference( private fun <D : CallableDescriptor> getResolvedTypeForCallableReference(
callableReference: KtCallableReferenceExpression, callableReference: KtCallableReferenceExpression,
context: CallCandidateResolutionContext<D>, context: CallCandidateResolutionContext<D>,
expectedType: KotlinType, expectedType: KotlinType,
valueArgument: ValueArgument valueArgument: ValueArgument
): KotlinType? { ): KotlinType? {
val dataFlowInfoForArgument = context.candidateCall.dataFlowInfoForArguments.getInfo(valueArgument) val dataFlowInfoForArgument = context.candidateCall.dataFlowInfoForArguments.getInfo(valueArgument)
val expectedTypeWithoutReturnType = if (!hasUnknownReturnType(expectedType)) replaceReturnTypeByUnknown(expectedType) else expectedType val expectedTypeWithoutReturnType =
if (!hasUnknownReturnType(expectedType)) replaceReturnTypeByUnknown(expectedType) else expectedType
val newContext = context val newContext = context
.replaceExpectedType(expectedTypeWithoutReturnType) .replaceExpectedType(expectedTypeWithoutReturnType)
.replaceDataFlowInfo(dataFlowInfoForArgument) .replaceDataFlowInfo(dataFlowInfoForArgument)
.replaceContextDependency(INDEPENDENT) .replaceContextDependency(INDEPENDENT)
return argumentTypeResolver.getCallableReferenceTypeInfo( return argumentTypeResolver.getCallableReferenceTypeInfo(
callableReference, callableReference, newContext, RESOLVE_FUNCTION_ARGUMENTS callableReference, callableReference, newContext, RESOLVE_FUNCTION_ARGUMENTS
).type ).type
} }
} }
@@ -539,7 +549,7 @@ fun makeConstantSubstitutor(typeParameterDescriptors: Collection<TypeParameterDe
return TypeSubstitutor.create(object : TypeConstructorSubstitution() { return TypeSubstitutor.create(object : TypeConstructorSubstitution() {
override operator fun get(key: TypeConstructor) = override operator fun get(key: TypeConstructor) =
if (key in constructors) projection else null if (key in constructors) projection else null
override fun isEmpty() = false override fun isEmpty() = false
}) })
@@ -33,6 +33,6 @@ class CallReturnsArrayOfNothingChecker : CallChecker {
if (this == null || isComputingDeferredType(this)) return false if (this == null || isComputingDeferredType(this)) return false
return isArrayOfNothing() || return isArrayOfNothing() ||
arguments.any { !it.isStarProjection && it.type.containsArrayOfNothing() } arguments.any { !it.isStarProjection && it.type.containsArrayOfNothing() }
} }
} }
@@ -32,11 +32,16 @@ class CallableReferenceCompatibilityChecker : CallChecker {
for ((_, resolvedArgument) in resolvedCall.valueArguments) { for ((_, resolvedArgument) in resolvedCall.valueArguments) {
inner@ for (argument in resolvedArgument.arguments) { inner@ for (argument in resolvedArgument.arguments) {
val argumentExpression = argument.getArgumentExpression() as? KtCallableReferenceExpression ?: continue@inner val argumentExpression = argument.getArgumentExpression() as? KtCallableReferenceExpression ?: continue@inner
val callableReferenceResolvedCall = argumentExpression.callableReference.getResolvedCall(context.trace.bindingContext) ?: continue@inner val callableReferenceResolvedCall =
argumentExpression.callableReference.getResolvedCall(context.trace.bindingContext) ?: continue@inner
if (callableReferenceResolvedCall.call.isCallableReference() && if (callableReferenceResolvedCall.call.isCallableReference() &&
callableReferenceResolvedCall.candidateDescriptor.typeParameters.isNotEmpty()) { callableReferenceResolvedCall.candidateDescriptor.typeParameters.isNotEmpty()) {
context.trace.report(Errors.UNSUPPORTED_FEATURE.on(argumentExpression, context.trace.report(
typeInferenceForCallableReferencesFeature to context.languageVersionSettings)) Errors.UNSUPPORTED_FEATURE.on(
argumentExpression,
typeInferenceForCallableReferencesFeature to context.languageVersionSettings
)
)
} }
} }
} }
@@ -62,7 +62,7 @@ class CapturingInClosureChecker : CallChecker {
} }
private fun isCapturedInInline( private fun isCapturedInInline(
context: BindingContext, scopeContainer: DeclarationDescriptor, variableParent: DeclarationDescriptor context: BindingContext, scopeContainer: DeclarationDescriptor, variableParent: DeclarationDescriptor
): Boolean { ): Boolean {
val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer) val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer)
if (!InlineUtil.canBeInlineArgument(scopeDeclaration)) return false if (!InlineUtil.canBeInlineArgument(scopeDeclaration)) return false
@@ -70,7 +70,7 @@ class CapturingInClosureChecker : CallChecker {
if (InlineUtil.isInlinedArgument(scopeDeclaration as KtFunction, context, false)) { if (InlineUtil.isInlinedArgument(scopeDeclaration as KtFunction, context, false)) {
val scopeContainerParent = scopeContainer.containingDeclaration ?: error("parent is null for $scopeContainer") val scopeContainerParent = scopeContainer.containingDeclaration ?: error("parent is null for $scopeContainer")
return !isCapturedVariable(variableParent, scopeContainerParent) || return !isCapturedVariable(variableParent, scopeContainerParent) ||
isCapturedInInline(context, scopeContainerParent, variableParent) isCapturedInInline(context, scopeContainerParent, variableParent)
} }
return false return false
} }
@@ -40,9 +40,9 @@ object DeprecatedCallChecker : CallChecker {
} }
private fun check( private fun check(
targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement, targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
deprecationResolver: DeprecationResolver deprecationResolver: DeprecationResolver
) { ) {
// Objects will be checked by DeprecatedClassifierUsageChecker // Objects will be checked by DeprecatedClassifierUsageChecker
if (targetDescriptor is FakeCallableDescriptorForObject) return if (targetDescriptor is FakeCallableDescriptorForObject) return
@@ -58,8 +58,7 @@ object DeprecatedCallChecker : CallChecker {
for (deprecation in deprecations) { for (deprecation in deprecations) {
trace.report(createDeprecationDiagnostic(element, deprecation, languageVersionSettings)) trace.report(createDeprecationDiagnostic(element, deprecation, languageVersionSettings))
} }
} } else if (targetDescriptor is PropertyDescriptor && shouldCheckPropertyGetter(element)) {
else if (targetDescriptor is PropertyDescriptor && shouldCheckPropertyGetter(element)) {
targetDescriptor.getter?.let { check(it, trace, element, languageVersionSettings, deprecationResolver) } targetDescriptor.getter?.let { check(it, trace, element, languageVersionSettings, deprecationResolver) }
} }
} }
@@ -46,15 +46,15 @@ object DslScopeViolationCallChecker : CallChecker {
} }
private fun checkCallImplicitReceiver( private fun checkCallImplicitReceiver(
callImplicitReceiver: ReceiverValue, callImplicitReceiver: ReceiverValue,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
reportOn: PsiElement, reportOn: PsiElement,
context: CallCheckerContext context: CallCheckerContext
) { ) {
val receiversUntilOneFromTheCall = val receiversUntilOneFromTheCall =
context.scope.parentsWithSelf context.scope.parentsWithSelf
.mapNotNull { (it as? LexicalScope)?.implicitReceiver?.value } .mapNotNull { (it as? LexicalScope)?.implicitReceiver?.value }
.takeWhile { it != callImplicitReceiver }.toList() .takeWhile { it != callImplicitReceiver }.toList()
if (receiversUntilOneFromTheCall.isEmpty()) return if (receiversUntilOneFromTheCall.isEmpty()) return
@@ -62,7 +62,7 @@ object DslScopeViolationCallChecker : CallChecker {
if (callDslMarkers.isEmpty()) return if (callDslMarkers.isEmpty()) return
val closestAnotherReceiverWithSameDslMarker = val closestAnotherReceiverWithSameDslMarker =
receiversUntilOneFromTheCall.firstOrNull { receiver -> receiver.type.extractDslMarkerFqNames().any(callDslMarkers::contains) } receiversUntilOneFromTheCall.firstOrNull { receiver -> receiver.type.extractDslMarkerFqNames().any(callDslMarkers::contains) }
if (closestAnotherReceiverWithSameDslMarker != null) { if (closestAnotherReceiverWithSameDslMarker != null) {
// TODO: report receivers configuration (what's one is used and what's one is the closest) // TODO: report receivers configuration (what's one is used and what's one is the closest)
@@ -83,7 +83,7 @@ object DslScopeViolationCallChecker : CallChecker {
} }
constructor.declarationDescriptor?.getAllSuperClassifiers()?.asIterable() constructor.declarationDescriptor?.getAllSuperClassifiers()?.asIterable()
?.flatMapTo(result) { it.annotations.extractDslMarkerFqNames() } ?.flatMapTo(result) { it.annotations.extractDslMarkerFqNames() }
return result return result
} }
@@ -91,7 +91,7 @@ object DslScopeViolationCallChecker : CallChecker {
} }
private fun Annotations.extractDslMarkerFqNames() = private fun Annotations.extractDslMarkerFqNames() =
filter(AnnotationDescriptor::isDslMarker).map { it.fqName!! } filter(AnnotationDescriptor::isDslMarker).map { it.fqName!! }
private fun AnnotationDescriptor.isDslMarker(): Boolean { private fun AnnotationDescriptor.isDslMarker(): Boolean {
val classDescriptor = annotationClass ?: return false val classDescriptor = annotationClass ?: return false
@@ -49,9 +49,11 @@ class OperatorCallChecker : CallChecker {
call is CallTransformer.CallForImplicitInvoke && call.itIsVariableAsFunctionCall) { call is CallTransformer.CallForImplicitInvoke && call.itIsVariableAsFunctionCall) {
val outerCall = call.outerCall val outerCall = call.outerCall
if (isConventionCall(outerCall) || isWrongCallWithExplicitTypeArguments(resolvedCall, outerCall)) { if (isConventionCall(outerCall) || isWrongCallWithExplicitTypeArguments(resolvedCall, outerCall)) {
throw AssertionError("Illegal resolved call to variable with invoke for $outerCall. " + throw AssertionError(
"Variable: ${resolvedCall.variableCall.resultingDescriptor}" + "Illegal resolved call to variable with invoke for $outerCall. " +
"Invoke: ${resolvedCall.functionCall.resultingDescriptor}") "Variable: ${resolvedCall.variableCall.resultingDescriptor}" +
"Invoke: ${resolvedCall.functionCall.resultingDescriptor}"
)
} }
} }
@@ -89,11 +91,11 @@ class OperatorCallChecker : CallChecker {
} }
private fun isWrongCallWithExplicitTypeArguments( private fun isWrongCallWithExplicitTypeArguments(
resolvedCall: VariableAsFunctionResolvedCall, resolvedCall: VariableAsFunctionResolvedCall,
outerCall: Call outerCall: Call
): Boolean { ): Boolean {
val passedTypeArgumentsToInvoke = outerCall.typeArguments.isNotEmpty() && val passedTypeArgumentsToInvoke = outerCall.typeArguments.isNotEmpty() &&
resolvedCall.functionCall.candidateDescriptor.typeParameters.isNotEmpty() resolvedCall.functionCall.candidateDescriptor.typeParameters.isNotEmpty()
return passedTypeArgumentsToInvoke && resolvedCall.variableCall.candidateDescriptor.typeParameters.isNotEmpty() return passedTypeArgumentsToInvoke && resolvedCall.variableCall.candidateDescriptor.typeParameters.isNotEmpty()
} }
} }
@@ -108,8 +110,8 @@ fun shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings: LanguageVe
} }
private fun checkModConvention( private fun checkModConvention(
descriptor: FunctionDescriptor, languageVersionSettings: LanguageVersionSettings, descriptor: FunctionDescriptor, languageVersionSettings: LanguageVersionSettings,
diagnosticHolder: DiagnosticSink, modifier: PsiElement diagnosticHolder: DiagnosticSink, modifier: PsiElement
) { ) {
if (!descriptor.isOperatorMod()) return if (!descriptor.isOperatorMod()) return
@@ -117,8 +119,7 @@ private fun checkModConvention(
if (shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings)) { if (shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings)) {
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier) addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
} }
} } else {
else {
if (languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) { if (languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) {
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier) addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
} }
@@ -33,8 +33,8 @@ enum class RttiOperation {
} }
class RttiExpressionInformation( class RttiExpressionInformation(
val subject: KtExpression, val subject: KtExpression,
val sourceType: KotlinType?, val sourceType: KotlinType?,
val targetType: KotlinType?, val targetType: KotlinType?,
val operation: RttiOperation val operation: RttiOperation
) )
@@ -56,9 +56,9 @@ object CoroutineSuspendCallChecker : CallChecker {
} }
val enclosingSuspendFunction = context.scope val enclosingSuspendFunction = context.scope
.parentsWithSelf.firstOrNull { .parentsWithSelf.firstOrNull {
it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS && it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS &&
it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>() }?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
when { when {
@@ -67,19 +67,32 @@ object CoroutineSuspendCallChecker : CallChecker {
if (!InlineUtil.checkNonLocalReturnUsage(enclosingSuspendFunction, callElement, context.resolutionContext)) { if (!InlineUtil.checkNonLocalReturnUsage(enclosingSuspendFunction, callElement, context.resolutionContext)) {
context.trace.report(Errors.NON_LOCAL_SUSPENSION_POINT.on(reportOn)) context.trace.report(Errors.NON_LOCAL_SUSPENSION_POINT.on(reportOn))
} } else if (context.scope.parentsWithSelf.any { it.isScopeForDefaultParameterValuesOf(enclosingSuspendFunction) }) {
else if (context.scope.parentsWithSelf.any { it.isScopeForDefaultParameterValuesOf(enclosingSuspendFunction) }) {
context.trace.report(Errors.UNSUPPORTED.on(reportOn, "suspend function calls in a context of default parameter value")) context.trace.report(Errors.UNSUPPORTED.on(reportOn, "suspend function calls in a context of default parameter value"))
} }
context.trace.record(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.call, enclosingSuspendFunction) context.trace.record(
BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL,
resolvedCall.call,
enclosingSuspendFunction
)
checkRestrictsSuspension(enclosingSuspendFunction, resolvedCall, reportOn, context) checkRestrictsSuspension(enclosingSuspendFunction, resolvedCall, reportOn, context)
} }
else -> { else -> {
when (descriptor) { when (descriptor) {
is FunctionDescriptor -> context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn, resolvedCall.candidateDescriptor)) is FunctionDescriptor -> context.trace.report(
is PropertyDescriptor -> context.trace.report(Errors.ILLEGAL_SUSPEND_PROPERTY_ACCESS.on(reportOn, resolvedCall.candidateDescriptor)) Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(
reportOn,
resolvedCall.candidateDescriptor
)
)
is PropertyDescriptor -> context.trace.report(
Errors.ILLEGAL_SUSPEND_PROPERTY_ACCESS.on(
reportOn,
resolvedCall.candidateDescriptor
)
)
} }
} }
} }
@@ -87,7 +100,7 @@ object CoroutineSuspendCallChecker : CallChecker {
} }
private fun HierarchicalScope.isScopeForDefaultParameterValuesOf(enclosingSuspendFunction: FunctionDescriptor) = private fun HierarchicalScope.isScopeForDefaultParameterValuesOf(enclosingSuspendFunction: FunctionDescriptor) =
this is LexicalScope && this.kind == LexicalScopeKind.DEFAULT_VALUE && this.ownerDescriptor == enclosingSuspendFunction this is LexicalScope && this.kind == LexicalScopeKind.DEFAULT_VALUE && this.ownerDescriptor == enclosingSuspendFunction
object BuilderFunctionsCallChecker : CallChecker { object BuilderFunctionsCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
@@ -116,10 +129,10 @@ fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, dia
} }
private fun checkRestrictsSuspension( private fun checkRestrictsSuspension(
enclosingCallableDescriptor: CallableDescriptor, enclosingCallableDescriptor: CallableDescriptor,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
reportOn: PsiElement, reportOn: PsiElement,
context: CallCheckerContext context: CallCheckerContext
) { ) {
val enclosingSuspendReceiverValue = enclosingCallableDescriptor.extensionReceiverParameter?.value ?: return val enclosingSuspendReceiverValue = enclosingCallableDescriptor.extensionReceiverParameter?.value ?: return
@@ -50,7 +50,7 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
internal data class Constraint( internal data class Constraint(
val kind: ConstraintKind, val subtype: KotlinType, val superType: KotlinType, val position: ConstraintPosition val kind: ConstraintKind, val subtype: KotlinType, val superType: KotlinType, val position: ConstraintPosition
) )
enum class ConstraintKind(val bound: TypeBounds.BoundKind) { enum class ConstraintKind(val bound: TypeBounds.BoundKind) {
@@ -74,7 +74,7 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
override fun registerTypeVariables( override fun registerTypeVariables(
call: CallHandle, typeParameters: Collection<TypeParameterDescriptor>, external: Boolean call: CallHandle, typeParameters: Collection<TypeParameterDescriptor>, external: Boolean
): TypeSubstitutor { ): TypeSubstitutor {
if (typeParameters.isEmpty()) return storeSubstitutor(call, TypeSubstitutor.EMPTY) if (typeParameters.isEmpty()) return storeSubstitutor(call, TypeSubstitutor.EMPTY)
@@ -82,11 +82,10 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
typeParameters.map { typeParameters.map {
TypeVariable(call, it, it, true) TypeVariable(call, it, it, true)
} }
} } else {
else {
val freshTypeParameters = ArrayList<TypeParameterDescriptor>(typeParameters.size) val freshTypeParameters = ArrayList<TypeParameterDescriptor>(typeParameters.size)
DescriptorSubstitutor.substituteTypeParameters( DescriptorSubstitutor.substituteTypeParameters(
typeParameters.toList(), TypeSubstitution.EMPTY, typeParameters.first().containingDeclaration, freshTypeParameters typeParameters.toList(), TypeSubstitution.EMPTY, typeParameters.first().containingDeclaration, freshTypeParameters
) )
freshTypeParameters.zip(typeParameters).map { freshTypeParameters.zip(typeParameters).map {
val (fresh, original) = it val (fresh, original) = it
@@ -106,33 +105,44 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
} }
return storeSubstitutor(call, TypeSubstitutor.create(TypeConstructorSubstitution.createByParametersMap( return storeSubstitutor(
typeParameters.zip(typeVariables.map { it.type }.defaultProjections()).toMap() call, TypeSubstitutor.create(
))) TypeConstructorSubstitution.createByParametersMap(
typeParameters.zip(typeVariables.map { it.type }.defaultProjections()).toMap()
)
)
)
} }
private fun KotlinType.isProper() = !TypeUtils.contains(this) { private fun KotlinType.isProper() = !TypeUtils.contains(this) { type ->
type -> type.constructor.declarationDescriptor.let { it is TypeParameterDescriptor && isMyTypeVariable(it) } type.constructor.declarationDescriptor.let { it is TypeParameterDescriptor && isMyTypeVariable(it) }
} }
internal fun getNestedTypeVariables(type: KotlinType): List<TypeVariable> = internal fun getNestedTypeVariables(type: KotlinType): List<TypeVariable> =
type.getNestedTypeParameters().mapNotNull { getMyTypeVariable(it) } type.getNestedTypeParameters().mapNotNull { getMyTypeVariable(it) }
override fun addSubtypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType?, constraintPosition: ConstraintPosition) { override fun addSubtypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType?, constraintPosition: ConstraintPosition) {
addConstraint(SUB_TYPE, constrainingType, subjectType, ConstraintContext(constraintPosition, initial = true, initialReduction = true)) addConstraint(
SUB_TYPE,
constrainingType,
subjectType,
ConstraintContext(constraintPosition, initial = true, initialReduction = true)
)
} }
fun addConstraint( fun addConstraint(
constraintKind: ConstraintKind, constraintKind: ConstraintKind,
subType: KotlinType?, subType: KotlinType?,
superType: KotlinType?, superType: KotlinType?,
constraintContext: ConstraintContext constraintContext: ConstraintContext
) { ) {
val constraintPosition = constraintContext.position val constraintPosition = constraintContext.position
// when processing nested constraints, `derivedFrom` information should be reset // when processing nested constraints, `derivedFrom` information should be reset
val newConstraintContext = ConstraintContext(constraintContext.position, derivedFrom = null, initial = false, val newConstraintContext = ConstraintContext(
initialReduction = constraintContext.initialReduction) constraintContext.position, derivedFrom = null, initial = false,
initialReduction = constraintContext.initialReduction
)
val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks { val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks {
private var depth = 0 private var depth = 0
@@ -187,11 +197,11 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
private fun doAddConstraint( private fun doAddConstraint(
constraintKind: ConstraintKind, constraintKind: ConstraintKind,
subType: KotlinType?, subType: KotlinType?,
superType: KotlinType?, superType: KotlinType?,
constraintContext: ConstraintContext, constraintContext: ConstraintContext,
typeCheckingProcedure: TypeCheckingProcedure typeCheckingProcedure: TypeCheckingProcedure
) { ) {
val constraintPosition = constraintContext.position val constraintPosition = constraintContext.position
if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return
@@ -212,8 +222,7 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
return return
} }
createTypeForFunctionPlaceholder(subType, superType) createTypeForFunctionPlaceholder(subType, superType)
} } else {
else {
subType subType
} }
@@ -230,8 +239,7 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
val superType2 = simplifyType(superType, constraintContext.initial) val superType2 = simplifyType(superType, constraintContext.initial)
val result = if (constraintKind == EQUAL) { val result = if (constraintKind == EQUAL) {
typeCheckingProcedure.equalTypes(subType2, superType2) typeCheckingProcedure.equalTypes(subType2, superType2)
} } else {
else {
typeCheckingProcedure.isSubtypeOf(subType2, superType) typeCheckingProcedure.isSubtypeOf(subType2, superType)
} }
if (!result) errors.add(newTypeInferenceOrParameterConstraintError(constraintPosition)) if (!result) errors.add(newTypeInferenceOrParameterConstraintError(constraintPosition))
@@ -245,22 +253,24 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
private fun simplifyType(type: KotlinType, isInitialConstraint: Boolean): KotlinType = private fun simplifyType(type: KotlinType, isInitialConstraint: Boolean): KotlinType =
if (mode == Mode.SPECIFICITY || !isInitialConstraint) if (mode == Mode.SPECIFICITY || !isInitialConstraint)
type type
else { else {
// if subType is nullable and superType is not nullable, unsafe call or type mismatch error will be generated later, // if subType is nullable and superType is not nullable, unsafe call or type mismatch error will be generated later,
// but constraint system should be solved anyway // but constraint system should be solved anyway
TypeUtils.makeNotNullable(type) TypeUtils.makeNotNullable(type)
} }
internal fun addBound( internal fun addBound(
typeVariable: TypeVariable, typeVariable: TypeVariable,
constrainingType: KotlinType, constrainingType: KotlinType,
kind: TypeBounds.BoundKind, kind: TypeBounds.BoundKind,
constraintContext: ConstraintContext constraintContext: ConstraintContext
) { ) {
val bound = Bound(typeVariable, constrainingType, kind, constraintContext.position, val bound = Bound(
constrainingType.isProper(), constraintContext.derivedFrom ?: emptySet()) typeVariable, constrainingType, kind, constraintContext.position,
constrainingType.isProper(), constraintContext.derivedFrom ?: emptySet()
)
val typeBounds = getTypeBounds(typeVariable) val typeBounds = getTypeBounds(typeVariable)
if (typeBounds.bounds.contains(bound)) return if (typeBounds.bounds.contains(bound)) return
@@ -277,10 +287,10 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
private fun generateTypeParameterBound( private fun generateTypeParameterBound(
parameterType: KotlinType, parameterType: KotlinType,
constrainingType: KotlinType, constrainingType: KotlinType,
boundKind: TypeBounds.BoundKind, boundKind: TypeBounds.BoundKind,
constraintContext: ConstraintContext constraintContext: ConstraintContext
) { ) {
val typeVariable = getMyTypeVariable(parameterType)!! val typeVariable = getMyTypeVariable(parameterType)!!
@@ -321,10 +331,10 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
private fun generateTypeParameterCaptureConstraint( private fun generateTypeParameterCaptureConstraint(
typeVariable: TypeVariable, typeVariable: TypeVariable,
constrainingTypeProjection: TypeProjection, constrainingTypeProjection: TypeProjection,
constraintContext: ConstraintContext, constraintContext: ConstraintContext,
isTypeMarkedNullable: Boolean isTypeMarkedNullable: Boolean
) { ) {
if (!typeVariable.originalTypeParameter.upperBounds.let { it.size == 1 && it.single().isDefaultBound() } && if (!typeVariable.originalTypeParameter.upperBounds.let { it.size == 1 && it.single().isDefaultBound() } &&
constrainingTypeProjection.projectionKind == Variance.IN_VARIANCE) { constrainingTypeProjection.projectionKind == Variance.IN_VARIANCE) {
@@ -332,8 +342,7 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
val typeProjection = if (isTypeMarkedNullable) { val typeProjection = if (isTypeMarkedNullable) {
TypeProjectionImpl(constrainingTypeProjection.projectionKind, TypeUtils.makeNotNullable(constrainingTypeProjection.type)) TypeProjectionImpl(constrainingTypeProjection.projectionKind, TypeUtils.makeNotNullable(constrainingTypeProjection.type))
} } else {
else {
constrainingTypeProjection constrainingTypeProjection
} }
val capturedType = createCapturedType(typeProjection) val capturedType = createCapturedType(typeProjection)
@@ -341,24 +350,29 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
internal fun getTypeBounds(variable: TypeVariable): TypeBoundsImpl { internal fun getTypeBounds(variable: TypeVariable): TypeBoundsImpl {
return allTypeParameterBounds[variable] ?: return allTypeParameterBounds[variable]
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $variable") ?: throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $variable")
} }
private fun isMyTypeVariable(typeParameter: TypeParameterDescriptor) = private fun isMyTypeVariable(typeParameter: TypeParameterDescriptor) =
getMyTypeVariable(typeParameter) != null getMyTypeVariable(typeParameter) != null
internal fun isMyTypeVariable(type: KotlinType): Boolean = internal fun isMyTypeVariable(type: KotlinType): Boolean =
getMyTypeVariable(type) != null getMyTypeVariable(type) != null
internal fun getMyTypeVariable(type: KotlinType): TypeVariable? { internal fun getMyTypeVariable(type: KotlinType): TypeVariable? {
return getMyTypeVariable(type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return null) return getMyTypeVariable(type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return null)
} }
private fun getMyTypeVariable(typeParameter: TypeParameterDescriptor): TypeVariable? = private fun getMyTypeVariable(typeParameter: TypeParameterDescriptor): TypeVariable? =
allTypeParameterBounds.keys.find { it.freshTypeParameter == typeParameter } allTypeParameterBounds.keys.find { it.freshTypeParameter == typeParameter }
private fun storeInitialConstraint(constraintKind: ConstraintKind, subType: KotlinType, superType: KotlinType, position: ConstraintPosition) { private fun storeInitialConstraint(
constraintKind: ConstraintKind,
subType: KotlinType,
superType: KotlinType,
position: ConstraintPosition
) {
initialConstraints.add(Constraint(constraintKind, subType, superType, position)) initialConstraints.add(Constraint(constraintKind, subType, superType, position))
} }
@@ -381,14 +395,14 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
if (!Collections.disjoint(typeVariableSubstitutors.keys, other.typeVariableSubstitutors.keys)) { if (!Collections.disjoint(typeVariableSubstitutors.keys, other.typeVariableSubstitutors.keys)) {
throw IllegalArgumentException( throw IllegalArgumentException(
"Combining two constraint systems only makes sense when they were created for different calls. " + "Combining two constraint systems only makes sense when they were created for different calls. " +
"Calls of the first system: ${typeVariableSubstitutors.keys}, second: ${other.typeVariableSubstitutors.keys}" "Calls of the first system: ${typeVariableSubstitutors.keys}, second: ${other.typeVariableSubstitutors.keys}"
) )
} }
if (!Collections.disjoint(other.allTypeParameterBounds.keys, allTypeParameterBounds.keys)) { if (!Collections.disjoint(other.allTypeParameterBounds.keys, allTypeParameterBounds.keys)) {
throw IllegalArgumentException( throw IllegalArgumentException(
"Combining two constraint systems only makes sense when they have no common variables. " + "Combining two constraint systems only makes sense when they have no common variables. " +
"First system variables: ${allTypeParameterBounds.keys}, second: ${other.allTypeParameterBounds.keys}" "First system variables: ${allTypeParameterBounds.keys}, second: ${other.allTypeParameterBounds.keys}"
) )
} }
@@ -415,10 +429,10 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
var counter = 0 var counter = 0
override fun registerTypeVariables(typeParameters: Collection<TypeParameterDescriptor>) = override fun registerTypeVariables(typeParameters: Collection<TypeParameterDescriptor>) =
registerTypeVariables(CallHandle.NONE, typeParameters) registerTypeVariables(CallHandle.NONE, typeParameters)
override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType) = override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType) =
addSubtypeConstraint(subType, superType, ConstraintPositionKind.VALUE_PARAMETER_POSITION.position(counter++)) addSubtypeConstraint(subType, superType, ConstraintPositionKind.VALUE_PARAMETER_POSITION.position(counter++))
override fun hasContradiction(): Boolean { override fun hasContradiction(): Boolean {
fixVariables() fixVariables()
@@ -429,8 +443,8 @@ open class ConstraintSystemBuilderImpl(private val mode: Mode = ConstraintSystem
} }
internal fun createTypeForFunctionPlaceholder( internal fun createTypeForFunctionPlaceholder(
functionPlaceholder: KotlinType, functionPlaceholder: KotlinType,
expectedType: KotlinType expectedType: KotlinType
): KotlinType { ): KotlinType {
if (!functionPlaceholder.isFunctionPlaceholder) return functionPlaceholder if (!functionPlaceholder.isFunctionPlaceholder) return functionPlaceholder
@@ -445,11 +459,12 @@ internal fun createTypeForFunctionPlaceholder(
val result = arrayListOf<KotlinType>() val result = arrayListOf<KotlinType>()
(1..functionArgumentsSize).forEach { result.add(DONT_CARE) } (1..functionArgumentsSize).forEach { result.add(DONT_CARE) }
result result
} } else {
else {
functionPlaceholderTypeConstructor.argumentTypes functionPlaceholderTypeConstructor.argumentTypes
} }
val receiverType = if (isExtension) DONT_CARE else null val receiverType = if (isExtension) DONT_CARE else null
return createFunctionType(functionPlaceholder.builtIns, Annotations.EMPTY, receiverType, newArgumentTypes, null, DONT_CARE, return createFunctionType(
suspendFunction = expectedType.isSuspendFunctionType) functionPlaceholder.builtIns, Annotations.EMPTY, receiverType, newArgumentTypes, null, DONT_CARE,
suspendFunction = expectedType.isSuspendFunctionType
)
} }
@@ -33,11 +33,11 @@ import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.* import java.util.*
internal class ConstraintSystemImpl( internal class ConstraintSystemImpl(
private val allTypeParameterBounds: Map<TypeVariable, TypeBoundsImpl>, private val allTypeParameterBounds: Map<TypeVariable, TypeBoundsImpl>,
private val usedInBounds: Map<TypeVariable, MutableList<TypeBounds.Bound>>, private val usedInBounds: Map<TypeVariable, MutableList<TypeBounds.Bound>>,
private val errors: List<ConstraintError>, private val errors: List<ConstraintError>,
private val initialConstraints: List<ConstraintSystemBuilderImpl.Constraint>, private val initialConstraints: List<ConstraintSystemBuilderImpl.Constraint>,
private val typeVariableSubstitutors: Map<CallHandle, TypeSubstitutor> private val typeVariableSubstitutors: Map<CallHandle, TypeSubstitutor>
) : ConstraintSystem { ) : ConstraintSystem {
private val localTypeParameterBounds: Map<TypeVariable, TypeBoundsImpl> private val localTypeParameterBounds: Map<TypeVariable, TypeBoundsImpl>
get() = allTypeParameterBounds.filterNot { it.key.isExternal } get() = allTypeParameterBounds.filterNot { it.key.isExternal }
@@ -48,7 +48,7 @@ internal class ConstraintSystemImpl(
override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters() && satisfyInitialConstraints() override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters() && satisfyInitialConstraints()
override fun hasContradiction() = hasParameterConstraintError() || hasConflictingConstraints() override fun hasContradiction() = hasParameterConstraintError() || hasConflictingConstraints()
|| hasCannotCaptureTypesError() || errors.any { it is TypeInferenceError } || hasCannotCaptureTypesError() || errors.any { it is TypeInferenceError }
/** /**
* All hacks were removed. This comment is left for information. * All hacks were removed. This comment is left for information.
@@ -84,7 +84,7 @@ internal class ConstraintSystemImpl(
override fun hasConflictingConstraints() = localTypeParameterBounds.values.any { it.values.size > 1 } override fun hasConflictingConstraints() = localTypeParameterBounds.values.any { it.values.size > 1 }
override fun hasUnknownParameters() = override fun hasUnknownParameters() =
localTypeParameterBounds.values.any { it.values.isEmpty() } || hasTypeParameterWithUnsatisfiedOnlyInputTypesError() localTypeParameterBounds.values.any { it.values.isEmpty() } || hasTypeParameterWithUnsatisfiedOnlyInputTypesError()
override fun hasParameterConstraintError() = errors.any { it is ParameterConstraintError } override fun hasParameterConstraintError() = errors.any { it is ParameterConstraintError }
@@ -101,26 +101,26 @@ internal class ConstraintSystemImpl(
override fun hasTypeInferenceIncorporationError() = errors.any { it is TypeInferenceError } || !satisfyInitialConstraints() override fun hasTypeInferenceIncorporationError() = errors.any { it is TypeInferenceError } || !satisfyInitialConstraints()
override fun hasTypeParameterWithUnsatisfiedOnlyInputTypesError() = override fun hasTypeParameterWithUnsatisfiedOnlyInputTypesError() =
localTypeParameterBounds.values.any { it.typeVariable.hasOnlyInputTypesAnnotation() && it.value == null } localTypeParameterBounds.values.any { it.typeVariable.hasOnlyInputTypesAnnotation() && it.value == null }
override val constraintErrors: List<ConstraintError> override val constraintErrors: List<ConstraintError>
get() = errors get() = errors
} }
private fun getParameterToInferredValueMap( private fun getParameterToInferredValueMap(
typeParameterBounds: Map<TypeVariable, TypeBoundsImpl>, typeParameterBounds: Map<TypeVariable, TypeBoundsImpl>,
getDefaultType: (TypeVariable) -> KotlinType, getDefaultType: (TypeVariable) -> KotlinType,
substituteOriginal: Boolean substituteOriginal: Boolean
): Map<TypeConstructor, TypeProjection> { ): Map<TypeConstructor, TypeProjection> {
val substitutionContext = HashMap<TypeConstructor, TypeProjection>() val substitutionContext = HashMap<TypeConstructor, TypeProjection>()
for ((variable, typeBounds) in typeParameterBounds) { for ((variable, typeBounds) in typeParameterBounds) {
val value = typeBounds.value val value = typeBounds.value
val typeConstructor = val typeConstructor =
if (substituteOriginal) variable.originalTypeParameter.typeConstructor if (substituteOriginal) variable.originalTypeParameter.typeConstructor
else variable.type.constructor else variable.type.constructor
val type = val type =
if (value != null && !TypeUtils.contains(value, DONT_CARE)) value if (value != null && !TypeUtils.contains(value, DONT_CARE)) value
else getDefaultType(variable) else getDefaultType(variable)
substitutionContext.put(typeConstructor, TypeProjectionImpl(type)) substitutionContext.put(typeConstructor, TypeProjectionImpl(type))
} }
return substitutionContext return substitutionContext
@@ -130,8 +130,8 @@ internal class ConstraintSystemImpl(
get() = allTypeParameterBounds.keys get() = allTypeParameterBounds.keys
override fun getTypeBounds(typeVariable: TypeVariable): TypeBoundsImpl { override fun getTypeBounds(typeVariable: TypeVariable): TypeBoundsImpl {
return allTypeParameterBounds[typeVariable] ?: return allTypeParameterBounds[typeVariable]
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable") ?: throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
} }
override val resultingSubstitutor: TypeSubstitutor override val resultingSubstitutor: TypeSubstitutor
@@ -143,11 +143,11 @@ internal class ConstraintSystemImpl(
private fun getSubstitutor(substituteOriginal: Boolean, getDefaultValue: (TypeVariable) -> KotlinType): TypeSubstitutor { private fun getSubstitutor(substituteOriginal: Boolean, getDefaultValue: (TypeVariable) -> KotlinType): TypeSubstitutor {
val parameterToInferredValueMap = getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue, substituteOriginal) val parameterToInferredValueMap = getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue, substituteOriginal)
return TypeSubstitutor.create( return TypeSubstitutor.create(
SubstitutionWithCapturedTypeApproximation( SubstitutionWithCapturedTypeApproximation(
SubstitutionFilteringInternalResolveAnnotations( SubstitutionFilteringInternalResolveAnnotations(
TypeConstructorSubstitution.createByConstructorsMap(parameterToInferredValueMap) TypeConstructorSubstitution.createByConstructorsMap(parameterToInferredValueMap)
)
) )
)
) )
} }
@@ -159,8 +159,7 @@ internal class ConstraintSystemImpl(
val substitutor = getSubstitutor(substituteOriginal = false) { ErrorUtils.createUninferredParameterType(it.originalTypeParameter) } val substitutor = getSubstitutor(substituteOriginal = false) { ErrorUtils.createUninferredParameterType(it.originalTypeParameter) }
fun KotlinType.substitute(): KotlinType? = substitutor.substitute(this, Variance.INVARIANT) fun KotlinType.substitute(): KotlinType? = substitutor.substitute(this, Variance.INVARIANT)
return initialConstraints.all { return initialConstraints.all { (kind, subtype, superType, position) ->
(kind, subtype, superType, position) ->
val resultSubType = subtype.substitute()?.let { val resultSubType = subtype.substitute()?.let {
// the call might be done via safe access, so we check for notNullable receiver type; // the call might be done via safe access, so we check for notNullable receiver type;
// 'unsafe call' error is reported otherwise later // 'unsafe call' error is reported otherwise later
@@ -181,7 +180,7 @@ internal class ConstraintSystemImpl(
} }
result.usedInBounds.putAll(usedInBounds.map { result.usedInBounds.putAll(usedInBounds.map {
val (variable, bounds) = it val (variable, bounds) = it
variable to bounds.filterTo(arrayListOf<TypeBounds.Bound>()) { filterConstraintPosition(it.position )} variable to bounds.filterTo(arrayListOf<TypeBounds.Bound>()) { filterConstraintPosition(it.position) }
}.toMap()) }.toMap())
result.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }) result.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) })
@@ -55,12 +55,12 @@ import org.jetbrains.kotlin.types.typeUtil.contains
import javax.inject.Inject import javax.inject.Inject
class TypeTemplate( class TypeTemplate(
val typeVariable: TypeVariable, val typeVariable: TypeVariable,
val coroutineInferenceData: CoroutineInferenceData, val coroutineInferenceData: CoroutineInferenceData,
nullable: Boolean = true nullable: Boolean = true
) : FlexibleType( ) : FlexibleType(
typeVariable.originalTypeParameter.builtIns.nothingType, typeVariable.originalTypeParameter.builtIns.nothingType,
typeVariable.originalTypeParameter.builtIns.anyType.makeNullableAsSpecified(nullable) typeVariable.originalTypeParameter.builtIns.anyType.makeNullableAsSpecified(nullable)
) { ) {
override fun replaceAnnotations(newAnnotations: Annotations) = this override fun replaceAnnotations(newAnnotations: Annotations) = this
@@ -79,9 +79,9 @@ class CoroutineInferenceData {
private var hereIsBadCall = false private var hereIsBadCall = false
fun getTypeTemplate(typeVariable: TypeVariable) = fun getTypeTemplate(typeVariable: TypeVariable) =
typeTemplates.getOrPut(typeVariable) { typeTemplates.getOrPut(typeVariable) {
TypeTemplate(typeVariable, this) TypeTemplate(typeVariable, this)
} }
fun initSystem() { fun initSystem() {
csBuilder.registerTypeVariables(CallHandle.NONE, typeTemplates.keys.map { it.freshTypeParameter }) csBuilder.registerTypeVariables(CallHandle.NONE, typeTemplates.keys.map { it.freshTypeParameter })
@@ -115,18 +115,18 @@ class CoroutineInferenceData {
} }
class CoroutineInferenceSupport( class CoroutineInferenceSupport(
val argumentTypeResolver: ArgumentTypeResolver, val argumentTypeResolver: ArgumentTypeResolver,
val expressionTypingServices: ExpressionTypingServices val expressionTypingServices: ExpressionTypingServices
) { ) {
@set:Inject @set:Inject
lateinit var callCompleter: CallCompleter lateinit var callCompleter: CallCompleter
fun analyzeCoroutine( fun analyzeCoroutine(
functionLiteral: KtFunction, functionLiteral: KtFunction,
valueArgument: ValueArgument, valueArgument: ValueArgument,
csBuilder: ConstraintSystem.Builder, csBuilder: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<*>, context: CallCandidateResolutionContext<*>,
lambdaExpectedType: KotlinType lambdaExpectedType: KotlinType
) { ) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return val argumentExpression = valueArgument.getArgumentExpression() ?: return
if (!lambdaExpectedType.isSuspendFunctionType) return if (!lambdaExpectedType.isSuspendFunctionType) return
@@ -155,13 +155,16 @@ class CoroutineInferenceSupport(
val approximationSubstitutor = object : DelegatedTypeSubstitution(constraintSystem.currentSubstitutor.substitution) { val approximationSubstitutor = object : DelegatedTypeSubstitution(constraintSystem.currentSubstitutor.substitution) {
override fun approximateContravariantCapturedTypes() = true override fun approximateContravariantCapturedTypes() = true
} }
val approximatedLambdaType = approximationSubstitutor.buildSubstitutor().substitute(lambdaExpectedType, Variance.IN_VARIANCE) ?: return val approximatedLambdaType =
approximationSubstitutor.buildSubstitutor().substitute(lambdaExpectedType, Variance.IN_VARIANCE) ?: return
val newExpectedType = createFunctionType(newReceiverType.builtIns, approximatedLambdaType.annotations, newReceiverType, val newExpectedType = createFunctionType(
approximatedLambdaType.getValueParameterTypesFromFunctionType().map(TypeProjection::getType), newReceiverType.builtIns, approximatedLambdaType.annotations, newReceiverType,
parameterNames = null, // TODO: parameterNames approximatedLambdaType.getValueParameterTypesFromFunctionType().map(TypeProjection::getType),
returnType = approximatedLambdaType.getReturnTypeFromFunctionType(), parameterNames = null, // TODO: parameterNames
suspendFunction = true) returnType = approximatedLambdaType.getReturnTypeFromFunctionType(),
suspendFunction = true
)
if (hasUnknownFunctionParameter(newExpectedType)) return if (hasUnknownFunctionParameter(newExpectedType)) return
@@ -169,20 +172,21 @@ class CoroutineInferenceSupport(
// this trace shouldn't be committed // this trace shouldn't be committed
val temporaryForCoroutine = TemporaryTraceAndCache.create( val temporaryForCoroutine = TemporaryTraceAndCache.create(
context, "trace for type argument inference for coroutine", functionLiteral) context, "trace for type argument inference for coroutine", functionLiteral
)
val newContext = context.replaceExpectedType(newExpectedType) val newContext = context.replaceExpectedType(newExpectedType)
.replaceDataFlowInfo(context.candidateCall.dataFlowInfoForArguments.getInfo(valueArgument)) .replaceDataFlowInfo(context.candidateCall.dataFlowInfoForArguments.getInfo(valueArgument))
.replaceContextDependency(ContextDependency.INDEPENDENT).replaceTraceAndCache(temporaryForCoroutine) .replaceContextDependency(ContextDependency.INDEPENDENT).replaceTraceAndCache(temporaryForCoroutine)
argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS) argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS)
inferenceData.reportInferenceResult(csBuilder) inferenceData.reportInferenceResult(csBuilder)
} }
fun checkCoroutineCalls( fun checkCoroutineCalls(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
tracingStrategy: TracingStrategy, tracingStrategy: TracingStrategy,
overloadResults: OverloadResolutionResultsImpl<*> overloadResults: OverloadResolutionResultsImpl<*>
) { ) {
val inferenceData = overloadResults.getCoroutineInferenceData() ?: return val inferenceData = overloadResults.getCoroutineInferenceData() ?: return
@@ -197,10 +201,9 @@ class CoroutineInferenceSupport(
inferenceData.badCallHappened() inferenceData.badCallHappened()
} }
forceInferenceForArguments(context) { forceInferenceForArguments(context) { valueArgument: ValueArgument, kotlinType: KotlinType ->
valueArgument: ValueArgument, kotlinType: KotlinType ->
val argumentMatch = resultingCall.getArgumentMapping(valueArgument) as? ArgumentMatch val argumentMatch = resultingCall.getArgumentMapping(valueArgument) as? ArgumentMatch
?: return@forceInferenceForArguments ?: return@forceInferenceForArguments
with(NewKotlinTypeChecker) { with(NewKotlinTypeChecker) {
val parameterType = getEffectiveExpectedType(argumentMatch.valueParameter, valueArgument, context) val parameterType = getEffectiveExpectedType(argumentMatch.valueParameter, valueArgument, context)
@@ -237,7 +240,10 @@ class CoroutineInferenceSupport(
} }
} }
private fun forceInferenceForArguments(context: CallResolutionContext<*>, callback: (argument: ValueArgument, argumentType: KotlinType) -> Unit) { private fun forceInferenceForArguments(
context: CallResolutionContext<*>,
callback: (argument: ValueArgument, argumentType: KotlinType) -> Unit
) {
val infoForArguments = context.dataFlowInfoForArguments val infoForArguments = context.dataFlowInfoForArguments
val call = context.call val call = context.call
val baseContext = context.replaceContextDependency(ContextDependency.INDEPENDENT).replaceExpectedType(NO_EXPECTED_TYPE) val baseContext = context.replaceContextDependency(ContextDependency.INDEPENDENT).replaceExpectedType(NO_EXPECTED_TYPE)
@@ -250,8 +256,8 @@ class CoroutineInferenceSupport(
} }
private fun getArgumentTypeInfo( private fun getArgumentTypeInfo(
expression: KtExpression, expression: KtExpression,
context: CallResolutionContext<*> context: CallResolutionContext<*>
): KotlinTypeInfo { ): KotlinTypeInfo {
getFunctionLiteralArgumentIfAny(expression, context)?.let { getFunctionLiteralArgumentIfAny(expression, context)?.let {
return argumentTypeResolver.getFunctionLiteralTypeInfo(expression, it, context, RESOLVE_FUNCTION_ARGUMENTS) return argumentTypeResolver.getFunctionLiteralTypeInfo(expression, it, context, RESOLVE_FUNCTION_ARGUMENTS)
@@ -266,9 +272,9 @@ class CoroutineInferenceSupport(
} }
fun isCoroutineCallWithAdditionalInference(parameterDescriptor: ValueParameterDescriptor, argument: ValueArgument) = fun isCoroutineCallWithAdditionalInference(parameterDescriptor: ValueParameterDescriptor, argument: ValueArgument) =
parameterDescriptor.hasSuspendFunctionType && parameterDescriptor.hasSuspendFunctionType &&
argument.getArgumentExpression() is KtLambdaExpression && argument.getArgumentExpression() is KtLambdaExpression &&
parameterDescriptor.type.let { it.isBuiltinFunctionalType && it.getReceiverTypeFromFunctionType() != null } parameterDescriptor.type.let { it.isBuiltinFunctionalType && it.getReceiverTypeFromFunctionType() != null }
fun OverloadResolutionResultsImpl<*>.isResultWithCoroutineInference() = getCoroutineInferenceData() != null fun OverloadResolutionResultsImpl<*>.isResultWithCoroutineInference() = getCoroutineInferenceData() != null
@@ -23,32 +23,32 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
class TypeVariable( class TypeVariable(
val call: CallHandle, val call: CallHandle,
internal val freshTypeParameter: TypeParameterDescriptor, internal val freshTypeParameter: TypeParameterDescriptor,
val originalTypeParameter: TypeParameterDescriptor, val originalTypeParameter: TypeParameterDescriptor,
val isExternal: Boolean val isExternal: Boolean
) { ) {
val name: Name get() = originalTypeParameter.name val name: Name get() = originalTypeParameter.name
val type: KotlinType get() = freshTypeParameter.defaultType val type: KotlinType get() = freshTypeParameter.defaultType
fun hasOnlyInputTypesAnnotation(): Boolean = fun hasOnlyInputTypesAnnotation(): Boolean =
originalTypeParameter.hasOnlyInputTypesAnnotation() originalTypeParameter.hasOnlyInputTypesAnnotation()
} }
interface CallHandle { interface CallHandle {
object NONE : CallHandle object NONE : CallHandle
} }
class CallBasedCallHandle(val call: Call): CallHandle { class CallBasedCallHandle(val call: Call) : CallHandle {
override fun equals(other: Any?) = override fun equals(other: Any?) =
other is CallBasedCallHandle && call === other.call other is CallBasedCallHandle && call === other.call
override fun hashCode() = override fun hashCode() =
System.identityHashCode(call) System.identityHashCode(call)
override fun toString() = override fun toString() =
call.toString() call.toString()
} }
fun Call.toHandle(): CallHandle = CallBasedCallHandle(this) fun Call.toHandle(): CallHandle = CallBasedCallHandle(this)
@@ -32,11 +32,12 @@ import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
import java.util.* import java.util.*
data class ConstraintContext( data class ConstraintContext(
val position: ConstraintPosition, val position: ConstraintPosition,
// see TypeBounds.Bound.derivedFrom // see TypeBounds.Bound.derivedFrom
val derivedFrom: Set<TypeVariable>? = null, val derivedFrom: Set<TypeVariable>? = null,
val initial: Boolean = false, val initial: Boolean = false,
val initialReduction: Boolean = false) val initialReduction: Boolean = false
)
fun ConstraintSystemBuilderImpl.incorporateBound(newBound: Bound) { fun ConstraintSystemBuilderImpl.incorporateBound(newBound: Bound) {
val typeVariable = newBound.typeVariable val typeVariable = newBound.typeVariable
@@ -23,7 +23,7 @@ interface ArgumentMapping {
fun isError(): Boolean fun isError(): Boolean
} }
object ArgumentUnmapped: ArgumentMapping { object ArgumentUnmapped : ArgumentMapping {
override fun isError(): Boolean = true override fun isError(): Boolean = true
} }
@@ -46,7 +46,7 @@ interface ArgumentMatch : ArgumentMapping {
override fun isError(): Boolean = status.isError override fun isError(): Boolean = status.isError
} }
class ArgumentMatchImpl(override val valueParameter: ValueParameterDescriptor): ArgumentMatch { class ArgumentMatchImpl(override val valueParameter: ValueParameterDescriptor) : ArgumentMatch {
private var _status: ArgumentMatchStatus? = null private var _status: ArgumentMatchStatus? = null
override val status: ArgumentMatchStatus override val status: ArgumentMatchStatus
@@ -64,5 +64,4 @@ class ArgumentMatchImpl(override val valueParameter: ValueParameterDescriptor):
} }
//TODO: temporary hack until status.isSuccess is not always correct //TODO: temporary hack until status.isSuccess is not always correct
fun ResolvedCall<*>.isReallySuccess(): Boolean fun ResolvedCall<*>.isReallySuccess(): Boolean = status.isSuccess && !ErrorUtils.isError(resultingDescriptor)
= status.isSuccess && !ErrorUtils.isError(resultingDescriptor)
@@ -32,8 +32,8 @@ interface VariableAsFunctionMutableResolvedCall : VariableAsFunctionResolvedCall
} }
class VariableAsFunctionResolvedCallImpl( class VariableAsFunctionResolvedCallImpl(
override val functionCall: MutableResolvedCall<FunctionDescriptor>, override val functionCall: MutableResolvedCall<FunctionDescriptor>,
override val variableCall: MutableResolvedCall<VariableDescriptor> override val variableCall: MutableResolvedCall<VariableDescriptor>
) : VariableAsFunctionMutableResolvedCall, MutableResolvedCall<FunctionDescriptor> by functionCall { ) : VariableAsFunctionMutableResolvedCall, MutableResolvedCall<FunctionDescriptor> by functionCall {
override fun markCallAsCompleted() { override fun markCallAsCompleted() {
@@ -28,9 +28,11 @@ private val KotlinType.immanentNullability: Nullability
* This class describes an arbitrary object which has some value in data flow analysis. * This class describes an arbitrary object which has some value in data flow analysis.
* In general case it's some r-value. * In general case it's some r-value.
*/ */
class DataFlowValue(val identifierInfo: IdentifierInfo, class DataFlowValue(
val type: KotlinType, val identifierInfo: IdentifierInfo,
val immanentNullability: Nullability = type.immanentNullability) { val type: KotlinType,
val immanentNullability: Nullability = type.immanentNullability
) {
val kind: Kind get() = identifierInfo.kind val kind: Kind get() = identifierInfo.kind
@@ -379,7 +379,7 @@ object DataFlowValueFactory {
// Local variable classification: STABLE or CAPTURED // Local variable classification: STABLE or CAPTURED
val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext) val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext)
// A case when we just analyse an expression alone: counts as captured // A case when we just analyse an expression alone: counts as captured
?: return CAPTURED_VARIABLE ?: return CAPTURED_VARIABLE
// Analyze who writes variable // Analyze who writes variable
@@ -30,18 +30,18 @@ import org.jetbrains.kotlin.utils.newLinkedHashSetWithExpectedSize
import java.util.* import java.util.*
internal class DelegatingDataFlowInfo private constructor( internal class DelegatingDataFlowInfo private constructor(
private val parent: DataFlowInfo?, private val parent: DataFlowInfo?,
private val nullabilityInfo: Map<DataFlowValue, Nullability>, private val nullabilityInfo: Map<DataFlowValue, Nullability>,
// Also immutable // Also immutable
private val typeInfo: SetMultimap<DataFlowValue, KotlinType>, private val typeInfo: SetMultimap<DataFlowValue, KotlinType>,
/** /**
* Value for which type info was cleared or reassigned at this point * Value for which type info was cleared or reassigned at this point
* so parent type info should not be in use * so parent type info should not be in use
*/ */
private val valueWithGivenTypeInfo: DataFlowValue? private val valueWithGivenTypeInfo: DataFlowValue?
) : DataFlowInfo { ) : DataFlowInfo {
constructor(): this(null, emptyMap(), newTypeInfo(), null) constructor() : this(null, emptyMap(), newTypeInfo(), null)
override val completeNullabilityInfo: Map<DataFlowValue, Nullability> override val completeNullabilityInfo: Map<DataFlowValue, Nullability>
get() { get() {
@@ -80,23 +80,22 @@ internal class DelegatingDataFlowInfo private constructor(
override fun getStableNullability(key: DataFlowValue) = getNullability(key, true) override fun getStableNullability(key: DataFlowValue) = getNullability(key, true)
private fun getNullability(key: DataFlowValue, stableOnly: Boolean) = private fun getNullability(key: DataFlowValue, stableOnly: Boolean) =
if (stableOnly && !key.isStable) { if (stableOnly && !key.isStable) {
key.immanentNullability key.immanentNullability
} } else {
else { nullabilityInfo[key] ?: parent?.getCollectedNullability(key) ?: key.immanentNullability
nullabilityInfo[key] ?: parent?.getCollectedNullability(key) ?: key.immanentNullability }
}
private fun putNullabilityAndTypeInfo( private fun putNullabilityAndTypeInfo(
map: MutableMap<DataFlowValue, Nullability>, map: MutableMap<DataFlowValue, Nullability>,
value: DataFlowValue, value: DataFlowValue,
nullability: Nullability, nullability: Nullability,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
typeInfo: SetMultimap<DataFlowValue, KotlinType>? = null, typeInfo: SetMultimap<DataFlowValue, KotlinType>? = null,
affectReceiver: Boolean = true, affectReceiver: Boolean = true,
// TODO: remove me in version 1.3! I'm very dirty hack! // TODO: remove me in version 1.3! I'm very dirty hack!
// In normal circumstances this should be always true // In normal circumstances this should be always true
recordUnstable: Boolean = true recordUnstable: Boolean = true
): Boolean { ): Boolean {
if (value.isStable || recordUnstable) { if (value.isStable || recordUnstable) {
map.put(value, nullability) map.put(value, nullability)
@@ -110,8 +109,10 @@ internal class DelegatingDataFlowInfo private constructor(
val receiverType = identifierInfo.receiverType val receiverType = identifierInfo.receiverType
if (identifierInfo.safe && receiverType != null) { if (identifierInfo.safe && receiverType != null) {
val receiverValue = DataFlowValue(identifierInfo.receiverInfo, receiverType) val receiverValue = DataFlowValue(identifierInfo.receiverInfo, receiverType)
putNullabilityAndTypeInfo(map, receiverValue, nullability, putNullabilityAndTypeInfo(
languageVersionSettings, typeInfo, recordUnstable = recordUnstable) map, receiverValue, nullability,
languageVersionSettings, typeInfo, recordUnstable = recordUnstable
)
} }
} }
is IdentifierInfo.SafeCast -> { is IdentifierInfo.SafeCast -> {
@@ -121,16 +122,20 @@ internal class DelegatingDataFlowInfo private constructor(
languageVersionSettings.supportsFeature(LanguageFeature.SafeCastCheckBoundSmartCasts)) { languageVersionSettings.supportsFeature(LanguageFeature.SafeCastCheckBoundSmartCasts)) {
val subjectValue = DataFlowValue(identifierInfo.subjectInfo, subjectType) val subjectValue = DataFlowValue(identifierInfo.subjectInfo, subjectType)
putNullabilityAndTypeInfo(map, subjectValue, nullability, putNullabilityAndTypeInfo(
languageVersionSettings, typeInfo, recordUnstable = false) map, subjectValue, nullability,
languageVersionSettings, typeInfo, recordUnstable = false
)
if (subjectValue.isStable) { if (subjectValue.isStable) {
typeInfo?.put(subjectValue, targetType) typeInfo?.put(subjectValue, targetType)
} }
} }
} }
is IdentifierInfo.Variable -> identifierInfo.bound?.let { is IdentifierInfo.Variable -> identifierInfo.bound?.let {
putNullabilityAndTypeInfo(map, it, nullability, putNullabilityAndTypeInfo(
languageVersionSettings, typeInfo, recordUnstable = recordUnstable) map, it, nullability,
languageVersionSettings, typeInfo, recordUnstable = recordUnstable
)
} }
} }
} }
@@ -139,12 +144,12 @@ internal class DelegatingDataFlowInfo private constructor(
} }
override fun getCollectedTypes(key: DataFlowValue, languageVersionSettings: LanguageVersionSettings) = override fun getCollectedTypes(key: DataFlowValue, languageVersionSettings: LanguageVersionSettings) =
getCollectedTypes(key, true, languageVersionSettings) getCollectedTypes(key, true, languageVersionSettings)
private fun getCollectedTypes( private fun getCollectedTypes(
key: DataFlowValue, key: DataFlowValue,
enrichWithNotNull: Boolean, enrichWithNotNull: Boolean,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): Set<KotlinType> { ): Set<KotlinType> {
val types = collectTypesFromMeAndParents(key, languageVersionSettings) val types = collectTypesFromMeAndParents(key, languageVersionSettings)
if (!enrichWithNotNull || getCollectedNullability(key).canBeNull()) { if (!enrichWithNotNull || getCollectedNullability(key).canBeNull()) {
@@ -164,10 +169,10 @@ internal class DelegatingDataFlowInfo private constructor(
} }
override fun getStableTypes(key: DataFlowValue, languageVersionSettings: LanguageVersionSettings) = override fun getStableTypes(key: DataFlowValue, languageVersionSettings: LanguageVersionSettings) =
getStableTypes(key, true, languageVersionSettings) getStableTypes(key, true, languageVersionSettings)
private fun getStableTypes(key: DataFlowValue, enrichWithNotNull: Boolean, languageVersionSettings: LanguageVersionSettings) = private fun getStableTypes(key: DataFlowValue, enrichWithNotNull: Boolean, languageVersionSettings: LanguageVersionSettings) =
if (!key.isStable) LinkedHashSet() else getCollectedTypes(key, enrichWithNotNull, languageVersionSettings) if (!key.isStable) LinkedHashSet() else getCollectedTypes(key, enrichWithNotNull, languageVersionSettings)
private fun KotlinType.canBeDefinitelyNotNullOrNotNull(settings: LanguageVersionSettings): Boolean { private fun KotlinType.canBeDefinitelyNotNullOrNotNull(settings: LanguageVersionSettings): Boolean {
return if (settings.supportsFeature(LanguageFeature.NewInference)) return if (settings.supportsFeature(LanguageFeature.NewInference))
@@ -182,6 +187,7 @@ internal class DelegatingDataFlowInfo private constructor(
else else
TypeUtils.makeNotNullable(this) TypeUtils.makeNotNullable(this)
} }
/** /**
* Call this function to clear all data flow information about * Call this function to clear all data flow information about
* the given data flow value. * the given data flow value.
@@ -214,7 +220,7 @@ internal class DelegatingDataFlowInfo private constructor(
} }
override fun equate( override fun equate(
a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo { ): DataFlowInfo {
val resultNullabilityInfo = hashMapOf<DataFlowValue, Nullability>() val resultNullabilityInfo = hashMapOf<DataFlowValue, Nullability>()
val nullabilityOfA = getStableNullability(a) val nullabilityOfA = getStableNullability(a)
@@ -222,8 +228,20 @@ internal class DelegatingDataFlowInfo private constructor(
val newTypeInfo = newTypeInfo() val newTypeInfo = newTypeInfo()
var changed = var changed =
putNullabilityAndTypeInfo(resultNullabilityInfo, a, nullabilityOfA.refine(nullabilityOfB), languageVersionSettings, newTypeInfo) or putNullabilityAndTypeInfo(
putNullabilityAndTypeInfo(resultNullabilityInfo, b, nullabilityOfB.refine(nullabilityOfA), languageVersionSettings, newTypeInfo) resultNullabilityInfo,
a,
nullabilityOfA.refine(nullabilityOfB),
languageVersionSettings,
newTypeInfo
) or
putNullabilityAndTypeInfo(
resultNullabilityInfo,
b,
nullabilityOfB.refine(nullabilityOfA),
languageVersionSettings,
newTypeInfo
)
// NB: == has no guarantees of type equality, see KT-11280 for the example // NB: == has no guarantees of type equality, see KT-11280 for the example
if (identityEquals || !nullabilityOfA.canBeNonNull() || !nullabilityOfB.canBeNonNull()) { if (identityEquals || !nullabilityOfA.canBeNonNull() || !nullabilityOfB.canBeNonNull()) {
@@ -252,8 +270,7 @@ internal class DelegatingDataFlowInfo private constructor(
if (current is DelegatingDataFlowInfo) { if (current is DelegatingDataFlowInfo) {
types.addAll(current.typeInfo.get(value)) types.addAll(current.typeInfo.get(value))
current = if (value == current.valueWithGivenTypeInfo) null else current.parent current = if (value == current.valueWithGivenTypeInfo) null else current.parent
} } else {
else {
types.addAll(current.getCollectedTypes(value, languageVersionSettings)) types.addAll(current.getCollectedTypes(value, languageVersionSettings))
break break
} }
@@ -263,7 +280,7 @@ internal class DelegatingDataFlowInfo private constructor(
} }
override fun disequate( override fun disequate(
a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo { ): DataFlowInfo {
val resultNullabilityInfo = hashMapOf<DataFlowValue, Nullability>() val resultNullabilityInfo = hashMapOf<DataFlowValue, Nullability>()
val nullabilityOfA = getStableNullability(a) val nullabilityOfA = getStableNullability(a)
@@ -271,15 +288,27 @@ internal class DelegatingDataFlowInfo private constructor(
val newTypeInfo = newTypeInfo() val newTypeInfo = newTypeInfo()
val changed = val changed =
putNullabilityAndTypeInfo(resultNullabilityInfo, a, nullabilityOfA.refine(nullabilityOfB.invert()), languageVersionSettings, newTypeInfo) or putNullabilityAndTypeInfo(
putNullabilityAndTypeInfo(resultNullabilityInfo, b, nullabilityOfB.refine(nullabilityOfA.invert()), languageVersionSettings, newTypeInfo) resultNullabilityInfo,
a,
nullabilityOfA.refine(nullabilityOfB.invert()),
languageVersionSettings,
newTypeInfo
) or
putNullabilityAndTypeInfo(
resultNullabilityInfo,
b,
nullabilityOfB.refine(nullabilityOfA.invert()),
languageVersionSettings,
newTypeInfo
)
return if (changed) create(this, resultNullabilityInfo, if (newTypeInfo.isEmpty) EMPTY_TYPE_INFO else newTypeInfo) else this return if (changed) create(this, resultNullabilityInfo, if (newTypeInfo.isEmpty) EMPTY_TYPE_INFO else newTypeInfo) else this
} }
override fun establishSubtyping( override fun establishSubtyping(
value: DataFlowValue, type: KotlinType, languageVersionSettings: LanguageVersionSettings value: DataFlowValue, type: KotlinType, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo { ): DataFlowInfo {
if (value.type == type) return this if (value.type == type) return this
if (getCollectedTypes(value, languageVersionSettings).contains(type)) return this if (getCollectedTypes(value, languageVersionSettings).contains(type)) return this
@@ -321,11 +350,11 @@ internal class DelegatingDataFlowInfo private constructor(
private fun Set<KotlinType>.containsNothing() = any { KotlinBuiltIns.isNothing(it) } private fun Set<KotlinType>.containsNothing() = any { KotlinBuiltIns.isNothing(it) }
private fun Set<KotlinType>.intersectConsideringNothing(other: Set<KotlinType>) = private fun Set<KotlinType>.intersectConsideringNothing(other: Set<KotlinType>) =
when { when {
other.containsNothing() -> this other.containsNothing() -> this
this.containsNothing() -> other this.containsNothing() -> other
else -> this.intersect(other) else -> this.intersect(other)
} }
override fun or(other: DataFlowInfo): DataFlowInfo { override fun or(other: DataFlowInfo): DataFlowInfo {
if (other === DataFlowInfo.EMPTY) return DataFlowInfo.EMPTY if (other === DataFlowInfo.EMPTY) return DataFlowInfo.EMPTY
@@ -358,15 +387,16 @@ internal class DelegatingDataFlowInfo private constructor(
private val EMPTY_TYPE_INFO = newTypeInfo() private val EMPTY_TYPE_INFO = newTypeInfo()
private fun containsAll(first: SetMultimap<DataFlowValue, KotlinType>, second: SetMultimap<DataFlowValue, KotlinType>) = private fun containsAll(first: SetMultimap<DataFlowValue, KotlinType>, second: SetMultimap<DataFlowValue, KotlinType>) =
first.entries().containsAll(second.entries()) first.entries().containsAll(second.entries())
fun newTypeInfo(): SetMultimap<DataFlowValue, KotlinType> = LinkedHashMultimap.create<DataFlowValue, KotlinType>() fun newTypeInfo(): SetMultimap<DataFlowValue, KotlinType> = LinkedHashMultimap.create<DataFlowValue, KotlinType>()
private fun create(parent: DataFlowInfo?, private fun create(
nullabilityInfo: Map<DataFlowValue, Nullability>, parent: DataFlowInfo?,
// NB: typeInfo must be mutable here! nullabilityInfo: Map<DataFlowValue, Nullability>,
typeInfo: SetMultimap<DataFlowValue, KotlinType>, // NB: typeInfo must be mutable here!
valueWithGivenTypeInfo: DataFlowValue? = null typeInfo: SetMultimap<DataFlowValue, KotlinType>,
valueWithGivenTypeInfo: DataFlowValue? = null
): DataFlowInfo { ): DataFlowInfo {
val toDelete = newTypeInfo() val toDelete = newTypeInfo()
for (value in typeInfo.keys()) { for (value in typeInfo.keys()) {
@@ -81,33 +81,32 @@ class DynamicCallableDescriptors(storageManager: StorageManager, builtIns: Kotli
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> { override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
return if (call.valueArgumentList == null && call.valueArguments.isEmpty()) { return if (call.valueArgumentList == null && call.valueArguments.isEmpty()) {
listOf(createDynamicProperty(owner, name, call)) listOf(createDynamicProperty(owner, name, call))
} } else listOf()
else listOf()
} }
} }
private fun createDynamicProperty(owner: DeclarationDescriptor, name: Name, call: Call): PropertyDescriptorImpl { private fun createDynamicProperty(owner: DeclarationDescriptor, name: Name, call: Call): PropertyDescriptorImpl {
val propertyDescriptor = PropertyDescriptorImpl.create( val propertyDescriptor = PropertyDescriptorImpl.create(
owner, owner,
Annotations.EMPTY, Annotations.EMPTY,
Modality.FINAL, Modality.FINAL,
Visibilities.PUBLIC, Visibilities.PUBLIC,
true, true,
name, name,
CallableMemberDescriptor.Kind.DECLARATION, CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE, SourceElement.NO_SOURCE,
/* lateInit = */ false, /* lateInit = */ false,
/* isConst = */ false, /* isConst = */ false,
/* isExpect = */ false, /* isExpect = */ false,
/* isActual = */ false, /* isActual = */ false,
/* isExternal = */ false, /* isExternal = */ false,
/* isDelegated = */ false /* isDelegated = */ false
) )
propertyDescriptor.setType( propertyDescriptor.setType(
dynamicType, dynamicType,
createTypeParameters(propertyDescriptor, call), createTypeParameters(propertyDescriptor, call),
createDynamicDispatchReceiverParameter(propertyDescriptor), createDynamicDispatchReceiverParameter(propertyDescriptor),
null as KotlinType? null as KotlinType?
) )
val getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY) val getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY)
@@ -121,20 +120,20 @@ class DynamicCallableDescriptors(storageManager: StorageManager, builtIns: Kotli
private fun createDynamicFunction(owner: DeclarationDescriptor, name: Name, call: Call): SimpleFunctionDescriptorImpl { private fun createDynamicFunction(owner: DeclarationDescriptor, name: Name, call: Call): SimpleFunctionDescriptorImpl {
val functionDescriptor = SimpleFunctionDescriptorImpl.create( val functionDescriptor = SimpleFunctionDescriptorImpl.create(
owner, owner,
Annotations.EMPTY, Annotations.EMPTY,
name, name,
CallableMemberDescriptor.Kind.DECLARATION, CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
) )
functionDescriptor.initialize( functionDescriptor.initialize(
null, null,
createDynamicDispatchReceiverParameter(functionDescriptor), createDynamicDispatchReceiverParameter(functionDescriptor),
createTypeParameters(functionDescriptor, call), createTypeParameters(functionDescriptor, call),
createValueParameters(functionDescriptor, call), createValueParameters(functionDescriptor, call),
dynamicType, dynamicType,
Modality.FINAL, Modality.FINAL,
Visibilities.PUBLIC Visibilities.PUBLIC
) )
functionDescriptor.setHasSynthesizedParameterNames(true) functionDescriptor.setHasSynthesizedParameterNames(true)
return functionDescriptor return functionDescriptor
@@ -144,26 +143,27 @@ class DynamicCallableDescriptors(storageManager: StorageManager, builtIns: Kotli
return ReceiverParameterDescriptorImpl(owner, TransientReceiver(dynamicType)) return ReceiverParameterDescriptorImpl(owner, TransientReceiver(dynamicType))
} }
private fun createTypeParameters(owner: DeclarationDescriptor, call: Call): List<TypeParameterDescriptor> = call.typeArguments.indices.map { private fun createTypeParameters(owner: DeclarationDescriptor, call: Call): List<TypeParameterDescriptor> =
index call.typeArguments.indices.map { index
-> ->
TypeParameterDescriptorImpl.createWithDefaultBound( TypeParameterDescriptorImpl.createWithDefaultBound(
owner, owner,
Annotations.EMPTY, Annotations.EMPTY,
false, false,
Variance.INVARIANT, Variance.INVARIANT,
Name.identifier("T$index"), Name.identifier("T$index"),
index index
) )
} }
private fun createValueParameters(owner: FunctionDescriptor, call: Call): List<ValueParameterDescriptor> { private fun createValueParameters(owner: FunctionDescriptor, call: Call): List<ValueParameterDescriptor> {
val parameters = ArrayList<ValueParameterDescriptor>() val parameters = ArrayList<ValueParameterDescriptor>()
fun addParameter(arg : ValueArgument, outType: KotlinType, varargElementType: KotlinType?) { fun addParameter(arg: ValueArgument, outType: KotlinType, varargElementType: KotlinType?) {
val index = parameters.size val index = parameters.size
parameters.add(ValueParameterDescriptorImpl( parameters.add(
ValueParameterDescriptorImpl(
owner, owner,
null, null,
index, index,
@@ -175,7 +175,8 @@ class DynamicCallableDescriptors(storageManager: StorageManager, builtIns: Kotli
/* isNoinline = */ false, /* isNoinline = */ false,
varargElementType, varargElementType,
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
)) )
)
} }
fun getFunctionType(funLiteralExpr: KtLambdaExpression): KotlinType { fun getFunctionType(funLiteralExpr: KtLambdaExpression): KotlinType {
@@ -55,14 +55,14 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinResolutionCallbacksImpl( class KotlinResolutionCallbacksImpl(
topLevelCallContext: BasicCallResolutionContext, topLevelCallContext: BasicCallResolutionContext,
val expressionTypingServices: ExpressionTypingServices, val expressionTypingServices: ExpressionTypingServices,
val typeApproximator: TypeApproximator, val typeApproximator: TypeApproximator,
val argumentTypeResolver: ArgumentTypeResolver, val argumentTypeResolver: ArgumentTypeResolver,
val languageVersionSettings: LanguageVersionSettings, val languageVersionSettings: LanguageVersionSettings,
val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer, val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer,
val constantExpressionEvaluator: ConstantExpressionEvaluator val constantExpressionEvaluator: ConstantExpressionEvaluator
): KotlinResolutionCallbacks { ) : KotlinResolutionCallbacks {
val trace: BindingTrace = topLevelCallContext.trace val trace: BindingTrace = topLevelCallContext.trace
class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) { class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) {
@@ -75,35 +75,42 @@ class KotlinResolutionCallbacksImpl(
} }
override fun analyzeAndGetLambdaReturnArguments( override fun analyzeAndGetLambdaReturnArguments(
lambdaArgument: LambdaKotlinCallArgument, lambdaArgument: LambdaKotlinCallArgument,
isSuspend: Boolean, isSuspend: Boolean,
receiverType: UnwrappedType?, receiverType: UnwrappedType?,
parameters: List<UnwrappedType>, parameters: List<UnwrappedType>,
expectedReturnType: UnwrappedType? expectedReturnType: UnwrappedType?
): List<SimpleKotlinCallArgument> { ): List<SimpleKotlinCallArgument> {
val psiCallArgument = lambdaArgument.psiCallArgument as PSIFunctionKotlinCallArgument val psiCallArgument = lambdaArgument.psiCallArgument as PSIFunctionKotlinCallArgument
val outerCallContext = psiCallArgument.outerCallContext val outerCallContext = psiCallArgument.outerCallContext
fun createCallArgument(ktExpression: KtExpression, typeInfo: KotlinTypeInfo) = fun createCallArgument(ktExpression: KtExpression, typeInfo: KotlinTypeInfo) =
createSimplePSICallArgument(trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor, createSimplePSICallArgument(
CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo, languageVersionSettings) trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor,
CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo, languageVersionSettings
)
val lambdaInfo = LambdaInfo(expectedReturnType ?: TypeUtils.NO_EXPECTED_TYPE, val lambdaInfo = LambdaInfo(
if (expectedReturnType == null) ContextDependency.DEPENDENT else ContextDependency.INDEPENDENT) expectedReturnType ?: TypeUtils.NO_EXPECTED_TYPE,
if (expectedReturnType == null) ContextDependency.DEPENDENT else ContextDependency.INDEPENDENT
)
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, lambdaInfo) trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, lambdaInfo)
val builtIns = outerCallContext.scope.ownerDescriptor.builtIns val builtIns = outerCallContext.scope.ownerDescriptor.builtIns
val expectedType = createFunctionType(builtIns, Annotations.EMPTY, receiverType, parameters, null, val expectedType = createFunctionType(
lambdaInfo.expectedType, isSuspend) builtIns, Annotations.EMPTY, receiverType, parameters, null,
lambdaInfo.expectedType, isSuspend
)
val approximatesExpectedType = typeApproximator.approximateToSubType(expectedType, TypeApproximatorConfiguration.LocalDeclaration) ?: expectedType val approximatesExpectedType =
typeApproximator.approximateToSubType(expectedType, TypeApproximatorConfiguration.LocalDeclaration) ?: expectedType
val actualContext = outerCallContext val actualContext = outerCallContext
.replaceBindingTrace(trace) .replaceBindingTrace(trace)
.replaceContextDependency(lambdaInfo.contextDependency) .replaceContextDependency(lambdaInfo.contextDependency)
.replaceExpectedType(approximatesExpectedType) .replaceExpectedType(approximatesExpectedType)
.replaceDataFlowInfo(psiCallArgument.lambdaInitialDataFlowInfo) .replaceDataFlowInfo(psiCallArgument.lambdaInitialDataFlowInfo)
val functionTypeInfo = expressionTypingServices.getTypeInfo(psiCallArgument.expression, actualContext) val functionTypeInfo = expressionTypingServices.getTypeInfo(psiCallArgument.expression, actualContext)
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, LambdaInfo.STUB_EMPTY) trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, LambdaInfo.STUB_EMPTY)
@@ -113,11 +120,10 @@ class KotlinResolutionCallbacksImpl(
val returnedExpression = expression.returnedExpression val returnedExpression = expression.returnedExpression
if (returnedExpression != null) { if (returnedExpression != null) {
createCallArgument( createCallArgument(
returnedExpression, returnedExpression,
typeInfo ?: throw AssertionError("typeInfo should be non-null for return with expression") typeInfo ?: throw AssertionError("typeInfo should be non-null for return with expression")
) )
} } else {
else {
hasReturnWithoutExpression = true hasReturnWithoutExpression = true
EmptyLabeledReturn(expression, builtIns) EmptyLabeledReturn(expression, builtIns)
} }
@@ -141,8 +147,7 @@ class KotlinResolutionCallbacksImpl(
val lastExpression: KtExpression? val lastExpression: KtExpression?
if (psiCallArgument is LambdaKotlinCallArgumentImpl) { if (psiCallArgument is LambdaKotlinCallArgumentImpl) {
lastExpression = psiCallArgument.ktLambdaExpression.bodyExpression?.statements?.lastOrNull() lastExpression = psiCallArgument.ktLambdaExpression.bodyExpression?.statements?.lastOrNull()
} } else {
else {
lastExpression = (psiCallArgument as FunctionExpressionImpl).ktFunction.bodyExpression?.lastBlockStatementOrThis() lastExpression = (psiCallArgument as FunctionExpressionImpl).ktFunction.bodyExpression?.lastBlockStatementOrThis()
} }
@@ -159,11 +164,11 @@ class KotlinResolutionCallbacksImpl(
val expression = psiKotlinCall.psiCall.callElement.safeAs<KtExpression>() ?: return null val expression = psiKotlinCall.psiCall.callElement.safeAs<KtExpression>() ?: return null
return transformToReceiverWithSmartCastInfo( return transformToReceiverWithSmartCastInfo(
resolvedAtom.candidateDescriptor, resolvedAtom.candidateDescriptor,
trace.bindingContext, trace.bindingContext,
psiKotlinCall.resultDataFlowInfo, psiKotlinCall.resultDataFlowInfo,
ExpressionReceiver.create(expression, returnType, trace.bindingContext), ExpressionReceiver.create(expression, returnType, trace.bindingContext),
languageVersionSettings languageVersionSettings
) )
} }
@@ -179,8 +184,8 @@ class KotlinResolutionCallbacksImpl(
val expression = findCommonParent(callElement, resolvedAtom.atom.psiKotlinCall.explicitReceiver) val expression = findCommonParent(callElement, resolvedAtom.atom.psiKotlinCall.explicitReceiver)
val temporaryBindingTrace = TemporaryBindingTrace.create( val temporaryBindingTrace = TemporaryBindingTrace.create(
trace, trace,
"Trace to check if some expression is constant, we have to avoid writing probably wrong COMPILE_TIME_VALUE slice" "Trace to check if some expression is constant, we have to avoid writing probably wrong COMPILE_TIME_VALUE slice"
) )
return constantExpressionEvaluator.evaluateExpression(expression, temporaryBindingTrace, expectedType) != null return constantExpressionEvaluator.evaluateExpression(expression, temporaryBindingTrace, expectedType) != null
} }
@@ -61,39 +61,45 @@ import java.lang.IllegalStateException
import java.util.* import java.util.*
class NewResolutionOldInference( class NewResolutionOldInference(
private val candidateResolver: CandidateResolver, private val candidateResolver: CandidateResolver,
private val towerResolver: TowerResolver, private val towerResolver: TowerResolver,
private val resolutionResultsHandler: ResolutionResultsHandler, private val resolutionResultsHandler: ResolutionResultsHandler,
private val dynamicCallableDescriptors: DynamicCallableDescriptors, private val dynamicCallableDescriptors: DynamicCallableDescriptors,
private val syntheticScopes: SyntheticScopes, private val syntheticScopes: SyntheticScopes,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val coroutineInferenceSupport: CoroutineInferenceSupport, private val coroutineInferenceSupport: CoroutineInferenceSupport,
private val deprecationResolver: DeprecationResolver private val deprecationResolver: DeprecationResolver
) { ) {
sealed class ResolutionKind<D : CallableDescriptor>(val kotlinCallKind: KotlinCallKind = KotlinCallKind.UNSUPPORTED) { sealed class ResolutionKind<D : CallableDescriptor>(val kotlinCallKind: KotlinCallKind = KotlinCallKind.UNSUPPORTED) {
abstract internal fun createTowerProcessor( abstract internal fun createTowerProcessor(
outer: NewResolutionOldInference, outer: NewResolutionOldInference,
name: Name, name: Name,
tracing: TracingStrategy, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, scopeTower: ImplicitScopeTower,
explicitReceiver: DetailedReceiver?, explicitReceiver: DetailedReceiver?,
context: BasicCallResolutionContext context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> ): ScopeTowerProcessor<MyCandidate>
object Function : ResolutionKind<FunctionDescriptor>(KotlinCallKind.FUNCTION) { object Function : ResolutionKind<FunctionDescriptor>(KotlinCallKind.FUNCTION) {
override fun createTowerProcessor( override fun createTowerProcessor(
outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> { ): ScopeTowerProcessor<MyCandidate> {
val functionFactory = outer.CandidateFactoryImpl(name, context, tracing) val functionFactory = outer.CandidateFactoryImpl(name, context, tracing)
return createFunctionProcessor(scopeTower, name, functionFactory, outer.CandidateFactoryProviderForInvokeImpl(functionFactory), explicitReceiver) return createFunctionProcessor(
scopeTower,
name,
functionFactory,
outer.CandidateFactoryProviderForInvokeImpl(functionFactory),
explicitReceiver
)
} }
} }
object Variable : ResolutionKind<VariableDescriptor>(KotlinCallKind.VARIABLE) { object Variable : ResolutionKind<VariableDescriptor>(KotlinCallKind.VARIABLE) {
override fun createTowerProcessor( override fun createTowerProcessor(
outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> { ): ScopeTowerProcessor<MyCandidate> {
val variableFactory = outer.CandidateFactoryImpl(name, context, tracing) val variableFactory = outer.CandidateFactoryImpl(name, context, tracing)
return createVariableAndObjectProcessor(scopeTower, name, variableFactory, explicitReceiver) return createVariableAndObjectProcessor(scopeTower, name, variableFactory, explicitReceiver)
@@ -102,22 +108,22 @@ class NewResolutionOldInference(
object CallableReference : ResolutionKind<CallableDescriptor>() { object CallableReference : ResolutionKind<CallableDescriptor>() {
override fun createTowerProcessor( override fun createTowerProcessor(
outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> { ): ScopeTowerProcessor<MyCandidate> {
val functionFactory = outer.CandidateFactoryImpl(name, context, tracing) val functionFactory = outer.CandidateFactoryImpl(name, context, tracing)
val variableFactory = outer.CandidateFactoryImpl(name, context, tracing) val variableFactory = outer.CandidateFactoryImpl(name, context, tracing)
return PrioritizedCompositeScopeTowerProcessor( return PrioritizedCompositeScopeTowerProcessor(
createSimpleFunctionProcessor(scopeTower, name, functionFactory, explicitReceiver, classValueReceiver = false), createSimpleFunctionProcessor(scopeTower, name, functionFactory, explicitReceiver, classValueReceiver = false),
createVariableProcessor(scopeTower, name, variableFactory, explicitReceiver, classValueReceiver = false) createVariableProcessor(scopeTower, name, variableFactory, explicitReceiver, classValueReceiver = false)
) )
} }
} }
object Invoke : ResolutionKind<FunctionDescriptor>() { object Invoke : ResolutionKind<FunctionDescriptor>() {
override fun createTowerProcessor( override fun createTowerProcessor(
outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> { ): ScopeTowerProcessor<MyCandidate> {
val functionFactory = outer.CandidateFactoryImpl(name, context, tracing) val functionFactory = outer.CandidateFactoryImpl(name, context, tracing)
// todo // todo
@@ -125,7 +131,12 @@ class NewResolutionOldInference(
"Call should be CallForImplicitInvoke, but it is: ${context.call}" "Call should be CallForImplicitInvoke, but it is: ${context.call}"
} }
return createProcessorWithReceiverValueOrEmpty(explicitReceiver) { return createProcessorWithReceiverValueOrEmpty(explicitReceiver) {
createCallTowerProcessorForExplicitInvoke(scopeTower, functionFactory, context.transformToReceiverWithSmartCastInfo(call.dispatchReceiver), it) createCallTowerProcessorForExplicitInvoke(
scopeTower,
functionFactory,
context.transformToReceiverWithSmartCastInfo(call.dispatchReceiver),
it
)
} }
} }
@@ -133,8 +144,8 @@ class NewResolutionOldInference(
class GivenCandidates<D : CallableDescriptor> : ResolutionKind<D>() { class GivenCandidates<D : CallableDescriptor> : ResolutionKind<D>() {
override fun createTowerProcessor( override fun createTowerProcessor(
outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy,
scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext
): ScopeTowerProcessor<MyCandidate> { ): ScopeTowerProcessor<MyCandidate> {
throw IllegalStateException("Should be not called") throw IllegalStateException("Should be not called")
} }
@@ -142,16 +153,15 @@ class NewResolutionOldInference(
} }
fun <D : CallableDescriptor> runResolution( fun <D : CallableDescriptor> runResolution(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
name: Name, name: Name,
kind: ResolutionKind<D>, kind: ResolutionKind<D>,
tracing: TracingStrategy tracing: TracingStrategy
): OverloadResolutionResultsImpl<D> { ): OverloadResolutionResultsImpl<D> {
val explicitReceiver = context.call.explicitReceiver val explicitReceiver = context.call.explicitReceiver
val detailedReceiver = if (explicitReceiver is QualifierReceiver?) { val detailedReceiver = if (explicitReceiver is QualifierReceiver?) {
explicitReceiver explicitReceiver
} } else {
else {
context.transformToReceiverWithSmartCastInfo(explicitReceiver as ReceiverValue) context.transformToReceiverWithSmartCastInfo(explicitReceiver as ReceiverValue)
} }
@@ -171,15 +181,22 @@ class NewResolutionOldInference(
return allCandidatesResult(towerResolver.collectAllCandidates(scopeTower, processor, nameToResolve)) return allCandidatesResult(towerResolver.collectAllCandidates(scopeTower, processor, nameToResolve))
} }
var candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference, name = nameToResolve) var candidates =
towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference, name = nameToResolve)
// Temporary hack to resolve 'rem' as 'mod' if the first is do not present // Temporary hack to resolve 'rem' as 'mod' if the first is do not present
val emptyOrInapplicableCandidates = candidates.isEmpty() || val emptyOrInapplicableCandidates = candidates.isEmpty() ||
candidates.all { it.resultingApplicability.isInapplicable } candidates.all { it.resultingApplicability.isInapplicable }
if (isBinaryRemOperator && shouldUseOperatorRem && emptyOrInapplicableCandidates) { if (isBinaryRemOperator && shouldUseOperatorRem && emptyOrInapplicableCandidates) {
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name] val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name]
val processorForDeprecatedName = kind.createTowerProcessor(this, deprecatedName!!, tracing, scopeTower, detailedReceiver, context) val processorForDeprecatedName =
candidates = towerResolver.runResolve(scopeTower, processorForDeprecatedName, useOrder = kind != ResolutionKind.CallableReference, name = deprecatedName) kind.createTowerProcessor(this, deprecatedName!!, tracing, scopeTower, detailedReceiver, context)
candidates = towerResolver.runResolve(
scopeTower,
processorForDeprecatedName,
useOrder = kind != ResolutionKind.CallableReference,
name = deprecatedName
)
} }
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
@@ -194,9 +211,9 @@ class NewResolutionOldInference(
} }
fun <D : CallableDescriptor> runResolutionForGivenCandidates( fun <D : CallableDescriptor> runResolutionForGivenCandidates(
basicCallContext: BasicCallResolutionContext, basicCallContext: BasicCallResolutionContext,
tracing: TracingStrategy, tracing: TracingStrategy,
candidates: Collection<ResolutionCandidate<D>> candidates: Collection<ResolutionCandidate<D>>
): OverloadResolutionResultsImpl<D> { ): OverloadResolutionResultsImpl<D> {
val resolvedCandidates = candidates.map { candidate -> val resolvedCandidates = candidates.map { candidate ->
val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate") val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate")
@@ -207,8 +224,8 @@ class NewResolutionOldInference(
} }
val callCandidateResolutionContext = CallCandidateResolutionContext.create( val callCandidateResolutionContext = CallCandidateResolutionContext.create(
resolvedCall, basicCallContext, candidateTrace, tracing, basicCallContext.call, resolvedCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.EXIT_ON_FIRST_ERROR CandidateResolveMode.EXIT_ON_FIRST_ERROR
) )
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
@@ -219,27 +236,31 @@ class NewResolutionOldInference(
} }
} }
if (basicCallContext.collectAllCandidates) { if (basicCallContext.collectAllCandidates) {
val allCandidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolvedCandidates), val allCandidates = towerResolver.runWithEmptyTowerData(
TowerResolver.AllCandidatesCollector(), useOrder = false) KnownResultProcessor(resolvedCandidates),
TowerResolver.AllCandidatesCollector(), useOrder = false
)
return allCandidatesResult(allCandidates) return allCandidatesResult(allCandidates)
} }
val processedCandidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolvedCandidates), val processedCandidates = towerResolver.runWithEmptyTowerData(
TowerResolver.SuccessfulResultCollector(), useOrder = true) KnownResultProcessor(resolvedCandidates),
TowerResolver.SuccessfulResultCollector(), useOrder = true
)
return convertToOverloadResults(processedCandidates, tracing, basicCallContext, languageVersionSettings) return convertToOverloadResults(processedCandidates, tracing, basicCallContext, languageVersionSettings)
} }
private fun <D: CallableDescriptor> allCandidatesResult(allCandidates: Collection<MyCandidate>) private fun <D : CallableDescriptor> allCandidatesResult(allCandidates: Collection<MyCandidate>) =
= OverloadResolutionResultsImpl.nameNotFound<D>().apply { OverloadResolutionResultsImpl.nameNotFound<D>().apply {
this.allCandidates = allCandidates.map { it.resolvedCall as MutableResolvedCall<D> } this.allCandidates = allCandidates.map { it.resolvedCall as MutableResolvedCall<D> }
} }
private fun <D : CallableDescriptor> convertToOverloadResults( private fun <D : CallableDescriptor> convertToOverloadResults(
candidates: Collection<MyCandidate>, candidates: Collection<MyCandidate>,
tracing: TracingStrategy, tracing: TracingStrategy,
basicCallContext: BasicCallResolutionContext, basicCallContext: BasicCallResolutionContext,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): OverloadResolutionResultsImpl<D> { ): OverloadResolutionResultsImpl<D> {
val resolvedCalls = candidates.map { val resolvedCalls = candidates.map {
val (diagnostics, resolvedCall) = it val (diagnostics, resolvedCall) = it
@@ -260,8 +281,17 @@ class NewResolutionOldInference(
if (resolvedCall.status.possibleTransformToSuccess()) { if (resolvedCall.status.possibleTransformToSuccess()) {
for (error in diagnostics) { for (error in diagnostics) {
when (error) { when (error) {
is UnsupportedInnerClassCall -> resolvedCall.trace.report(Errors.UNSUPPORTED.on(resolvedCall.call.callElement, error.message)) is UnsupportedInnerClassCall -> resolvedCall.trace.report(
is NestedClassViaInstanceReference -> tracing.nestedClassAccessViaInstanceReference(resolvedCall.trace, error.classDescriptor, resolvedCall.explicitReceiverKind) Errors.UNSUPPORTED.on(
resolvedCall.call.callElement,
error.message
)
)
is NestedClassViaInstanceReference -> tracing.nestedClassAccessViaInstanceReference(
resolvedCall.trace,
error.classDescriptor,
resolvedCall.explicitReceiverKind
)
is ErrorDescriptorDiagnostic -> { is ErrorDescriptorDiagnostic -> {
// todo // todo
// return@map null // return@map null
@@ -278,11 +308,11 @@ class NewResolutionOldInference(
// true if we found something // true if we found something
private fun reportAdditionalDiagnosticIfNoCandidates( private fun reportAdditionalDiagnosticIfNoCandidates(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
name: Name, name: Name,
kind: ResolutionKind<*>, kind: ResolutionKind<*>,
scopeTower: ImplicitScopeTower, scopeTower: ImplicitScopeTower,
detailedReceiver: DetailedReceiver? detailedReceiver: DetailedReceiver?
): Boolean { ): Boolean {
val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false
@@ -301,17 +331,17 @@ class NewResolutionOldInference(
} }
private class ImplicitScopeTowerImpl( private class ImplicitScopeTowerImpl(
val resolutionContext: ResolutionContext<*>, val resolutionContext: ResolutionContext<*>,
override val dynamicScope: MemberScope, override val dynamicScope: MemberScope,
override val syntheticScopes: SyntheticScopes, override val syntheticScopes: SyntheticScopes,
override val location: LookupLocation override val location: LookupLocation
): ImplicitScopeTower { ) : ImplicitScopeTower {
private val cache = HashMap<ReceiverValue, ReceiverValueWithSmartCastInfo>() private val cache = HashMap<ReceiverValue, ReceiverValueWithSmartCastInfo>()
override fun getImplicitReceiver(scope: LexicalScope): ReceiverValueWithSmartCastInfo? = override fun getImplicitReceiver(scope: LexicalScope): ReceiverValueWithSmartCastInfo? =
scope.implicitReceiver?.value?.let { scope.implicitReceiver?.value?.let {
cache.getOrPut(it) { resolutionContext.transformToReceiverWithSmartCastInfo(it) } cache.getOrPut(it) { resolutionContext.transformToReceiverWithSmartCastInfo(it) }
} }
override val lexicalScope: LexicalScope get() = resolutionContext.scope override val lexicalScope: LexicalScope get() = resolutionContext.scope
@@ -322,12 +352,12 @@ class NewResolutionOldInference(
} }
internal class MyCandidate( internal class MyCandidate(
// Diagnostics that are already computed // Diagnostics that are already computed
// if resultingApplicability is successful they must be the same as `diagnostics`, // if resultingApplicability is successful they must be the same as `diagnostics`,
// otherwise they might be a bit different but result remains unsuccessful // otherwise they might be a bit different but result remains unsuccessful
val eagerDiagnostics: List<KotlinCallDiagnostic>, val eagerDiagnostics: List<KotlinCallDiagnostic>,
val resolvedCall: MutableResolvedCall<*>, val resolvedCall: MutableResolvedCall<*>,
finalDiagnosticsComputation: (() -> List<KotlinCallDiagnostic>)? = null finalDiagnosticsComputation: (() -> List<KotlinCallDiagnostic>)? = null
) : Candidate { ) : Candidate {
val diagnostics: List<KotlinCallDiagnostic> by lazy(LazyThreadSafetyMode.NONE) { val diagnostics: List<KotlinCallDiagnostic> by lazy(LazyThreadSafetyMode.NONE) {
finalDiagnosticsComputation?.invoke() ?: eagerDiagnostics finalDiagnosticsComputation?.invoke() ?: eagerDiagnostics
@@ -344,22 +374,22 @@ class NewResolutionOldInference(
} }
private inner class CandidateFactoryImpl( private inner class CandidateFactoryImpl(
val name: Name, val name: Name,
val basicCallContext: BasicCallResolutionContext, val basicCallContext: BasicCallResolutionContext,
val tracing: TracingStrategy val tracing: TracingStrategy
) : CandidateFactory<MyCandidate> { ) : CandidateFactory<MyCandidate> {
override fun createCandidate( override fun createCandidate(
towerCandidate: CandidateWithBoundDispatchReceiver, towerCandidate: CandidateWithBoundDispatchReceiver,
explicitReceiverKind: ExplicitReceiverKind, explicitReceiverKind: ExplicitReceiverKind,
extensionReceiver: ReceiverValueWithSmartCastInfo? extensionReceiver: ReceiverValueWithSmartCastInfo?
): MyCandidate { ): MyCandidate {
val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate") val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate")
val candidateCall = ResolvedCallImpl( val candidateCall = ResolvedCallImpl(
basicCallContext.call, towerCandidate.descriptor, basicCallContext.call, towerCandidate.descriptor,
towerCandidate.dispatchReceiver?.receiverValue, extensionReceiver?.receiverValue, towerCandidate.dispatchReceiver?.receiverValue, extensionReceiver?.receiverValue,
explicitReceiverKind, null, candidateTrace, tracing, explicitReceiverKind, null, candidateTrace, tracing,
basicCallContext.dataFlowInfoForArguments // todo may be we should create new mutable info for arguments basicCallContext.dataFlowInfoForArguments // todo may be we should create new mutable info for arguments
) )
/** /**
@@ -385,8 +415,8 @@ class NewResolutionOldInference(
} }
val callCandidateResolutionContext = CallCandidateResolutionContext.create( val callCandidateResolutionContext = CallCandidateResolutionContext.create(
candidateCall, basicCallContext, candidateTrace, tracing, basicCallContext.call, candidateCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.EXIT_ON_FIRST_ERROR CandidateResolveMode.EXIT_ON_FIRST_ERROR
) )
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
@@ -398,14 +428,14 @@ class NewResolutionOldInference(
} }
private fun createDiagnosticsForCandidate( private fun createDiagnosticsForCandidate(
towerCandidate: CandidateWithBoundDispatchReceiver, towerCandidate: CandidateWithBoundDispatchReceiver,
candidateCall: ResolvedCallImpl<CallableDescriptor> candidateCall: ResolvedCallImpl<CallableDescriptor>
): List<ResolutionDiagnostic> = ): List<ResolutionDiagnostic> =
mutableListOf<ResolutionDiagnostic>().apply { mutableListOf<ResolutionDiagnostic>().apply {
addAll(towerCandidate.diagnostics) addAll(towerCandidate.diagnostics)
addAll(checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor)) addAll(checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor))
addIfNotNull(createPreviousResolveError(candidateCall.status)) addIfNotNull(createPreviousResolveError(candidateCall.status))
} }
private fun checkInfixAndOperator(call: Call, descriptor: CallableDescriptor): List<ResolutionDiagnostic> { private fun checkInfixAndOperator(call: Call, descriptor: CallableDescriptor): List<ResolutionDiagnostic> {
if (descriptor !is FunctionDescriptor || ErrorUtils.isError(descriptor)) return emptyList() if (descriptor !is FunctionDescriptor || ErrorUtils.isError(descriptor)) return emptyList()
@@ -421,16 +451,16 @@ class NewResolutionOldInference(
} }
private inner class CandidateFactoryProviderForInvokeImpl( private inner class CandidateFactoryProviderForInvokeImpl(
val functionContext: CandidateFactoryImpl val functionContext: CandidateFactoryImpl
) : CandidateFactoryProviderForInvoke<MyCandidate> { ) : CandidateFactoryProviderForInvoke<MyCandidate> {
override fun transformCandidate( override fun transformCandidate(
variable: MyCandidate, variable: MyCandidate,
invoke: MyCandidate invoke: MyCandidate
): MyCandidate { ): MyCandidate {
val resolvedCallImpl = VariableAsFunctionResolvedCallImpl( val resolvedCallImpl = VariableAsFunctionResolvedCallImpl(
invoke.resolvedCall as MutableResolvedCall<FunctionDescriptor>, invoke.resolvedCall as MutableResolvedCall<FunctionDescriptor>,
variable.resolvedCall as MutableResolvedCall<VariableDescriptor> variable.resolvedCall as MutableResolvedCall<VariableDescriptor>
) )
assert(variable.resultingApplicability.isSuccess) { assert(variable.resultingApplicability.isSuccess) {
"Variable call must be success: $variable" "Variable call must be success: $variable"
@@ -445,16 +475,20 @@ class NewResolutionOldInference(
val newCall = CallTransformer.stripCallArguments(functionContext.basicCallContext.call).let { val newCall = CallTransformer.stripCallArguments(functionContext.basicCallContext.call).let {
if (stripExplicitReceiver) CallTransformer.stripReceiver(it) else it if (stripExplicitReceiver) CallTransformer.stripReceiver(it) else it
} }
return CandidateFactoryImpl(functionContext.name, functionContext.basicCallContext.replaceCall(newCall), functionContext.tracing) return CandidateFactoryImpl(
functionContext.name,
functionContext.basicCallContext.replaceCall(newCall),
functionContext.tracing
)
} }
override fun factoryForInvoke( override fun factoryForInvoke(
variable: MyCandidate, variable: MyCandidate,
useExplicitReceiver: Boolean useExplicitReceiver: Boolean
): Pair<ReceiverValueWithSmartCastInfo, CandidateFactory<MyCandidate>>? { ): Pair<ReceiverValueWithSmartCastInfo, CandidateFactory<MyCandidate>>? {
assert(variable.resolvedCall.status.possibleTransformToSuccess()) { assert(variable.resolvedCall.status.possibleTransformToSuccess()) {
"Incorrect status: ${variable.resolvedCall.status} for variable call: ${variable.resolvedCall} " + "Incorrect status: ${variable.resolvedCall.status} for variable call: ${variable.resolvedCall} " +
"and descriptor: ${variable.resolvedCall.candidateDescriptor}" "and descriptor: ${variable.resolvedCall.candidateDescriptor}"
} }
val calleeExpression = variable.resolvedCall.call.calleeExpression val calleeExpression = variable.resolvedCall.call.calleeExpression
val variableDescriptor = variable.resolvedCall.resultingDescriptor as VariableDescriptor val variableDescriptor = variable.resolvedCall.resultingDescriptor as VariableDescriptor
@@ -467,19 +501,22 @@ class NewResolutionOldInference(
return null // todo: create special check that there is no invoke on variable return null // todo: create special check that there is no invoke on variable
} }
val basicCallContext = functionContext.basicCallContext val basicCallContext = functionContext.basicCallContext
val variableReceiver = ExpressionReceiver.create(calleeExpression!!, val variableReceiver = ExpressionReceiver.create(
variableType, calleeExpression!!,
basicCallContext.trace.bindingContext) variableType,
basicCallContext.trace.bindingContext
)
// used for smartCasts, see: DataFlowValueFactory.getIdForSimpleNameExpression // used for smartCasts, see: DataFlowValueFactory.getIdForSimpleNameExpression
functionContext.tracing.bindReference(variable.resolvedCall.trace, variable.resolvedCall) functionContext.tracing.bindReference(variable.resolvedCall.trace, variable.resolvedCall)
// todo hacks // todo hacks
val functionCall = CallTransformer.CallForImplicitInvoke( val functionCall = CallTransformer.CallForImplicitInvoke(
basicCallContext.call.explicitReceiver?.takeIf { useExplicitReceiver }, basicCallContext.call.explicitReceiver?.takeIf { useExplicitReceiver },
variableReceiver, basicCallContext.call, true) variableReceiver, basicCallContext.call, true
)
val tracingForInvoke = TracingStrategyForInvoke(calleeExpression, functionCall, variableReceiver.type) val tracingForInvoke = TracingStrategyForInvoke(calleeExpression, functionCall, variableReceiver.type)
val basicCallResolutionContext = basicCallContext.replaceBindingTrace(variable.resolvedCall.trace) val basicCallResolutionContext = basicCallContext.replaceBindingTrace(variable.resolvedCall.trace)
.replaceCall(functionCall) .replaceCall(functionCall)
.replaceContextDependency(ContextDependency.DEPENDENT) // todo .replaceContextDependency(ContextDependency.DEPENDENT) // todo
val newContext = CandidateFactoryImpl(OperatorNameConventions.INVOKE, basicCallResolutionContext, tracingForInvoke) val newContext = CandidateFactoryImpl(OperatorNameConventions.INVOKE, basicCallResolutionContext, tracingForInvoke)
@@ -491,25 +528,25 @@ class NewResolutionOldInference(
} }
fun ResolutionContext<*>.transformToReceiverWithSmartCastInfo(receiver: ReceiverValue) = fun ResolutionContext<*>.transformToReceiverWithSmartCastInfo(receiver: ReceiverValue) =
transformToReceiverWithSmartCastInfo(scope.ownerDescriptor, trace.bindingContext, dataFlowInfo, receiver, languageVersionSettings) transformToReceiverWithSmartCastInfo(scope.ownerDescriptor, trace.bindingContext, dataFlowInfo, receiver, languageVersionSettings)
fun transformToReceiverWithSmartCastInfo( fun transformToReceiverWithSmartCastInfo(
containingDescriptor: DeclarationDescriptor, containingDescriptor: DeclarationDescriptor,
bindingContext: BindingContext, bindingContext: BindingContext,
dataFlowInfo: DataFlowInfo, dataFlowInfo: DataFlowInfo,
receiver: ReceiverValue, receiver: ReceiverValue,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): ReceiverValueWithSmartCastInfo { ): ReceiverValueWithSmartCastInfo {
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, bindingContext, containingDescriptor) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, bindingContext, containingDescriptor)
return ReceiverValueWithSmartCastInfo( return ReceiverValueWithSmartCastInfo(
receiver, receiver,
dataFlowInfo.getCollectedTypes(dataFlowValue, languageVersionSettings), dataFlowInfo.getCollectedTypes(dataFlowValue, languageVersionSettings),
dataFlowValue.isStable dataFlowValue.isStable
) )
} }
@Deprecated("Temporary error") @Deprecated("Temporary error")
internal class PreviousResolutionError(candidateLevel: ResolutionCandidateApplicability): ResolutionDiagnostic(candidateLevel) internal class PreviousResolutionError(candidateLevel: ResolutionCandidateApplicability) : ResolutionDiagnostic(candidateLevel)
@Deprecated("Temporary error") @Deprecated("Temporary error")
internal fun createPreviousResolveError(status: ResolutionStatus): PreviousResolutionError? { internal fun createPreviousResolveError(status: ResolutionStatus): PreviousResolutionError? {
@@ -60,28 +60,28 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.util.* import java.util.*
class PSICallResolver( class PSICallResolver(
private val typeResolver: TypeResolver, private val typeResolver: TypeResolver,
private val expressionTypingServices: ExpressionTypingServices, private val expressionTypingServices: ExpressionTypingServices,
private val doubleColonExpressionResolver: DoubleColonExpressionResolver, private val doubleColonExpressionResolver: DoubleColonExpressionResolver,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val dynamicCallableDescriptors: DynamicCallableDescriptors, private val dynamicCallableDescriptors: DynamicCallableDescriptors,
private val syntheticScopes: SyntheticScopes, private val syntheticScopes: SyntheticScopes,
private val callComponents: KotlinCallComponents, private val callComponents: KotlinCallComponents,
private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer, private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer,
private val kotlinCallResolver: KotlinCallResolver, private val kotlinCallResolver: KotlinCallResolver,
private val typeApproximator: TypeApproximator, private val typeApproximator: TypeApproximator,
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val effectSystem: EffectSystem, private val effectSystem: EffectSystem,
private val constantExpressionEvaluator: ConstantExpressionEvaluator private val constantExpressionEvaluator: ConstantExpressionEvaluator
) { ) {
private val GIVEN_CANDIDATES_NAME = Name.special("<given candidates>") private val GIVEN_CANDIDATES_NAME = Name.special("<given candidates>")
fun <D : CallableDescriptor> runResolutionAndInference( fun <D : CallableDescriptor> runResolutionAndInference(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
name: Name, name: Name,
resolutionKind: NewResolutionOldInference.ResolutionKind<D>, resolutionKind: NewResolutionOldInference.ResolutionKind<D>,
tracingStrategy: TracingStrategy tracingStrategy: TracingStrategy
) : OverloadResolutionResults<D> { ): OverloadResolutionResults<D> {
val isBinaryRemOperator = isBinaryRemOperator(context.call) val isBinaryRemOperator = isBinaryRemOperator(context.call)
val refinedName = refineNameForRemOperator(isBinaryRemOperator, name) val refinedName = refineNameForRemOperator(isBinaryRemOperator, name)
@@ -93,7 +93,8 @@ class PSICallResolver(
val expectedType = calculateExpectedType(context) val expectedType = calculateExpectedType(context)
var result = kotlinCallResolver.resolveCall( var result = kotlinCallResolver.resolveCall(
scopeTower, resolutionCallbacks, kotlinCall, expectedType, factoryProviderForInvoke, context.collectAllCandidates) scopeTower, resolutionCallbacks, kotlinCall, expectedType, factoryProviderForInvoke, context.collectAllCandidates
)
val shouldUseOperatorRem = languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem) val shouldUseOperatorRem = languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)
if (isBinaryRemOperator && shouldUseOperatorRem && (result.isEmpty() || result.areAllInapplicable())) { if (isBinaryRemOperator && shouldUseOperatorRem && (result.isEmpty() || result.areAllInapplicable())) {
@@ -109,42 +110,48 @@ class PSICallResolver(
// actually, `D` is at least FunctionDescriptor, but right now because of CallResolver it isn't possible change upper bound for `D` // actually, `D` is at least FunctionDescriptor, but right now because of CallResolver it isn't possible change upper bound for `D`
fun <D : CallableDescriptor> runResolutionAndInferenceForGivenCandidates( fun <D : CallableDescriptor> runResolutionAndInferenceForGivenCandidates(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
resolutionCandidates: Collection<ResolutionCandidate<D>>, resolutionCandidates: Collection<ResolutionCandidate<D>>,
tracingStrategy: TracingStrategy tracingStrategy: TracingStrategy
): OverloadResolutionResults<D> { ): OverloadResolutionResults<D> {
val dispatchReceiver = resolutionCandidates.firstNotNullResult { it.dispatchReceiver } val dispatchReceiver = resolutionCandidates.firstNotNullResult { it.dispatchReceiver }
val kotlinCall = toKotlinCall(context, KotlinCallKind.FUNCTION, context.call, GIVEN_CANDIDATES_NAME, tracingStrategy, dispatchReceiver) val kotlinCall =
toKotlinCall(context, KotlinCallKind.FUNCTION, context.call, GIVEN_CANDIDATES_NAME, tracingStrategy, dispatchReceiver)
val scopeTower = ASTScopeTower(context) val scopeTower = ASTScopeTower(context)
val resolutionCallbacks = createResolutionCallbacks(context) val resolutionCallbacks = createResolutionCallbacks(context)
val givenCandidates = resolutionCandidates.map { val givenCandidates = resolutionCandidates.map {
GivenCandidate(it.descriptor as FunctionDescriptor, GivenCandidate(
it.dispatchReceiver?.let { context.transformToReceiverWithSmartCastInfo(it) }, it.descriptor as FunctionDescriptor,
it.knownTypeParametersResultingSubstitutor) it.dispatchReceiver?.let { context.transformToReceiverWithSmartCastInfo(it) },
it.knownTypeParametersResultingSubstitutor
)
} }
val result = kotlinCallResolver.resolveGivenCandidates( val result = kotlinCallResolver.resolveGivenCandidates(
scopeTower, resolutionCallbacks, kotlinCall, calculateExpectedType(context), givenCandidates, context.collectAllCandidates) scopeTower, resolutionCallbacks, kotlinCall, calculateExpectedType(context), givenCandidates, context.collectAllCandidates
)
return convertToOverloadResolutionResults(context, result, tracingStrategy) return convertToOverloadResolutionResults(context, result, tracingStrategy)
} }
private fun <D : CallableDescriptor> resolveToDeprecatedMod( private fun <D : CallableDescriptor> resolveToDeprecatedMod(
remOperatorName: Name, remOperatorName: Name,
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
resolutionKind: NewResolutionOldInference.ResolutionKind<D>, resolutionKind: NewResolutionOldInference.ResolutionKind<D>,
tracingStrategy: TracingStrategy, tracingStrategy: TracingStrategy,
scopeTower: ImplicitScopeTower, scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacksImpl, resolutionCallbacks: KotlinResolutionCallbacksImpl,
expectedType: UnwrappedType? expectedType: UnwrappedType?
): CallResolutionResult { ): CallResolutionResult {
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[remOperatorName]!! val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[remOperatorName]!!
val callWithDeprecatedName = toKotlinCall(context, resolutionKind.kotlinCallKind, context.call, deprecatedName, tracingStrategy) val callWithDeprecatedName = toKotlinCall(context, resolutionKind.kotlinCallKind, context.call, deprecatedName, tracingStrategy)
val refinedProviderForInvokeFactory = FactoryProviderForInvoke(context, scopeTower, callWithDeprecatedName) val refinedProviderForInvokeFactory = FactoryProviderForInvoke(context, scopeTower, callWithDeprecatedName)
return kotlinCallResolver.resolveCall(scopeTower, resolutionCallbacks, callWithDeprecatedName, expectedType, return kotlinCallResolver.resolveCall(
refinedProviderForInvokeFactory, context.collectAllCandidates) scopeTower, resolutionCallbacks, callWithDeprecatedName, expectedType,
refinedProviderForInvokeFactory, context.collectAllCandidates
)
} }
private fun refineNameForRemOperator(isBinaryRemOperator: Boolean, name: Name): Name { private fun refineNameForRemOperator(isBinaryRemOperator: Boolean, name: Name): Name {
@@ -153,9 +160,11 @@ class PSICallResolver(
} }
private fun createResolutionCallbacks(context: BasicCallResolutionContext) = private fun createResolutionCallbacks(context: BasicCallResolutionContext) =
KotlinResolutionCallbacksImpl(context, expressionTypingServices, typeApproximator, KotlinResolutionCallbacksImpl(
argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer, context, expressionTypingServices, typeApproximator,
constantExpressionEvaluator) argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer,
constantExpressionEvaluator
)
private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? { private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? {
val expectedType = context.expectedType.unwrap() val expectedType = context.expectedType.unwrap()
@@ -165,16 +174,15 @@ class PSICallResolver(
"Should have no expected type, got: $expectedType" "Should have no expected type, got: $expectedType"
} }
null null
} } else {
else {
if (expectedType.isError) TypeUtils.NO_EXPECTED_TYPE else expectedType if (expectedType.isError) TypeUtils.NO_EXPECTED_TYPE else expectedType
} }
} }
private fun <D : CallableDescriptor> convertToOverloadResolutionResults( private fun <D : CallableDescriptor> convertToOverloadResolutionResults(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
result: CallResolutionResult, result: CallResolutionResult,
tracingStrategy: TracingStrategy tracingStrategy: TracingStrategy
): OverloadResolutionResults<D> { ): OverloadResolutionResults<D> {
if (result.type == CallResolutionResult.Type.ALL_CANDIDATES) { if (result.type == CallResolutionResult.Type.ALL_CANDIDATES) {
val resolvedCalls = result.allCandidates?.map { val resolvedCalls = result.allCandidates?.map {
@@ -197,28 +205,26 @@ class PSICallResolver(
if (it.candidates.areAllFailed()) { if (it.candidates.areAllFailed()) {
tracingStrategy.noneApplicable(trace, resolvedCalls) tracingStrategy.noneApplicable(trace, resolvedCalls)
tracingStrategy.recordAmbiguity(trace, resolvedCalls) tracingStrategy.recordAmbiguity(trace, resolvedCalls)
} } else {
else {
tracingStrategy.recordAmbiguity(trace, resolvedCalls) tracingStrategy.recordAmbiguity(trace, resolvedCalls)
if (resolvedCalls.first().status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) { if (resolvedCalls.first().status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) {
tracingStrategy.cannotCompleteResolve(trace, resolvedCalls) tracingStrategy.cannotCompleteResolve(trace, resolvedCalls)
} } else {
else {
tracingStrategy.ambiguity(trace, resolvedCalls) tracingStrategy.ambiguity(trace, resolvedCalls)
} }
} }
return ManyCandidates(resolvedCalls) return ManyCandidates(resolvedCalls)
} }
val isInapplicableReceiver = getResultApplicability(result.diagnostics) == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER val isInapplicableReceiver =
getResultApplicability(result.diagnostics) == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER
val resolvedCall = if (isInapplicableReceiver) { val resolvedCall = if (isInapplicableReceiver) {
val singleCandidate = result.resultCallAtom ?: error("Should be not null for result: $result") val singleCandidate = result.resultCallAtom ?: error("Should be not null for result: $result")
kotlinToResolvedCallTransformer.onlyTransform<D>(singleCandidate, result.diagnostics).also { kotlinToResolvedCallTransformer.onlyTransform<D>(singleCandidate, result.diagnostics).also {
tracingStrategy.unresolvedReferenceWrongReceiver(trace, listOf(it)) tracingStrategy.unresolvedReferenceWrongReceiver(trace, listOf(it))
} }
} } else {
else {
kotlinToResolvedCallTransformer.transformAndReport<D>(result, context) kotlinToResolvedCallTransformer.transformAndReport<D>(result, context)
} }
@@ -246,12 +252,12 @@ class PSICallResolver(
} }
private fun CallResolutionResult.isEmpty(): Boolean = private fun CallResolutionResult.isEmpty(): Boolean =
diagnostics.firstIsInstanceOrNull<NoneCandidatesCallDiagnostic>() != null diagnostics.firstIsInstanceOrNull<NoneCandidatesCallDiagnostic>() != null
private fun Collection<KotlinResolutionCandidate>.areAllFailed() = private fun Collection<KotlinResolutionCandidate>.areAllFailed() =
all { all {
!it.resultingApplicability.isSuccess !it.resultingApplicability.isSuccess
} }
private fun CallResolutionResult.areAllInapplicable(): Boolean { private fun CallResolutionResult.areAllInapplicable(): Boolean {
val manyCandidates = diagnostics.firstIsInstanceOrNull<ManyCandidatesCallDiagnostic>()?.candidates val manyCandidates = diagnostics.firstIsInstanceOrNull<ManyCandidatesCallDiagnostic>()?.candidates
@@ -261,16 +267,16 @@ class PSICallResolver(
val applicability = getResultApplicability(diagnostics) val applicability = getResultApplicability(diagnostics)
return applicability == ResolutionCandidateApplicability.INAPPLICABLE || return applicability == ResolutionCandidateApplicability.INAPPLICABLE ||
applicability == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER || applicability == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER ||
applicability == ResolutionCandidateApplicability.HIDDEN applicability == ResolutionCandidateApplicability.HIDDEN
} }
// true if we found something // true if we found something
private fun reportAdditionalDiagnosticIfNoCandidates( private fun reportAdditionalDiagnosticIfNoCandidates(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
scopeTower: ImplicitScopeTower, scopeTower: ImplicitScopeTower,
kind: KotlinCallKind, kind: KotlinCallKind,
kotlinCall: KotlinCall kotlinCall: KotlinCall
): Boolean { ): Boolean {
val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false
@@ -285,7 +291,14 @@ class PSICallResolver(
for (candidate in errorCandidates) { for (candidate in errorCandidates) {
if (candidate is ErrorCandidate.Classifier) { if (candidate is ErrorCandidate.Classifier) {
context.trace.record(BindingContext.REFERENCE_TARGET, reference, candidate.descriptor) context.trace.record(BindingContext.REFERENCE_TARGET, reference, candidate.descriptor)
context.trace.report(Errors.RESOLUTION_TO_CLASSIFIER.on(reference, candidate.descriptor, candidate.kind, candidate.errorMessage)) context.trace.report(
Errors.RESOLUTION_TO_CLASSIFIER.on(
reference,
candidate.descriptor,
candidate.kind,
candidate.errorMessage
)
)
return true return true
} }
} }
@@ -294,10 +307,11 @@ class PSICallResolver(
private inner class ASTScopeTower( private inner class ASTScopeTower(
val context: BasicCallResolutionContext val context: BasicCallResolutionContext
): ImplicitScopeTower { ) : ImplicitScopeTower {
// todo may be for invoke for case variable + invoke we should create separate dynamicScope(by newCall for invoke) // todo may be for invoke for case variable + invoke we should create separate dynamicScope(by newCall for invoke)
override val dynamicScope: MemberScope = dynamicCallableDescriptors.createDynamicDescriptorScope(context.call, context.scope.ownerDescriptor) override val dynamicScope: MemberScope =
dynamicCallableDescriptors.createDynamicDescriptorScope(context.call, context.scope.ownerDescriptor)
// same for location // same for location
override val location: LookupLocation = context.call.createLookupLocation() override val location: LookupLocation = context.call.createLookupLocation()
@@ -317,9 +331,9 @@ class PSICallResolver(
} }
private inner class FactoryProviderForInvoke( private inner class FactoryProviderForInvoke(
val context: BasicCallResolutionContext, val context: BasicCallResolutionContext,
val scopeTower: ImplicitScopeTower, val scopeTower: ImplicitScopeTower,
val kotlinCall: PSIKotlinCallImpl val kotlinCall: PSIKotlinCallImpl
) : CandidateFactoryProviderForInvoke<KotlinResolutionCandidate> { ) : CandidateFactoryProviderForInvoke<KotlinResolutionCandidate> {
init { init {
@@ -327,8 +341,8 @@ class PSICallResolver(
} }
override fun transformCandidate( override fun transformCandidate(
variable: KotlinResolutionCandidate, variable: KotlinResolutionCandidate,
invoke: KotlinResolutionCandidate invoke: KotlinResolutionCandidate
) = invoke ) = invoke
override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<KotlinResolutionCandidate> { override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<KotlinResolutionCandidate> {
@@ -343,15 +357,14 @@ class PSICallResolver(
assert(variable.isSuccessful) { assert(variable.isSuccessful) {
"Variable call should be successful: $variable " + "Variable call should be successful: $variable " +
"Descriptor: ${variable.resolvedCall.candidateDescriptor}" "Descriptor: ${variable.resolvedCall.candidateDescriptor}"
} }
val variableCallArgument = createReceiverCallArgument(variable) val variableCallArgument = createReceiverCallArgument(variable)
val explicitReceiver = kotlinCall.explicitReceiver val explicitReceiver = kotlinCall.explicitReceiver
val callForInvoke = if (useExplicitReceiver && explicitReceiver != null) { val callForInvoke = if (useExplicitReceiver && explicitReceiver != null) {
PSIKotlinCallForInvoke(kotlinCall, variable, explicitReceiver, variableCallArgument) PSIKotlinCallForInvoke(kotlinCall, variable, explicitReceiver, variableCallArgument)
} } else {
else {
PSIKotlinCallForInvoke(kotlinCall, variable, variableCallArgument, null) PSIKotlinCallForInvoke(kotlinCall, variable, variableCallArgument, null)
} }
@@ -369,22 +382,32 @@ class PSICallResolver(
variable.forceResolution() variable.forceResolution()
val variableReceiver = createReceiverValueWithSmartCastInfo(variable) val variableReceiver = createReceiverValueWithSmartCastInfo(variable)
if (variableReceiver.possibleTypes.isNotEmpty()) { if (variableReceiver.possibleTypes.isNotEmpty()) {
return ReceiverExpressionKotlinCallArgument(createReceiverValueWithSmartCastInfo(variable), isVariableReceiverForInvoke = true) return ReceiverExpressionKotlinCallArgument(
createReceiverValueWithSmartCastInfo(variable),
isVariableReceiverForInvoke = true
)
} }
val psiKotlinCall = variable.resolvedCall.atom.psiKotlinCall val psiKotlinCall = variable.resolvedCall.atom.psiKotlinCall
val variableResult = CallResolutionResult(CallResolutionResult.Type.PARTIAL, variable.resolvedCall, listOf(), variable.getSystem().asReadOnlyStorage()) val variableResult = CallResolutionResult(
return SubKotlinCallArgumentImpl(CallMaker.makeExternalValueArgument((variableReceiver.receiverValue as ExpressionReceiver).expression), CallResolutionResult.Type.PARTIAL,
psiKotlinCall.resultDataFlowInfo, psiKotlinCall.resultDataFlowInfo, variableReceiver, variable.resolvedCall,
variableResult) listOf(),
variable.getSystem().asReadOnlyStorage()
)
return SubKotlinCallArgumentImpl(
CallMaker.makeExternalValueArgument((variableReceiver.receiverValue as ExpressionReceiver).expression),
psiKotlinCall.resultDataFlowInfo, psiKotlinCall.resultDataFlowInfo, variableReceiver,
variableResult
)
} }
// todo: decrease hacks count // todo: decrease hacks count
private fun createReceiverValueWithSmartCastInfo(variable: KotlinResolutionCandidate): ReceiverValueWithSmartCastInfo { private fun createReceiverValueWithSmartCastInfo(variable: KotlinResolutionCandidate): ReceiverValueWithSmartCastInfo {
val callForVariable = variable.resolvedCall.atom as PSIKotlinCallForVariable val callForVariable = variable.resolvedCall.atom as PSIKotlinCallForVariable
val calleeExpression = callForVariable.baseCall.psiCall.calleeExpression as? KtReferenceExpression ?: val calleeExpression = callForVariable.baseCall.psiCall.calleeExpression as? KtReferenceExpression
error("Unexpected call : ${callForVariable.baseCall.psiCall}") ?: error("Unexpected call : ${callForVariable.baseCall.psiCall}")
val temporaryTrace = TemporaryBindingTrace.create(context.trace, "Context for resolve candidate") val temporaryTrace = TemporaryBindingTrace.create(context.trace, "Context for resolve candidate")
@@ -392,31 +415,32 @@ class PSICallResolver(
val variableReceiver = ExpressionReceiver.create(calleeExpression, type, temporaryTrace.bindingContext) val variableReceiver = ExpressionReceiver.create(calleeExpression, type, temporaryTrace.bindingContext)
temporaryTrace.record(BindingContext.REFERENCE_TARGET, calleeExpression, variable.resolvedCall.candidateDescriptor) temporaryTrace.record(BindingContext.REFERENCE_TARGET, calleeExpression, variable.resolvedCall.candidateDescriptor)
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(variableReceiver, temporaryTrace.bindingContext, context.scope.ownerDescriptor) val dataFlowValue =
DataFlowValueFactory.createDataFlowValue(variableReceiver, temporaryTrace.bindingContext, context.scope.ownerDescriptor)
return ReceiverValueWithSmartCastInfo( return ReceiverValueWithSmartCastInfo(
variableReceiver, variableReceiver,
context.dataFlowInfo.getCollectedTypes(dataFlowValue, context.languageVersionSettings), context.dataFlowInfo.getCollectedTypes(dataFlowValue, context.languageVersionSettings),
dataFlowValue.isStable dataFlowValue.isStable
) )
} }
} }
private fun toKotlinCall( private fun toKotlinCall(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
kotlinCallKind: KotlinCallKind, kotlinCallKind: KotlinCallKind,
oldCall: Call, oldCall: Call,
name: Name, name: Name,
tracingStrategy: TracingStrategy, tracingStrategy: TracingStrategy,
forcedExplicitReceiver: Receiver? = null forcedExplicitReceiver: Receiver? = null
): PSIKotlinCallImpl { ): PSIKotlinCallImpl {
val resolvedExplicitReceiver = resolveExplicitReceiver(context, forcedExplicitReceiver?: oldCall.explicitReceiver, oldCall.isSafeCall()) val resolvedExplicitReceiver =
resolveExplicitReceiver(context, forcedExplicitReceiver ?: oldCall.explicitReceiver, oldCall.isSafeCall())
val resolvedTypeArguments = resolveTypeArguments(context, oldCall.typeArguments) val resolvedTypeArguments = resolveTypeArguments(context, oldCall.typeArguments)
val argumentsInParenthesis = if (oldCall.callType != Call.CallType.ARRAY_SET_METHOD && oldCall.functionLiteralArguments.isEmpty()) { val argumentsInParenthesis = if (oldCall.callType != Call.CallType.ARRAY_SET_METHOD && oldCall.functionLiteralArguments.isEmpty()) {
oldCall.valueArguments oldCall.valueArguments
} } else {
else {
oldCall.valueArguments.dropLast(1) oldCall.valueArguments.dropLast(1)
} }
@@ -428,8 +452,7 @@ class PSICallResolver(
"Unexpected lambda parameters for call $oldCall" "Unexpected lambda parameters for call $oldCall"
} }
oldCall.valueArguments.last() oldCall.valueArguments.last()
} } else {
else {
if (externalLambdaArguments.size > 2) { if (externalLambdaArguments.size > 2) {
externalLambdaArguments.drop(1).forEach { externalLambdaArguments.drop(1).forEach {
context.trace.report(Errors.MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(it.getLambdaExpression())) context.trace.report(Errors.MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(it.getLambdaExpression()))
@@ -440,10 +463,10 @@ class PSICallResolver(
} }
val dataFlowInfoAfterArgumentsInParenthesis = val dataFlowInfoAfterArgumentsInParenthesis =
if (externalArgument != null && resolvedArgumentsInParenthesis.isNotEmpty()) if (externalArgument != null && resolvedArgumentsInParenthesis.isNotEmpty())
resolvedArgumentsInParenthesis.last().psiCallArgument.dataFlowInfoAfterThisArgument resolvedArgumentsInParenthesis.last().psiCallArgument.dataFlowInfoAfterThisArgument
else else
context.dataFlowInfoForArguments.resultInfo context.dataFlowInfoForArguments.resultInfo
val astExternalArgument = externalArgument?.let { resolveValueArgument(context, dataFlowInfoAfterArgumentsInParenthesis, it) } val astExternalArgument = externalArgument?.let { resolveValueArgument(context, dataFlowInfoAfterArgumentsInParenthesis, it) }
val resultDataFlowInfo = astExternalArgument?.dataFlowInfoAfterThisArgument ?: dataFlowInfoAfterArgumentsInParenthesis val resultDataFlowInfo = astExternalArgument?.dataFlowInfoAfterThisArgument ?: dataFlowInfoAfterArgumentsInParenthesis
@@ -451,39 +474,47 @@ class PSICallResolver(
resolvedArgumentsInParenthesis.forEach { it.setResultDataFlowInfoIfRelevant(resultDataFlowInfo) } resolvedArgumentsInParenthesis.forEach { it.setResultDataFlowInfoIfRelevant(resultDataFlowInfo) }
astExternalArgument?.setResultDataFlowInfoIfRelevant(resultDataFlowInfo) astExternalArgument?.setResultDataFlowInfoIfRelevant(resultDataFlowInfo)
return PSIKotlinCallImpl(kotlinCallKind, oldCall, tracingStrategy, resolvedExplicitReceiver, name, resolvedTypeArguments, resolvedArgumentsInParenthesis, return PSIKotlinCallImpl(
astExternalArgument, context.dataFlowInfo, resultDataFlowInfo, context.dataFlowInfoForArguments) kotlinCallKind, oldCall, tracingStrategy, resolvedExplicitReceiver, name, resolvedTypeArguments, resolvedArgumentsInParenthesis,
astExternalArgument, context.dataFlowInfo, resultDataFlowInfo, context.dataFlowInfoForArguments
)
} }
private fun resolveExplicitReceiver(context: BasicCallResolutionContext, oldReceiver: Receiver?, isSafeCall: Boolean): ReceiverKotlinCallArgument? = private fun resolveExplicitReceiver(
when(oldReceiver) { context: BasicCallResolutionContext,
null -> null oldReceiver: Receiver?,
is QualifierReceiver -> QualifierReceiverKotlinCallArgument(oldReceiver) // todo report warning if isSafeCall isSafeCall: Boolean
is ReceiverValue -> { ): ReceiverKotlinCallArgument? =
val detailedReceiver = context.transformToReceiverWithSmartCastInfo(oldReceiver) when (oldReceiver) {
null -> null
is QualifierReceiver -> QualifierReceiverKotlinCallArgument(oldReceiver) // todo report warning if isSafeCall
is ReceiverValue -> {
val detailedReceiver = context.transformToReceiverWithSmartCastInfo(oldReceiver)
var subCallArgument: ReceiverKotlinCallArgument? = null var subCallArgument: ReceiverKotlinCallArgument? = null
if (oldReceiver is ExpressionReceiver) { if (oldReceiver is ExpressionReceiver) {
val ktExpression = KtPsiUtil.getLastElementDeparenthesized(oldReceiver.expression, context.statementFilter) val ktExpression = KtPsiUtil.getLastElementDeparenthesized(oldReceiver.expression, context.statementFilter)
val bindingContext = context.trace.bindingContext val bindingContext = context.trace.bindingContext
val call = bindingContext[BindingContext.DELEGATE_EXPRESSION_TO_PROVIDE_DELEGATE_CALL, ktExpression] val call = bindingContext[BindingContext.DELEGATE_EXPRESSION_TO_PROVIDE_DELEGATE_CALL, ktExpression]
?: ktExpression?.getCall(bindingContext) ?: ktExpression?.getCall(bindingContext)
val onlyResolvedCall = call?.let { val onlyResolvedCall = call?.let {
bindingContext.get(BindingContext.ONLY_RESOLVED_CALL, it) bindingContext.get(BindingContext.ONLY_RESOLVED_CALL, it)
}
if (onlyResolvedCall != null) {
subCallArgument = SubKotlinCallArgumentImpl(CallMaker.makeExternalValueArgument(oldReceiver.expression),
context.dataFlowInfo, context.dataFlowInfo, detailedReceiver, onlyResolvedCall)
}
} }
if (onlyResolvedCall != null) {
subCallArgument = SubKotlinCallArgumentImpl(
CallMaker.makeExternalValueArgument(oldReceiver.expression),
context.dataFlowInfo, context.dataFlowInfo, detailedReceiver, onlyResolvedCall
)
subCallArgument ?: ReceiverExpressionKotlinCallArgument(detailedReceiver, isSafeCall) }
} }
else -> error("Incorrect receiver: $oldReceiver")
subCallArgument ?: ReceiverExpressionKotlinCallArgument(detailedReceiver, isSafeCall)
} }
else -> error("Incorrect receiver: $oldReceiver")
}
private fun resolveType(context: BasicCallResolutionContext, typeReference: KtTypeReference?): UnwrappedType? { private fun resolveType(context: BasicCallResolutionContext, typeReference: KtTypeReference?): UnwrappedType? {
if (typeReference == null) return null if (typeReference == null) return null
@@ -494,18 +525,19 @@ class PSICallResolver(
} }
private fun resolveTypeArguments(context: BasicCallResolutionContext, typeArguments: List<KtTypeProjection>): List<TypeArgument> = private fun resolveTypeArguments(context: BasicCallResolutionContext, typeArguments: List<KtTypeProjection>): List<TypeArgument> =
typeArguments.map { projection -> typeArguments.map { projection ->
if (projection.projectionKind != KtProjectionKind.NONE) { if (projection.projectionKind != KtProjectionKind.NONE) {
context.trace.report(Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection)) context.trace.report(Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection))
}
ModifierCheckerCore.check(projection, context.trace, null, languageVersionSettings)
resolveType(context, projection.typeReference)?.let { SimpleTypeArgumentImpl(projection.typeReference!!, it) } ?: TypeArgumentPlaceholder
} }
ModifierCheckerCore.check(projection, context.trace, null, languageVersionSettings)
resolveType(context, projection.typeReference)?.let { SimpleTypeArgumentImpl(projection.typeReference!!, it) }
?: TypeArgumentPlaceholder
}
private fun resolveArgumentsInParenthesis( private fun resolveArgumentsInParenthesis(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
arguments: List<ValueArgument> arguments: List<ValueArgument>
): List<KotlinCallArgument> { ): List<KotlinCallArgument> {
val dataFlowInfoForArguments = context.dataFlowInfoForArguments val dataFlowInfoForArguments = context.dataFlowInfoForArguments
return arguments.map { argument -> return arguments.map { argument ->
@@ -516,9 +548,9 @@ class PSICallResolver(
} }
private fun resolveValueArgument( private fun resolveValueArgument(
outerCallContext: BasicCallResolutionContext, outerCallContext: BasicCallResolutionContext,
startDataFlowInfo: DataFlowInfo, startDataFlowInfo: DataFlowInfo,
valueArgument: ValueArgument valueArgument: ValueArgument
): PSIKotlinCallArgument { ): PSIKotlinCallArgument {
val builtIns = outerCallContext.scope.ownerDescriptor.builtIns val builtIns = outerCallContext.scope.ownerDescriptor.builtIns
val parseErrorArgument = ParseErrorKotlinCallArgument(valueArgument, startDataFlowInfo, builtIns) val parseErrorArgument = ParseErrorKotlinCallArgument(valueArgument, startDataFlowInfo, builtIns)
@@ -534,17 +566,21 @@ class PSICallResolver(
if (ktExpression is KtCollectionLiteralExpression) { if (ktExpression is KtCollectionLiteralExpression) {
return CollectionLiteralKotlinCallArgumentImpl( return CollectionLiteralKotlinCallArgumentImpl(
valueArgument, argumentName, startDataFlowInfo, startDataFlowInfo, ktExpression, outerCallContext) valueArgument, argumentName, startDataFlowInfo, startDataFlowInfo, ktExpression, outerCallContext
)
} }
val context = outerCallContext.replaceContextDependency(ContextDependency.DEPENDENT) val context = outerCallContext.replaceContextDependency(ContextDependency.DEPENDENT)
.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceDataFlowInfo(startDataFlowInfo) .replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceDataFlowInfo(startDataFlowInfo)
if (ktExpression is KtCallableReferenceExpression) { if (ktExpression is KtCallableReferenceExpression) {
checkNoSpread(outerCallContext, valueArgument) checkNoSpread(outerCallContext, valueArgument)
val expressionTypingContext = ExpressionTypingContext.newContext(context) val expressionTypingContext = ExpressionTypingContext.newContext(context)
val lhsResult = if (ktExpression.isEmptyLHS) null else doubleColonExpressionResolver.resolveDoubleColonLHS(ktExpression, expressionTypingContext) val lhsResult = if (ktExpression.isEmptyLHS) null else doubleColonExpressionResolver.resolveDoubleColonLHS(
ktExpression,
expressionTypingContext
)
val newDataFlowInfo = (lhsResult as? DoubleColonLHS.Expression)?.dataFlowInfo ?: startDataFlowInfo val newDataFlowInfo = (lhsResult as? DoubleColonLHS.Expression)?.dataFlowInfo ?: startDataFlowInfo
val name = ktExpression.callableReference.getReferencedNameAsName() val name = ktExpression.callableReference.getReferencedNameAsName()
@@ -556,12 +592,10 @@ class PSICallResolver(
val calleeExpression = ktExpression.receiverExpression?.getCalleeExpressionIfAny() val calleeExpression = ktExpression.receiverExpression?.getCalleeExpressionIfAny()
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) { if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
LHSResult.Object(ClassQualifier(calleeExpression, classifier)) LHSResult.Object(ClassQualifier(calleeExpression, classifier))
} } else {
else {
LHSResult.Error LHSResult.Error
} }
} } else {
else {
val fakeArgument = FakeValueArgumentForLeftCallableReference(ktExpression) val fakeArgument = FakeValueArgumentForLeftCallableReference(ktExpression)
val kotlinCallArgument = createSimplePSICallArgument(context, fakeArgument, lhsResult.typeInfo) val kotlinCallArgument = createSimplePSICallArgument(context, fakeArgument, lhsResult.typeInfo)
@@ -573,15 +607,16 @@ class PSICallResolver(
val qualifier = expressionTypingContext.trace.get(BindingContext.QUALIFIER, qualifiedExpression) val qualifier = expressionTypingContext.trace.get(BindingContext.QUALIFIER, qualifiedExpression)
if (qualifier is ClassQualifier) { if (qualifier is ClassQualifier) {
LHSResult.Type(qualifier, lhsResult.type.unwrap()) LHSResult.Type(qualifier, lhsResult.type.unwrap())
} } else {
else {
LHSResult.Error LHSResult.Error
} }
} }
} }
return CallableReferenceKotlinCallArgumentImpl(ASTScopeTower(context), valueArgument, startDataFlowInfo, newDataFlowInfo, return CallableReferenceKotlinCallArgumentImpl(
ktExpression, argumentName, lhsNewResult, name) ASTScopeTower(context), valueArgument, startDataFlowInfo, newDataFlowInfo,
ktExpression, argumentName, lhsNewResult, name
)
} }
// argumentExpression instead of ktExpression is hack -- type info should be stored also for parenthesized expression // argumentExpression instead of ktExpression is hack -- type info should be stored also for parenthesized expression
@@ -590,29 +625,33 @@ class PSICallResolver(
} }
private fun processFunctionalExpression( private fun processFunctionalExpression(
outerCallContext: BasicCallResolutionContext, outerCallContext: BasicCallResolutionContext,
argumentExpression: KtExpression, argumentExpression: KtExpression,
startDataFlowInfo: DataFlowInfo, startDataFlowInfo: DataFlowInfo,
valueArgument: ValueArgument, valueArgument: ValueArgument,
argumentName: Name?, argumentName: Name?,
builtIns: KotlinBuiltIns builtIns: KotlinBuiltIns
): PSIKotlinCallArgument? { ): PSIKotlinCallArgument? {
val expression = ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(argumentExpression, outerCallContext) ?: return null val expression = ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(argumentExpression, outerCallContext) ?: return null
val postponedExpression = if (expression is KtFunctionLiteral) expression.getParentOfType<KtLambdaExpression>(true) else expression val postponedExpression = if (expression is KtFunctionLiteral) expression.getParentOfType<KtLambdaExpression>(true) else expression
val lambdaArgument: PSIKotlinCallArgument? = when (postponedExpression) { val lambdaArgument: PSIKotlinCallArgument? = when (postponedExpression) {
is KtLambdaExpression -> is KtLambdaExpression ->
LambdaKotlinCallArgumentImpl(outerCallContext, valueArgument, startDataFlowInfo, argumentName, postponedExpression, LambdaKotlinCallArgumentImpl(
argumentExpression, resolveParametersTypes(outerCallContext, postponedExpression.functionLiteral)) outerCallContext, valueArgument, startDataFlowInfo, argumentName, postponedExpression,
argumentExpression, resolveParametersTypes(outerCallContext, postponedExpression.functionLiteral)
)
is KtNamedFunction -> { is KtNamedFunction -> {
val receiverType = resolveType(outerCallContext, postponedExpression.receiverTypeReference) val receiverType = resolveType(outerCallContext, postponedExpression.receiverTypeReference)
val parametersTypes = resolveParametersTypes(outerCallContext, postponedExpression) ?: emptyArray() val parametersTypes = resolveParametersTypes(outerCallContext, postponedExpression) ?: emptyArray()
val returnType = resolveType(outerCallContext, postponedExpression.typeReference) ?: val returnType = resolveType(outerCallContext, postponedExpression.typeReference)
if (postponedExpression.hasBlockBody()) builtIns.unitType else null ?: if (postponedExpression.hasBlockBody()) builtIns.unitType else null
FunctionExpressionImpl(outerCallContext, valueArgument, startDataFlowInfo, argumentName, FunctionExpressionImpl(
argumentExpression, postponedExpression, receiverType, parametersTypes, returnType) outerCallContext, valueArgument, startDataFlowInfo, argumentName,
argumentExpression, postponedExpression, receiverType, parametersTypes, returnType
)
} }
else -> return null else -> return null
@@ -47,16 +47,16 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
class ResolvedAtomCompleter( class ResolvedAtomCompleter(
private val resultSubstitutor: NewTypeSubstitutor, private val resultSubstitutor: NewTypeSubstitutor,
private val trace: BindingTrace, private val trace: BindingTrace,
private val topLevelCallContext: BasicCallResolutionContext, private val topLevelCallContext: BasicCallResolutionContext,
private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer, private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer,
private val expressionTypingServices: ExpressionTypingServices, private val expressionTypingServices: ExpressionTypingServices,
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val doubleColonExpressionResolver: DoubleColonExpressionResolver, private val doubleColonExpressionResolver: DoubleColonExpressionResolver,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
deprecationResolver: DeprecationResolver deprecationResolver: DeprecationResolver
) { ) {
private val callCheckerContext = CallCheckerContext(topLevelCallContext, languageVersionSettings, deprecationResolver) private val callCheckerContext = CallCheckerContext(topLevelCallContext, languageVersionSettings, deprecationResolver)
@@ -79,7 +79,12 @@ class ResolvedAtomCompleter(
fun completeResolvedCall(resolvedCallAtom: ResolvedCallAtom, diagnostics: Collection<KotlinCallDiagnostic>): ResolvedCall<*>? { fun completeResolvedCall(resolvedCallAtom: ResolvedCallAtom, diagnostics: Collection<KotlinCallDiagnostic>): ResolvedCall<*>? {
if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null
val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall<CallableDescriptor>(resolvedCallAtom, trace, resultSubstitutor, diagnostics) val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall<CallableDescriptor>(
resolvedCallAtom,
trace,
resultSubstitutor,
diagnostics
)
kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall, diagnostics) kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall, diagnostics)
kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext) kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext)
@@ -90,20 +95,20 @@ class ResolvedAtomCompleter(
} }
private fun completeLambda(lambda: ResolvedLambdaAtom) { private fun completeLambda(lambda: ResolvedLambdaAtom) {
val returnType = resultSubstitutor.safeSubstitute(lambda.returnType) val returnType = resultSubstitutor.safeSubstitute(lambda.returnType)
updateTraceForLambdaReturnType(lambda, trace, returnType) updateTraceForLambdaReturnType(lambda, trace, returnType)
for (lambdaResult in lambda.resultArguments) { for (lambdaResult in lambda.resultArguments) {
val resultValueArgument = lambdaResult as? PSIKotlinCallArgument ?: continue val resultValueArgument = lambdaResult as? PSIKotlinCallArgument ?: continue
val newContext = val newContext =
topLevelCallContext.replaceDataFlowInfo(resultValueArgument.dataFlowInfoAfterThisArgument) topLevelCallContext.replaceDataFlowInfo(resultValueArgument.dataFlowInfoAfterThisArgument)
.replaceExpectedType(returnType) .replaceExpectedType(returnType)
.replaceBindingTrace(trace) .replaceBindingTrace(trace)
val argumentExpression = resultValueArgument.valueArgument.getArgumentExpression() ?: continue val argumentExpression = resultValueArgument.valueArgument.getArgumentExpression() ?: continue
kotlinToResolvedCallTransformer.updateRecordedType(argumentExpression, newContext, true) kotlinToResolvedCallTransformer.updateRecordedType(argumentExpression, newContext, true)
} }
} }
private fun updateTraceForLambdaReturnType(lambda: ResolvedLambdaAtom, trace: BindingTrace, returnType: UnwrappedType) { private fun updateTraceForLambdaReturnType(lambda: ResolvedLambdaAtom, trace: BindingTrace, returnType: UnwrappedType) {
@@ -123,8 +128,8 @@ class ResolvedAtomCompleter(
else -> throw AssertionError("Unexpected psiCallArgument for resolved lambda argument: $psiCallArgument") else -> throw AssertionError("Unexpected psiCallArgument for resolved lambda argument: $psiCallArgument")
} }
val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl ?: val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl
throw AssertionError("No function descriptor for resolved lambda argument") ?: throw AssertionError("No function descriptor for resolved lambda argument")
functionDescriptor.setReturnType(returnType) functionDescriptor.setReturnType(returnType)
val existingLambdaType = trace.getType(ktArgumentExpression) ?: throw AssertionError("No type for resolved lambda argument") val existingLambdaType = trace.getType(ktArgumentExpression) ?: throw AssertionError("No type for resolved lambda argument")
@@ -132,26 +137,31 @@ class ResolvedAtomCompleter(
} }
private fun completeCallableReference( private fun completeCallableReference(
resolvedAtom: ResolvedCallableReferenceAtom resolvedAtom: ResolvedCallableReferenceAtom
) { ) {
val callableCandidate = resolvedAtom.candidate val callableCandidate = resolvedAtom.candidate
if (callableCandidate == null) { if (callableCandidate == null) {
// todo report meanfull diagnostic here // todo report meanfull diagnostic here
return return
} }
val resultTypeParameters = callableCandidate.freshSubstitutor!!.freshVariables.map { resultSubstitutor.safeSubstitute(it.defaultType) } val resultTypeParameters =
callableCandidate.freshSubstitutor!!.freshVariables.map { resultSubstitutor.safeSubstitute(it.defaultType) }
val psiCallArgument = resolvedAtom.atom.psiCallArgument as CallableReferenceKotlinCallArgumentImpl val psiCallArgument = resolvedAtom.atom.psiCallArgument as CallableReferenceKotlinCallArgumentImpl
val callableReferenceExpression = psiCallArgument.ktCallableReferenceExpression val callableReferenceExpression = psiCallArgument.ktCallableReferenceExpression
val resultSubstitutor = IndexedParametersSubstitution(callableCandidate.candidate.typeParameters, resultTypeParameters.map { it.asTypeProjection() }).buildSubstitutor() val resultSubstitutor = IndexedParametersSubstitution(
callableCandidate.candidate.typeParameters,
resultTypeParameters.map { it.asTypeProjection() }).buildSubstitutor()
// write down type for callable reference expression // write down type for callable reference expression
val resultType = resultSubstitutor.safeSubstitute(callableCandidate.reflectionCandidateType, Variance.INVARIANT) val resultType = resultSubstitutor.safeSubstitute(callableCandidate.reflectionCandidateType, Variance.INVARIANT)
argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(trace, expressionTypingServices.statementFilter, argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(
resultType, trace, expressionTypingServices.statementFilter,
callableReferenceExpression) resultType,
callableReferenceExpression
)
val reference = callableReferenceExpression.callableReference val reference = callableReferenceExpression.callableReference
val explicitCallableReceiver = when (callableCandidate.explicitReceiverKind) { val explicitCallableReceiver = when (callableCandidate.explicitReceiverKind) {
@@ -166,9 +176,11 @@ class ResolvedAtomCompleter(
val tracing = TracingStrategyImpl.create(reference, psiCall) val tracing = TracingStrategyImpl.create(reference, psiCall)
val temporaryTrace = TemporaryBindingTrace.create(trace, "callable reference fake call") val temporaryTrace = TemporaryBindingTrace.create(trace, "callable reference fake call")
val resolvedCall = ResolvedCallImpl(psiCall, callableCandidate.candidate, callableCandidate.dispatchReceiver?.receiver?.receiverValue, val resolvedCall = ResolvedCallImpl(
callableCandidate.extensionReceiver?.receiver?.receiverValue, callableCandidate.explicitReceiverKind, psiCall, callableCandidate.candidate, callableCandidate.dispatchReceiver?.receiver?.receiverValue,
null, temporaryTrace, tracing, MutableDataFlowInfoForArguments.WithoutArgumentsCheck(DataFlowInfo.EMPTY)) callableCandidate.extensionReceiver?.receiver?.receiverValue, callableCandidate.explicitReceiverKind,
null, temporaryTrace, tracing, MutableDataFlowInfoForArguments.WithoutArgumentsCheck(DataFlowInfo.EMPTY)
)
resolvedCall.setResultingSubstitutor(resultSubstitutor) resolvedCall.setResultingSubstitutor(resultSubstitutor)
tracing.bindCall(trace, psiCall) tracing.bindCall(trace, psiCall)
@@ -179,27 +191,40 @@ class ResolvedAtomCompleter(
resolvedCall.markCallAsCompleted() resolvedCall.markCallAsCompleted()
when (callableCandidate.candidate) { when (callableCandidate.candidate) {
is FunctionDescriptor -> doubleColonExpressionResolver.bindFunctionReference(callableReferenceExpression, resultType, topLevelCallContext) is FunctionDescriptor -> doubleColonExpressionResolver.bindFunctionReference(
is PropertyDescriptor -> doubleColonExpressionResolver.bindPropertyReference(callableReferenceExpression, resultType, topLevelCallContext) callableReferenceExpression,
resultType,
topLevelCallContext
)
is PropertyDescriptor -> doubleColonExpressionResolver.bindPropertyReference(
callableReferenceExpression,
resultType,
topLevelCallContext
)
} }
// TODO: probably we should also record key 'DATA_FLOW_INFO_BEFORE', see ExpressionTypingVisitorDispatcher.getTypeInfo // TODO: probably we should also record key 'DATA_FLOW_INFO_BEFORE', see ExpressionTypingVisitorDispatcher.getTypeInfo
trace.recordType(callableReferenceExpression, resultType) trace.recordType(callableReferenceExpression, resultType)
trace.record(BindingContext.PROCESSED, callableReferenceExpression) trace.record(BindingContext.PROCESSED, callableReferenceExpression)
doubleColonExpressionResolver.checkReferenceIsToAllowedMember(callableCandidate.candidate, topLevelCallContext.trace, callableReferenceExpression) doubleColonExpressionResolver.checkReferenceIsToAllowedMember(
callableCandidate.candidate,
topLevelCallContext.trace,
callableReferenceExpression
)
} }
private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) { private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) {
val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl
val context = psiCallArgument.outerCallContext val context = psiCallArgument.outerCallContext
val expectedType = collectionLiteralArgument.expectedType?.let { resultSubstitutor.safeSubstitute(it) } ?: TypeUtils.NO_EXPECTED_TYPE val expectedType =
collectionLiteralArgument.expectedType?.let { resultSubstitutor.safeSubstitute(it) } ?: TypeUtils.NO_EXPECTED_TYPE
val actualContext = context val actualContext = context
.replaceBindingTrace(trace) .replaceBindingTrace(trace)
.replaceExpectedType(expectedType) .replaceExpectedType(expectedType)
.replaceContextDependency(ContextDependency.INDEPENDENT) .replaceContextDependency(ContextDependency.INDEPENDENT)
expressionTypingServices.getTypeInfo(psiCallArgument.collectionLiteralExpression, actualContext) expressionTypingServices.getTypeInfo(psiCallArgument.collectionLiteralExpression, actualContext)
} }
@@ -49,8 +49,8 @@ fun <D : CallableDescriptor> ResolvedCall<D>.hasUnmappedParameters(): Boolean {
return !parameterToArgumentMap.keys.containsAll(resultingDescriptor.valueParameters) return !parameterToArgumentMap.keys.containsAll(resultingDescriptor.valueParameters)
} }
fun <D : CallableDescriptor> ResolvedCall<D>.allArgumentsMapped() fun <D : CallableDescriptor> ResolvedCall<D>.allArgumentsMapped() =
= call.valueArguments.all { argument -> getArgumentMapping(argument) is ArgumentMatch } call.valueArguments.all { argument -> getArgumentMapping(argument) is ArgumentMatch }
fun <D : CallableDescriptor> ResolvedCall<D>.hasTypeMismatchErrorOnParameter(parameter: ValueParameterDescriptor): Boolean { fun <D : CallableDescriptor> ResolvedCall<D>.hasTypeMismatchErrorOnParameter(parameter: ValueParameterDescriptor): Boolean {
val resolvedValueArgument = valueArguments[parameter] val resolvedValueArgument = valueArguments[parameter]
@@ -73,9 +73,9 @@ fun <D : CallableDescriptor> ResolvedCall<D>.usesDefaultArguments(): Boolean {
// call // call
fun <C: ResolutionContext<C>> Call.hasUnresolvedArguments(context: ResolutionContext<C>): Boolean { fun <C : ResolutionContext<C>> Call.hasUnresolvedArguments(context: ResolutionContext<C>): Boolean {
val arguments = valueArguments.map { it.getArgumentExpression() } val arguments = valueArguments.map { it.getArgumentExpression() }
return arguments.any (fun (argument: KtExpression?): Boolean { return arguments.any(fun(argument: KtExpression?): Boolean {
if (argument == null || ArgumentTypeResolver.isFunctionLiteralOrCallableReference(argument, context)) return false if (argument == null || ArgumentTypeResolver.isFunctionLiteralOrCallableReference(argument, context)) return false
val resolvedCall = argument.getResolvedCall(context.trace.bindingContext) as MutableResolvedCall<*>? val resolvedCall = argument.getResolvedCall(context.trace.bindingContext) as MutableResolvedCall<*>?
@@ -91,12 +91,11 @@ fun Call.getValueArgumentsInParentheses(): List<ValueArgument> = valueArguments.
fun KtCallElement.getValueArgumentsInParentheses(): List<ValueArgument> = valueArguments.filterArgsInParentheses() fun KtCallElement.getValueArgumentsInParentheses(): List<ValueArgument> = valueArguments.filterArgsInParentheses()
fun Call.getValueArgumentListOrElement(): KtElement = fun Call.getValueArgumentListOrElement(): KtElement =
if (this is CallTransformer.CallForImplicitInvoke) { if (this is CallTransformer.CallForImplicitInvoke) {
outerCall.getValueArgumentListOrElement() outerCall.getValueArgumentListOrElement()
} } else {
else { valueArgumentList ?: calleeExpression ?: callElement
valueArgumentList ?: calleeExpression ?: callElement }
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun List<ValueArgument?>.filterArgsInParentheses() = filter { it !is KtLambdaArgument } as List<ValueArgument> private fun List<ValueArgument?>.filterArgsInParentheses() = filter { it !is KtLambdaArgument } as List<ValueArgument>
@@ -111,6 +110,7 @@ fun Call.getValueArgumentForExpression(expression: KtExpression): ValueArgument?
else -> null else -> null
} }
} }
fun KtElement.isParenthesizedExpression() = generateSequence(this) { it.deparenthesizeStructurally() }.any { it == expression } fun KtElement.isParenthesizedExpression() = generateSequence(this) { it.deparenthesizeStructurally() }.any { it == expression }
return valueArguments.firstOrNull { it?.getArgumentExpression()?.isParenthesizedExpression() ?: false } return valueArguments.firstOrNull { it?.getArgumentExpression()?.isParenthesizedExpression() ?: false }
} }
@@ -156,8 +156,9 @@ fun KtElement.getCall(context: BindingContext): Call? {
fun KtElement.getParentCall(context: BindingContext, strict: Boolean = true): Call? { fun KtElement.getParentCall(context: BindingContext, strict: Boolean = true): Call? {
val callExpressionTypes = arrayOf<Class<out KtElement>?>( val callExpressionTypes = arrayOf<Class<out KtElement>?>(
KtSimpleNameExpression::class.java, KtCallElement::class.java, KtBinaryExpression::class.java, KtSimpleNameExpression::class.java, KtCallElement::class.java, KtBinaryExpression::class.java,
KtUnaryExpression::class.java, KtArrayAccessExpression::class.java) KtUnaryExpression::class.java, KtArrayAccessExpression::class.java
)
val parent = if (strict) { val parent = if (strict) {
PsiTreeUtil.getParentOfType(this, *callExpressionTypes) PsiTreeUtil.getParentOfType(this, *callExpressionTypes)
@@ -248,20 +249,19 @@ fun Call.isSafeCall(): Boolean {
fun Call.isCallableReference(): Boolean { fun Call.isCallableReference(): Boolean {
val callElement = callElement val callElement = callElement
return callElement is KtNameReferenceExpression && return callElement is KtNameReferenceExpression &&
(callElement.parent as? KtCallableReferenceExpression)?.callableReference == callElement (callElement.parent as? KtCallableReferenceExpression)?.callableReference == callElement
} }
fun Call.createLookupLocation(): KotlinLookupLocation { fun Call.createLookupLocation(): KotlinLookupLocation {
val calleeExpression = calleeExpression val calleeExpression = calleeExpression
val element = val element =
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
else callElement else callElement
return KotlinLookupLocation(element) return KotlinLookupLocation(element)
} }
fun ResolvedCall<*>.getFirstArgumentExpression(): KtExpression? = fun ResolvedCall<*>.getFirstArgumentExpression(): KtExpression? =
valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() }
fun ResolvedCall<*>.getReceiverExpression(): KtExpression? = fun ResolvedCall<*>.getReceiverExpression(): KtExpression? =
extensionReceiver.safeAs<ExpressionReceiver>()?.expression ?: extensionReceiver.safeAs<ExpressionReceiver>()?.expression ?: dispatchReceiver.safeAs<ExpressionReceiver>()?.expression
dispatchReceiver.safeAs<ExpressionReceiver>()?.expression
@@ -26,11 +26,11 @@ import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
object ApiVersionClassifierUsageChecker : ClassifierUsageChecker { object ApiVersionClassifierUsageChecker : ClassifierUsageChecker {
override fun check( override fun check(
targetDescriptor: ClassifierDescriptor, targetDescriptor: ClassifierDescriptor,
trace: BindingTrace, trace: BindingTrace,
element: PsiElement, element: PsiElement,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
deprecationResolver: DeprecationResolver deprecationResolver: DeprecationResolver
) { ) {
targetDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) { version -> targetDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) { version ->
trace.report(Errors.API_NOT_AVAILABLE.on(element, version.versionString, languageVersionSettings.apiVersion.versionString)) trace.report(Errors.API_NOT_AVAILABLE.on(element, version.versionString, languageVersionSettings.apiVersion.versionString))
@@ -29,10 +29,10 @@ import org.jetbrains.kotlin.types.isError
object ConstModifierChecker : SimpleDeclarationChecker { object ConstModifierChecker : SimpleDeclarationChecker {
override fun check( override fun check(
declaration: KtDeclaration, declaration: KtDeclaration,
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
diagnosticHolder: DiagnosticSink, diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext bindingContext: BindingContext
) { ) {
if (descriptor !is VariableDescriptor || !declaration.hasModifier(KtTokens.CONST_KEYWORD)) return if (descriptor !is VariableDescriptor || !declaration.hasModifier(KtTokens.CONST_KEYWORD)) return
@@ -45,11 +45,13 @@ object ConstModifierChecker : SimpleDeclarationChecker {
} }
fun canBeConst(declaration: KtDeclaration, constModifierPsiElement: PsiElement, descriptor: VariableDescriptor): Boolean = fun canBeConst(declaration: KtDeclaration, constModifierPsiElement: PsiElement, descriptor: VariableDescriptor): Boolean =
checkCanBeConst(declaration, constModifierPsiElement, descriptor).canBeConst checkCanBeConst(declaration, constModifierPsiElement, descriptor).canBeConst
private fun checkCanBeConst(declaration: KtDeclaration, private fun checkCanBeConst(
constModifierPsiElement: PsiElement, declaration: KtDeclaration,
descriptor: VariableDescriptor): ConstApplicability { constModifierPsiElement: PsiElement,
descriptor: VariableDescriptor
): ConstApplicability {
if (descriptor.isVar) { if (descriptor.isVar) {
return Errors.WRONG_MODIFIER_TARGET.on(constModifierPsiElement, KtTokens.CONST_KEYWORD, "vars").nonApplicable() return Errors.WRONG_MODIFIER_TARGET.on(constModifierPsiElement, KtTokens.CONST_KEYWORD, "vars").nonApplicable()
} }
@@ -34,12 +34,12 @@ import org.jetbrains.kotlin.resolve.OverridingUtil
class DelegationChecker : DeclarationChecker { class DelegationChecker : DeclarationChecker {
override fun check( override fun check(
declaration: KtDeclaration, declaration: KtDeclaration,
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
diagnosticHolder: DiagnosticSink, diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext, bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
expectActualTracker: ExpectActualTracker expectActualTracker: ExpectActualTracker
) { ) {
if (descriptor !is ClassDescriptor) return if (descriptor !is ClassDescriptor) return
if (declaration !is KtClassOrObject) return if (declaration !is KtClassOrObject) return
@@ -57,21 +57,27 @@ class DelegationChecker : DeclarationChecker {
} }
private fun checkDescriptor( private fun checkDescriptor(
classDeclaration: KtClassOrObject, classDeclaration: KtClassOrObject,
delegatedDescriptor: CallableMemberDescriptor, delegatedDescriptor: CallableMemberDescriptor,
delegatedToDescriptor: CallableMemberDescriptor, delegatedToDescriptor: CallableMemberDescriptor,
diagnosticHolder: DiagnosticSink diagnosticHolder: DiagnosticSink
) { ) {
val reachableFromDelegated = val reachableFromDelegated =
OverridingUtil.filterOutOverridden( OverridingUtil.filterOutOverridden(
DescriptorUtils.getAllOverriddenDescriptors(delegatedDescriptor).filter { it.kind.isReal }.toSet() DescriptorUtils.getAllOverriddenDescriptors(delegatedDescriptor).filter { it.kind.isReal }.toSet()
) - DescriptorUtils.unwrapFakeOverride(delegatedToDescriptor).original ) - DescriptorUtils.unwrapFakeOverride(delegatedToDescriptor).original
val nonAbstractReachable = reachableFromDelegated.filter { it.modality == Modality.OPEN } val nonAbstractReachable = reachableFromDelegated.filter { it.modality == Modality.OPEN }
if (nonAbstractReachable.isNotEmpty()) { if (nonAbstractReachable.isNotEmpty()) {
/*In case of MANY_IMPL_MEMBER_NOT_IMPLEMENTED error there could be several elements otherwise only one*/ /*In case of MANY_IMPL_MEMBER_NOT_IMPLEMENTED error there could be several elements otherwise only one*/
diagnosticHolder.report(DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE.on(classDeclaration, delegatedDescriptor, nonAbstractReachable)) diagnosticHolder.report(
DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE.on(
classDeclaration,
delegatedDescriptor,
nonAbstractReachable
)
)
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More