diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 5d64b5fd14a..ec9e068678c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -714,7 +714,8 @@ public interface Errors { DiagnosticFactory1 NAMED_ARGUMENTS_NOT_ALLOWED = DiagnosticFactory1.create(ERROR); enum BadNamedArgumentsTarget { - NON_KOTLIN_FUNCTION, + NON_KOTLIN_FUNCTION, // a function provided by non-Kotlin artifact, ex: Java function + INTEROP_FUNCTION, // deserialized Kotlin function that serves as a bridge to a function written in another language, ex: Obj-C INVOKE_ON_FUNCTION_TYPE, EXPECTED_CLASS_MEMBER, } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index f493c66fa7d..a9f79469e77 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -197,6 +197,8 @@ public class DefaultErrorMessages { switch (target) { case NON_KOTLIN_FUNCTION: return "non-Kotlin functions"; + case INTEROP_FUNCTION: + return "interop functions with ambiguous parameter names"; case INVOKE_ON_FUNCTION_TYPE: return "function types"; case EXPECTED_CLASS_MEMBER: diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 7d19c1ea5af..76db25492b8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -11,8 +11,7 @@ import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.* -import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.INVOKE_ON_FUNCTION_TYPE -import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION +import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.* import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isNull @@ -32,6 +31,7 @@ import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.intersectWrappedTypes import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils @@ -260,7 +260,11 @@ class DiagnosticReporterByTrackingStrategy( NamedArgumentNotAllowed::class.java -> trace.report( NAMED_ARGUMENTS_NOT_ALLOWED.on( nameReference, - if ((diagnostic as NamedArgumentNotAllowed).descriptor is FunctionInvokeDescriptor) INVOKE_ON_FUNCTION_TYPE else NON_KOTLIN_FUNCTION + when ((diagnostic as NamedArgumentNotAllowed).descriptor) { + is FunctionInvokeDescriptor -> INVOKE_ON_FUNCTION_TYPE + is DeserializedCallableMemberDescriptor -> INTEROP_FUNCTION + else -> NON_KOTLIN_FUNCTION + } ) ) ArgumentPassedTwice::class.java -> trace.report(ARGUMENT_PASSED_TWICE.on(nameReference)) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java index 3a0ae7dc46f..0a52140561a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.components.ArgumentsUtilsKt; import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy; +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import java.util.*; @@ -185,10 +186,16 @@ public class ValueArgumentsToParametersMapper { report(NAMED_ARGUMENTS_NOT_ALLOWED.on(nameReference, EXPECTED_CLASS_MEMBER)); } else if (!candidate.hasStableParameterNames()) { - report(NAMED_ARGUMENTS_NOT_ALLOWED.on( - nameReference, - candidate instanceof FunctionInvokeDescriptor ? INVOKE_ON_FUNCTION_TYPE : NON_KOTLIN_FUNCTION - )); + BadNamedArgumentsTarget badNamedArgumentsTarget; + if (candidate instanceof FunctionInvokeDescriptor) { + badNamedArgumentsTarget = INVOKE_ON_FUNCTION_TYPE; + } else if (candidate instanceof DeserializedCallableMemberDescriptor) { + badNamedArgumentsTarget = INTEROP_FUNCTION; + } else { + badNamedArgumentsTarget = NON_KOTLIN_FUNCTION; + } + + report(NAMED_ARGUMENTS_NOT_ALLOWED.on(nameReference, badNamedArgumentsTarget)); } } diff --git a/compiler/testData/diagnostics/nonStableParameterNames/library.kt b/compiler/testData/diagnostics/nonStableParameterNames/library.kt new file mode 100644 index 00000000000..432c0e62540 --- /dev/null +++ b/compiler/testData/diagnostics/nonStableParameterNames/library.kt @@ -0,0 +1,15 @@ +package library + +fun foo(a: Int, b: String, c: Boolean) = Unit + +class Foo(a: Int, b: String, c: Boolean) { + constructor(a: Int, b: String, c: Boolean, d: Double) : this(a, b, c) + + fun foo(a: Int, b: String, c: Boolean) = Unit + + class Bar(a: Int, b: String, c: Boolean) { + constructor(a: Int, b: String, c: Boolean, d: Double) : this(a, b, c) + + fun bar(a: Int, b: String, c: Boolean) = Unit + } +} diff --git a/compiler/testData/diagnostics/nonStableParameterNames/test.kt b/compiler/testData/diagnostics/nonStableParameterNames/test.kt new file mode 100644 index 00000000000..9170ae58d19 --- /dev/null +++ b/compiler/testData/diagnostics/nonStableParameterNames/test.kt @@ -0,0 +1,48 @@ +// !WITH_NEW_INFERENCE +// !LANGUAGE: +NewInference +// MODULE: m1-common +// FILE: test.kt + +import library.* + +fun test() { + foo( + a = 42, + b = "hello", + c = true + ) + + Foo( + a = 42, + b = "hello", + c = true + ).foo( + a = 42, + b = "hello", + c = true + ) + + Foo( + a = 42, + b = "hello", + c = true, + 3.14 + ) + + Foo.Bar( + a = 42, + b = "hello", + c = true + ).bar( + a = 42, + b = "hello", + c = true + ) + + Foo.Bar( + a = 42, + b = "hello", + c = true, + 3.14 + ) +} diff --git a/compiler/testData/diagnostics/nonStableParameterNames/test.txt b/compiler/testData/diagnostics/nonStableParameterNames/test.txt new file mode 100644 index 00000000000..93e27f34c8c --- /dev/null +++ b/compiler/testData/diagnostics/nonStableParameterNames/test.txt @@ -0,0 +1,3 @@ +package + +public fun test(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/InteropFunctionsWithNonStableParameterNamesDiagnosticsTest.kt b/compiler/tests/org/jetbrains/kotlin/checkers/InteropFunctionsWithNonStableParameterNamesDiagnosticsTest.kt new file mode 100644 index 00000000000..e71724ccb7b --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/checkers/InteropFunctionsWithNonStableParameterNamesDiagnosticsTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.checkers + +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer +import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices +import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory +import org.jetbrains.kotlin.builtins.DefaultBuiltIns +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import org.jetbrains.kotlin.serialization.NonStableParameterNamesSerializationTest +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.test.KlibTestUtil +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +class InteropFunctionsWithNonStableParameterNamesDiagnosticsTest : AbstractDiagnosticsTest() { + private lateinit var klibFile: File + + override fun setUp() { + super.setUp() + + val tmpDir = KotlinTestUtils.tmpDirForTest(this) + klibFile = prepareKlibWithNonStableParameterNames(tmpDir) + } + + override fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map>) = true + + override fun getEnvironmentConfigFiles(): EnvironmentConfigFiles = EnvironmentConfigFiles.METADATA_CONFIG_FILES + + override fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl = + ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, DefaultBuiltIns.Instance) + + override fun getAdditionalDependencies(module: ModuleDescriptorImpl): List = + listOf(KlibTestUtil.deserializeKlibToCommonModule(klibFile)) + + override fun analyzeModuleContents( + moduleContext: ModuleContext, + files: List, + moduleTrace: BindingTrace, + languageVersionSettings: LanguageVersionSettings, + separateModules: Boolean, + jvmTarget: JvmTarget + ): AnalysisResult { + return CommonResolverForModuleFactory.analyzeFiles( + files = files, + moduleName = moduleContext.module.name, + dependOnBuiltIns = true, + languageVersionSettings = languageVersionSettings, + targetPlatform = CommonPlatforms.defaultCommonPlatform, + capabilities = mapOf( +// MODULE_FILES to files + ), + dependenciesContainer = CommonDependenciesContainerImpl(moduleContext.module.allDependencyModules) + ) { content -> + environment.createPackagePartProvider(content.moduleContentScope) + } + } + + fun testInteropFunctionsWithNonStableParameterNames() { + doTest(File(TEST_DATA_DIR, "test.kt").path) + } + + private class CommonDependenciesContainerImpl(dependees: Collection) : CommonDependenciesContainer { + private class ModuleInfoImpl(val module: ModuleDescriptor) : ModuleInfo { + override val name: Name get() = module.name + + override fun dependencies(): List = listOf(this) + override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns = ModuleInfo.DependencyOnBuiltIns.LAST + + override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform + override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices + } + + private val dependeeModuleInfos: List = dependees.map(::ModuleInfoImpl) + + override val moduleInfos: List get() = dependeeModuleInfos + + override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor { + // let's assume there is a few module infos at all + return dependeeModuleInfos.firstOrNull { it === moduleInfo }?.module + ?: error("Unknown module info $moduleInfo") + } + + override fun registerDependencyForAllModules(moduleInfo: ModuleInfo, descriptorForModule: ModuleDescriptorImpl) = Unit + override fun packageFragmentProviderForModuleInfo(moduleInfo: ModuleInfo): PackageFragmentProvider? = null + + override val friendModuleInfos: List get() = emptyList() + override val refinesModuleInfos: List get() = dependeeModuleInfos + } + + companion object { + private const val TEST_DATA_DIR = "compiler/testData/diagnostics/nonStableParameterNames" + + private fun prepareKlibWithNonStableParameterNames(tmpDir: File): File { + val libraryName = "library" + + val klibFile = tmpDir.resolve("$libraryName.klib") + KlibTestUtil.compileCommonSourcesToKlib(listOf(File(TEST_DATA_DIR, "library.kt")), libraryName, klibFile) + + val module = KlibTestUtil.deserializeKlibToCommonModule(klibFile) + NonStableParameterNamesSerializationTest.collectCallablesForPatch(module).forEach { it.setHasStableParameterNames(false) } + + klibFile.delete() + KlibTestUtil.serializeCommonModuleToKlib(module, libraryName, klibFile) + + return klibFile + } + + } +} + diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt index a7ef57ff556..d1c51795abd 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt @@ -51,7 +51,7 @@ class NonStableParameterNamesSerializationTest : TestCaseWithTmpdir() { companion object { private const val SOURCE_FILE = "compiler/testData/serialization/nonStableParameterNames/test.kt" - private fun collectCallablesForPatch(module: ModuleDescriptorImpl): List { + fun collectCallablesForPatch(module: ModuleDescriptorImpl): List { fun DeclarationDescriptor.castToFunctionImpl(): FunctionDescriptorImpl = assertedCast { "Not an instance of ${FunctionDescriptorImpl::class.java}: ${this::class.java}, $this" }