NI: Allow to resolve to functions with SAM conversion and passing array without spread as vararg (with warning)

^KT-35224 Fixed
This commit is contained in:
Victor Petukhov
2019-12-04 20:29:03 +03:00
parent fe875d628d
commit 07269661b4
33 changed files with 573 additions and 27 deletions
@@ -18603,6 +18603,21 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/samConversions"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentProhibited.kt")
public void testArrayAsVarargAfterSamArgumentProhibited() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentProhibited.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("checkSamConversionsAreDisabledByDefault.kt")
public void testCheckSamConversionsAreDisabledByDefault() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/checkSamConversionsAreDisabledByDefault.kt");
@@ -46,7 +46,8 @@ class JavaSyntheticScopes(
init {
val samConversionPerArgumentIsEnabled =
languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument)
languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) &&
languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVarargAsArrayAfterSamArgument)
val javaSyntheticPropertiesScope = JavaSyntheticPropertiesScope(storageManager, lookupTracker)
val scopesFromExtensions = SyntheticScopeProviderExtension
@@ -59,7 +60,10 @@ class JavaSyntheticScopes(
samConventionResolver,
deprecationResolver,
lookupTracker,
samViaSyntheticScopeDisabled = samConversionPerArgumentIsEnabled
samViaSyntheticScopeDisabled = samConversionPerArgumentIsEnabled,
shouldGenerateCandidateForVarargAfterSam = !languageVersionSettings.supportsFeature(
LanguageFeature.ProhibitVarargAsArrayAfterSamArgument
)
)
scopes = listOf(javaSyntheticPropertiesScope, samAdapterFunctionsScope) + scopesFromExtensions
@@ -70,7 +74,8 @@ class JavaSyntheticScopes(
samConventionResolver,
deprecationResolver,
lookupTracker,
samViaSyntheticScopeDisabled = false
samViaSyntheticScopeDisabled = false,
shouldGenerateCandidateForVarargAfterSam = false
)
scopesWithForceEnabledSamAdapters =
@@ -17,8 +17,6 @@
package org.jetbrains.kotlin.synthetic
import com.intellij.util.SmartList
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
@@ -37,6 +35,7 @@ import org.jetbrains.kotlin.load.java.sam.SamAdapterDescriptor
import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.inference.wrapWithCapturingSubstitution
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -58,8 +57,10 @@ class SamAdapterFunctionsScope(
private val samResolver: SamConversionResolver,
private val deprecationResolver: DeprecationResolver,
private val lookupTracker: LookupTracker,
private val samViaSyntheticScopeDisabled: Boolean
private val samViaSyntheticScopeDisabled: Boolean,
private val shouldGenerateCandidateForVarargAfterSam: Boolean
) : SyntheticScope.Default() {
private val shouldGenerateAdditionalSamCandidate = !samViaSyntheticScopeDisabled || shouldGenerateCandidateForVarargAfterSam
private val extensionForFunction =
storageManager.createMemoizedFunctionWithNullableValues<FunctionDescriptor, FunctionDescriptor> { function ->
@@ -96,12 +97,18 @@ class SamAdapterFunctionsScope(
return SamAdapterExtensionFunctionDescriptorImpl.create(function, samResolver)
}
override fun getSyntheticMemberFunctions(receiverTypes: Collection<KotlinType>, name: Name, location: LookupLocation): Collection<FunctionDescriptor> {
if (samViaSyntheticScopeDisabled) return emptyList()
override fun getSyntheticMemberFunctions(
receiverTypes: Collection<KotlinType>,
name: Name,
location: LookupLocation
): Collection<FunctionDescriptor> {
if (!shouldGenerateAdditionalSamCandidate) return emptyList()
var result: SmartList<FunctionDescriptor>? = null
for (type in receiverTypes) {
for (function in type.memberScope.getContributedFunctions(name, location)) {
if (samViaSyntheticScopeDisabled && !function.shouldGenerateCandidateForVarargAfterSamAndHasVararg) continue
val extension = extensionForFunction(function.original)?.substituteForReceiverType(type)
if (extension != null) {
recordSamLookupsForParameters(function, location)
@@ -125,6 +132,9 @@ class SamAdapterFunctionsScope(
}
}
private val FunctionDescriptor.shouldGenerateCandidateForVarargAfterSamAndHasVararg
get() = shouldGenerateCandidateForVarargAfterSam && valueParameters.lastOrNull()?.isVararg == true
private fun FunctionDescriptor.substituteForReceiverType(receiverType: KotlinType): FunctionDescriptor? {
val containingClass = containingDeclaration as? ClassDescriptor ?: return null
val correspondingSupertype = findCorrespondingSupertype(receiverType, containingClass.defaultType) ?: return null
@@ -138,19 +148,22 @@ class SamAdapterFunctionsScope(
}
override fun getSyntheticMemberFunctions(receiverTypes: Collection<KotlinType>): Collection<FunctionDescriptor> {
if (samViaSyntheticScopeDisabled) return emptyList()
if (!shouldGenerateAdditionalSamCandidate) return emptyList()
return receiverTypes.flatMapTo(LinkedHashSet<FunctionDescriptor>()) { type ->
type.memberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
.filterIsInstance<FunctionDescriptor>()
.mapNotNull {
extensionForFunction(it.original)?.substituteForReceiverType(type)
}
.filterIsInstance<FunctionDescriptor>()
.run {
if (samViaSyntheticScopeDisabled) filter { it.shouldGenerateCandidateForVarargAfterSamAndHasVararg } else this
}
.mapNotNull {
extensionForFunction(it.original)?.substituteForReceiverType(type)
}
}
}
override fun getSyntheticStaticFunctions(scope: ResolutionScope, name: Name, location: LookupLocation): Collection<FunctionDescriptor> {
if (samViaSyntheticScopeDisabled) return emptyList()
if (!shouldGenerateAdditionalSamCandidate) return emptyList()
return getSamFunctions(scope.getContributedFunctions(name, location), location)
}
@@ -159,7 +172,7 @@ class SamAdapterFunctionsScope(
val classifier = scope.getContributedClassifier(name, location) ?: return emptyList()
recordSamLookupsToClassifier(classifier, location)
if (samViaSyntheticScopeDisabled) return listOfNotNull(getSamConstructor(classifier))
if (!shouldGenerateAdditionalSamCandidate) return listOfNotNull(getSamConstructor(classifier))
return getAllSamConstructors(classifier)
}
@@ -173,7 +186,7 @@ class SamAdapterFunctionsScope(
}
override fun getSyntheticStaticFunctions(scope: ResolutionScope): Collection<FunctionDescriptor> {
if (samViaSyntheticScopeDisabled) return emptyList()
if (!shouldGenerateAdditionalSamCandidate) return emptyList()
return getSamFunctions(scope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS), location = null)
}
@@ -181,13 +194,13 @@ class SamAdapterFunctionsScope(
override fun getSyntheticConstructors(scope: ResolutionScope): Collection<FunctionDescriptor> {
val classifiers = scope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS).filterIsInstance<ClassifierDescriptor>()
if (samViaSyntheticScopeDisabled) return classifiers.mapNotNull { getSamConstructor(it) }
if (!shouldGenerateAdditionalSamCandidate) return classifiers.mapNotNull { getSamConstructor(it) }
return classifiers.flatMap { getAllSamConstructors(it) }
}
override fun getSyntheticConstructor(constructor: ConstructorDescriptor): ConstructorDescriptor? {
if (samViaSyntheticScopeDisabled) return null
if (samViaSyntheticScopeDisabled && !constructor.shouldGenerateCandidateForVarargAfterSamAndHasVararg) return null
return when (constructor) {
is JavaClassConstructorDescriptor -> createJavaSamAdapterConstructor(constructor)
@@ -211,10 +224,14 @@ class SamAdapterFunctionsScope(
functions: Collection<DeclarationDescriptor>,
location: LookupLocation?
): List<SamAdapterDescriptor<JavaMethodDescriptor>> {
if (!shouldGenerateAdditionalSamCandidate) return emptyList()
return functions.mapNotNull { function ->
if (function !is JavaMethodDescriptor) return@mapNotNull null
if (function.dispatchReceiverParameter != null) return@mapNotNull null // consider only statics
if (!SingleAbstractMethodUtils.isSamAdapterNecessary(function)) return@mapNotNull null
if (samViaSyntheticScopeDisabled && !function.shouldGenerateCandidateForVarargAfterSamAndHasVararg)
return@mapNotNull null
location?.let { recordSamLookupsForParameters(function, it) }
@@ -227,7 +244,7 @@ class SamAdapterFunctionsScope(
}
private fun getSamAdaptersFromConstructors(classifier: ClassifierDescriptor): List<FunctionDescriptor> {
if (classifier !is JavaClassDescriptor) return emptyList()
if (!shouldGenerateAdditionalSamCandidate || classifier !is JavaClassDescriptor) return emptyList()
return arrayListOf<FunctionDescriptor>().apply {
for (constructor in classifier.constructors) {
@@ -765,6 +765,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, TypeParameterDescriptor> TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtElement, KotlinType, KotlinType> TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtExpression> TYPE_INFERENCE_FAILED_ON_SPECIAL_CONSTRUCT = DiagnosticFactory0.create(ERROR, SPECIAL_CONSTRUCT_TOKEN);
@@ -859,6 +859,7 @@ public class DefaultErrorMessages {
"(argument types, receiver type or expected type). Try to specify it explicitly.", NAME);
MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "{0}", TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER);
MAP.put(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, "Type inference failed. Expected type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
MAP.put(TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG, "Please use spread operator to pass an array as vararg. It will be an error in 1.5.");
MAP.put(TYPE_INFERENCE_FAILED_ON_SPECIAL_CONSTRUCT, "Type inference for control flow expression failed. Please specify its type explicitly.");
@@ -175,6 +175,10 @@ class DiagnosticReporterByTrackingStrategy(
}
}
ResolvedToSamWithVarargDiagnostic::class.java -> {
trace.report(TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG.on(callArgument.psiCallArgument.valueArgument.asElement()))
}
}
}
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
@@ -45,6 +47,7 @@ class KotlinCallCompleter(
candidate.addExpectedTypeConstraint(returnType, expectedType)
candidate.addExpectedTypeFromCastConstraint(returnType, resolutionCallbacks)
candidate.checkSamWithVararg(diagnosticHolder)
return if (resolutionCallbacks.inferenceSession.shouldRunCompletion(candidate))
candidate.runCompletion(
@@ -56,6 +59,22 @@ class KotlinCallCompleter(
candidate.asCallResolutionResult(ConstraintSystemCompletionMode.PARTIAL, diagnosticHolder)
}
private fun KotlinResolutionCandidate.checkSamWithVararg(diagnosticHolder: KotlinDiagnosticsHolder.SimpleHolder) {
val samConversionPerArgumentWithWarningsForVarargAfterSam =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) &&
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVarargAsArrayAfterSamArgument)
if (samConversionPerArgumentWithWarningsForVarargAfterSam && resolvedCall.candidateDescriptor is SyntheticMemberDescriptor<*>) {
val declarationDescriptor = resolvedCall.candidateDescriptor.baseDescriptorForSynthetic as? FunctionDescriptor ?: return
if (declarationDescriptor.valueParameters.lastOrNull()?.isVararg == true) {
diagnosticHolder.addDiagnostic(
ResolvedToSamWithVarargDiagnostic(resolvedCall.atom.argumentsInParenthesis.lastOrNull() ?: return)
)
}
}
}
fun createAllCandidatesResult(
candidates: Collection<KotlinResolutionCandidate>,
expectedType: UnwrappedType?,
@@ -294,7 +294,11 @@ private fun KotlinResolutionCandidate.getExpectedTypeWithSAMConversion(
argument: KotlinCallArgument,
candidateParameter: ParameterDescriptor
): UnwrappedType? {
if (!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument)) return null
val generatingAdditionalSamCandidateIsEnabled =
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) &&
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVarargAsArrayAfterSamArgument)
if (generatingAdditionalSamCandidateIsEnabled) return null
if (!callComponents.samConversionTransformer.shouldRunSamConversionForFunction(resolvedCall.candidateDescriptor)) return null
val argumentIsFunctional = when (argument) {
@@ -202,4 +202,10 @@ class OnlyInputTypesDiagnostic(val typeVariable: NewTypeVariable) : KotlinCallDi
override fun report(reporter: DiagnosticReporter) {
reporter.onCall(this)
}
}
}
class ResolvedToSamWithVarargDiagnostic(val argument: KotlinCallArgument) : KotlinCallDiagnostic(RESOLVED_TO_SAM_WITH_VARARG) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(argument, this)
}
}
@@ -83,6 +83,7 @@ enum class ResolutionCandidateApplicability {
INAPPLICABLE_ARGUMENTS_MAPPING_ERROR, // arguments not mapped to parameters (i.e. different size of arguments and parameters)
INAPPLICABLE_WRONG_RECEIVER, // receiver not matched
HIDDEN, // removed from resolve
RESOLVED_TO_SAM_WITH_VARARG, // migration warning up to 1.5 (when resolve to function with SAM conversion and array without spread as vararg)
}
abstract class ResolutionDiagnostic(candidateApplicability: ResolutionCandidateApplicability) :
@@ -0,0 +1,53 @@
// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions +SamConversionPerArgument
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: Test.java
public class Test {
public static String foo1(Runnable r, String... strs) {
return null;
}
public String foo2(Runnable r1, Runnable r2, String... strs) {
return null;
}
public Test(Runnable r, String... strs) {}
public Test(Runnable r1, Runnable r2, String... strs) {}
}
// FILE: main.kt
fun box(): String {
val x1 = {}
val x2: Runnable = Runnable { }
val x3 = arrayOf<String>()
Test.foo1({}, arrayOf())
Test.foo1({}, *arrayOf())
Test.foo1({}, x3)
Test.foo1({}, *arrayOf(""))
Test.foo1(x1, arrayOf())
Test.foo1(x1, *arrayOf())
Test.foo1(x2, *arrayOf())
Test.foo1(x1, x3)
Test.foo1(x1, *x3)
Test.foo1(x2, *arrayOf(""))
val i1 = Test({}, arrayOf())
val i2 = Test({}, *arrayOf())
val i3 = Test({}, x3)
val i4 = Test({}, arrayOf(""))
val i5 = Test({}, {}, *arrayOf(""))
val i6 = Test({}, {}, arrayOf())
i1.foo2({}, {}, arrayOf())
i2.foo2({}, {}, *arrayOf())
i3.foo2(x2, {}, *arrayOf())
i4.foo2({}, {}, arrayOf(""))
i5.foo2({}, {}, *x3)
i6.foo2(x2, {}, *arrayOf(""))
return "OK"
}
@@ -0,0 +1,41 @@
// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// FILE: Test.java
public class Test {
public static String foo1(Runnable r, String... strs) {
return null;
}
public String foo2(Runnable r1, Runnable r2, String... strs) {
return null;
}
public Test(Runnable r, String... strs) {}
public Test(Runnable r1, Runnable r2, String... strs) {}
}
// FILE: main.kt
fun box(): String {
val x1 = {}
val x2: Runnable = Runnable { }
val x3 = arrayOf<String>()
Test.foo1({}, arrayOf())
Test.foo1({}, arrayOf(""))
Test.foo1(x1, arrayOf())
Test.foo1(x2, *arrayOf())
Test.foo1(x1, x3)
Test.foo1(x2, *arrayOf(""))
val i1 = Test({}, arrayOf())
val i3 = Test({}, x3)
val i4 = Test({}, arrayOf(""))
val i6 = Test({}, {}, arrayOf())
i1.foo2({}, {}, arrayOf())
i1.foo2({}, {}, arrayOf(""))
return "OK"
}
@@ -1,4 +1,4 @@
// !LANGUAGE: +SamConversionPerArgument +NewInference
// !LANGUAGE: +SamConversionPerArgument +NewInference +ProhibitVarargAsArrayAfterSamArgument
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: A.java
@@ -0,0 +1,54 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions +SamConversionPerArgument
// IGNORE_BACKEND: JS
// SKIP_TXT
// FILE: Test.java
public class Test {
public static String foo1(Runnable r, String... strs) {
return null;
}
public String foo2(Runnable r1, Runnable r2, String... strs) {
return null;
}
public Test(Runnable r, String... strs) {}
public Test(Runnable r, Runnable r, String... strs) {}
}
// FILE: main.kt
fun main(x2: Runnable) {
val x1 = {}
val x3 = arrayOf<String>()
Test.foo1({}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
Test.foo1({}, *arrayOf())
Test.foo1({}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>x3<!>)
Test.foo1({}, *arrayOf(""))
Test.foo1(x1, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
Test.foo1(x1, *arrayOf())
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf()<!>)
Test.foo1(x2, *arrayOf())
Test.foo1(x1, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>x3<!>)
Test.foo1(x1, *x3)
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf("")<!>)
Test.foo1(x2, *arrayOf(""))
val i1 = Test({}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
val i2 = Test({}, *arrayOf())
val i3 = Test({}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>x3<!>)
val i4 = Test({}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf("")<!>)
val i5 = Test({}, {}, *arrayOf(""))
val i6 = Test({}, {}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
i1.foo2({}, {}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
i1.foo2({}, {}, *arrayOf())
i1.foo2({}, <!TYPE_MISMATCH!>x2<!>, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf()<!>)
i1.foo2(x2, {}, *arrayOf())
i1.foo2({}, {}, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>arrayOf("")<!>)
i1.foo2({}, {}, *x3)
i1.foo2({}, <!TYPE_MISMATCH!>x2<!>, <!TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG!>x3<!>)
i1.foo2(x2, {}, *arrayOf(""))
}
@@ -0,0 +1,54 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions +SamConversionPerArgument +ProhibitVarargAsArrayAfterSamArgument
// IGNORE_BACKEND: JS
// SKIP_TXT
// FILE: Test.java
public class Test {
public static String foo1(Runnable r, String... strs) {
return null;
}
public String foo2(Runnable r, Runnable r, String... strs) {
return null;
}
public Test(Runnable r, String... strs) {}
public Test(Runnable r, Runnable r, String... strs) {}
}
// FILE: main.kt
fun main(x2: Runnable) {
val x1 = {}
val x3 = arrayOf<String>()
Test.foo1({}, <!TYPE_MISMATCH!>arrayOf()<!>)
Test.foo1({}, *arrayOf())
Test.foo1({}, *x3)
Test.foo1({}, <!TYPE_MISMATCH!>arrayOf("")<!>)
Test.foo1(x1, <!TYPE_MISMATCH!>arrayOf()<!>)
Test.foo1(x1, *arrayOf())
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf()<!>)
Test.foo1(x2, *arrayOf())
Test.foo1(x1, <!TYPE_MISMATCH!>x3<!>)
Test.foo1(x1, *x3)
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf("")<!>)
Test.foo1(x2, *arrayOf(""))
val i1 = <!NONE_APPLICABLE!>Test<!>({}, arrayOf())
val i2 = Test({}, *arrayOf())
val i3 = <!NONE_APPLICABLE!>Test<!>({}, x3)
val i4 = <!NONE_APPLICABLE!>Test<!>({}, arrayOf(""))
val i5 = Test({}, {}, *arrayOf(""))
val i6 = <!NONE_APPLICABLE!>Test<!>({}, {}, arrayOf())
i2.foo2({}, {}, <!TYPE_MISMATCH!>arrayOf()<!>)
i2.foo2({}, {}, *arrayOf())
i2.foo2({}, x2, <!TYPE_MISMATCH!>arrayOf()<!>)
i2.foo2(x2, {}, *arrayOf())
i2.foo2({}, {}, <!TYPE_MISMATCH!>arrayOf("")<!>)
i2.foo2({}, {}, *x3)
i2.foo2({}, x2, <!TYPE_MISMATCH!>x3<!>)
i2.foo2(x2, {}, *arrayOf(""))
}
@@ -0,0 +1,54 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions
// IGNORE_BACKEND: JS, JS_IR
// SKIP_TXT
// FILE: Test.java
public class Test {
public static String foo1(Runnable r, String... strs) {
return null;
}
public String foo2(Runnable r, Runnable r, String... strs) {
return null;
}
public Test(Runnable r, String... strs) {}
public Test(Runnable r, Runnable r, String... strs) {}
}
// FILE: main.kt
fun main(x2: Runnable) {
val x1 = {}
val x3 = arrayOf<String>()
Test.foo1({}, arrayOf())
Test.foo1(<!TYPE_MISMATCH!>{}<!>, *arrayOf())
Test.foo1(<!TYPE_MISMATCH!>{}<!>, *x3)
Test.foo1({}, arrayOf(""))
Test.foo1(x1, arrayOf())
Test.foo1(<!TYPE_MISMATCH!>x1<!>, *arrayOf())
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf()<!>)
Test.foo1(x2, *arrayOf())
Test.foo1(x1, x3)
Test.foo1(<!TYPE_MISMATCH!>x1<!>, *x3)
Test.foo1(x2, <!TYPE_MISMATCH!>arrayOf("")<!>)
Test.foo1(x2, *arrayOf(""))
val i1 = Test({}, arrayOf())
val i2 = <!NONE_APPLICABLE!>Test<!>({}, *arrayOf())
val i3 = Test({}, x3)
val i4 = Test({}, arrayOf(""))
val i5 = <!NONE_APPLICABLE!>Test<!>({}, {}, *arrayOf(""))
val i6 = Test({}, {}, arrayOf())
i1.foo2({}, {}, arrayOf())
i1.foo2(<!TYPE_MISMATCH!>{}<!>, <!TYPE_MISMATCH!>{}<!>, *arrayOf())
i1.foo2(<!TYPE_MISMATCH!>{}<!>, x2, <!TYPE_MISMATCH!>arrayOf()<!>)
i1.foo2(x2, <!TYPE_MISMATCH!>{}<!>, *arrayOf())
i1.foo2({}, {}, arrayOf(""))
i1.foo2(<!TYPE_MISMATCH!>{}<!>, <!TYPE_MISMATCH!>{}<!>, *x3)
i1.foo2(<!TYPE_MISMATCH!>{}<!>, x2, <!TYPE_MISMATCH!>x3<!>)
i1.foo2(x2, <!TYPE_MISMATCH!>{}<!>, *arrayOf(""))
}
@@ -1,4 +1,4 @@
// !LANGUAGE: +NewInference +SamConversionPerArgument
// !LANGUAGE: +NewInference +SamConversionPerArgument +ProhibitVarargAsArrayAfterSamArgument
// FILE: samConversionInGenericConstructorCall.kt
fun test1(f: (String) -> String) = C(f)
@@ -1,4 +1,4 @@
// !LANGUAGE: +NewInference +SamConversionPerArgument +SamConversionForKotlinFunctions
// !LANGUAGE: +NewInference +SamConversionPerArgument +SamConversionForKotlinFunctions +ProhibitVarargAsArrayAfterSamArgument
// FILE: samConversionInGenericConstructorCall_NI.kt
fun test3(
f1: (String) -> String,
@@ -18615,6 +18615,21 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/samConversions"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentProhibited.kt")
public void testArrayAsVarargAfterSamArgumentProhibited() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentProhibited.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("checkSamConversionsAreDisabledByDefault.kt")
public void testCheckSamConversionsAreDisabledByDefault() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/checkSamConversionsAreDisabledByDefault.kt");
@@ -18605,6 +18605,21 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/samConversions"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentProhibited.kt")
public void testArrayAsVarargAfterSamArgumentProhibited() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentProhibited.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("checkSamConversionsAreDisabledByDefault.kt")
public void testCheckSamConversionsAreDisabledByDefault() throws Exception {
runTest("compiler/testData/diagnostics/tests/samConversions/checkSamConversionsAreDisabledByDefault.kt");
@@ -25784,6 +25784,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("castFromAny.kt")
public void testCastFromAny() throws Exception {
runTest("compiler/testData/codegen/box/sam/castFromAny.kt");
@@ -24601,6 +24601,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("castFromAny.kt")
public void testCastFromAny() throws Exception {
runTest("compiler/testData/codegen/box/sam/castFromAny.kt");
@@ -24283,6 +24283,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("castFromAny.kt")
public void testCastFromAny() throws Exception {
runTest("compiler/testData/codegen/box/sam/castFromAny.kt");
@@ -24283,6 +24283,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("arrayAsVarargAfterSamArgument.kt")
public void testArrayAsVarargAfterSamArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgument.kt");
}
@TestMetadata("arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt")
public void testArrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument() throws Exception {
runTest("compiler/testData/codegen/box/sam/arrayAsVarargAfterSamArgumentWithoutSamConversionsPerArgument.kt");
}
@TestMetadata("castFromAny.kt")
public void testCastFromAny() throws Exception {
runTest("compiler/testData/codegen/box/sam/castFromAny.kt");
@@ -116,6 +116,7 @@ enum class LanguageFeature(
ProhibitNonReifiedArraysAsReifiedTypeArguments(KOTLIN_1_4, kind = BUG_FIX),
ProhibitProtectedCallFromInline(KOTLIN_1_4, kind = BUG_FIX),
ProperFinally(KOTLIN_1_4, kind = BUG_FIX),
ProhibitVarargAsArrayAfterSamArgument(KOTLIN_1_4, kind = BUG_FIX),
ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX),
// Temporarily disabled, see KT-27084/KT-22379
@@ -132,10 +132,12 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
if (!resolutionResults.isSuccess) return false
val generatingAdditionalSamCandidateIsDisabled =
parentCall.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) ||
parentCall.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVarargAsArrayAfterSamArgument)
val samAdapterOriginalDescriptor =
if (parentCall.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) &&
resolutionResults.resultingCall is NewResolvedCallImpl<*>
) {
if (generatingAdditionalSamCandidateIsDisabled && resolutionResults.resultingCall is NewResolvedCallImpl<*>) {
resolutionResults.resultingDescriptor
} else {
SamCodegenUtil.getOriginalIfSamAdapter(resolutionResults.resultingDescriptor) ?: return false
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2019 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.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
object AddSpreadOperatorForArrayAsVarargAfterSamFixFactory : KotlinSingleIntentionActionFactory() {
public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val diagnosticWithParameters = Errors.TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG.cast(diagnostic)
val argument = diagnosticWithParameters.psiElement
return AddSpreadOperatorForArrayAsVarargAfterSamFix(argument)
}
}
class AddSpreadOperatorForArrayAsVarargAfterSamFix(element: PsiElement) : KotlinQuickFixAction<PsiElement>(element) {
override fun getFamilyName() = "Add a spread operator before an array passing as vararg"
override fun getText() = familyName
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val element = element ?: return
element.addBefore(KtPsiFactory(file).createStar(), element.firstChild)
}
}
@@ -160,6 +160,7 @@ class QuickFixRegistrar : QuickFixContributor {
UNRESOLVED_REFERENCE.registerFactory(ImportFix)
UNRESOLVED_REFERENCE.registerFactory(ImportConstructorReferenceFix)
DEPRECATED_ACCESS_BY_SHORT_NAME.registerFactory(AddExplicitImportForDeprecatedVisibilityFix.Factory)
TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG.registerFactory(AddSpreadOperatorForArrayAsVarargAfterSamFixFactory)
TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix)
NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix)
@@ -0,0 +1,21 @@
// FILE: test.before.kt
// "Add a spread operator before an array passing as vararg" "false"
// ACTION: Add 'toString()' call
// ACTION: Create member function 'Test.foo'
// ACTION: Introduce import alias
// ACTION: Introduce local variable
// ACTION: Put arguments on separate lines
// ERROR: Type mismatch: inferred type is Array<???> but String! was expected
// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+SamConversionPerArgument -XXLanguage:+ProhibitVarargAsArrayAfterSamArgument
// WITH_RUNTIME
fun main() {
Test.foo({}, <caret>arrayOf())
}
// FILE: Test.java
public class Test {
public static String foo(Runnable r, String... strs) {
return null;
}
}
@@ -0,0 +1,27 @@
// FILE: test.before.kt
// "Add a spread operator before an array passing as vararg" "true"
// WARNING: Please use spread operator to pass an array as vararg. It will be an error in 1.5.
// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+SamConversionPerArgument
// WITH_RUNTIME
fun main() {
Test.foo({}, <caret>arrayOf())
}
// FILE: Test.java
public class Test {
public static String foo(Runnable r, String... strs) {
return null;
}
}
// FILE: test.after.kt
// "Add a spread operator before an array passing as vararg" "true"
// WARNING: Please use spread operator to pass an array as vararg. It will be an error in 1.5.
// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+SamConversionPerArgument
// WITH_RUNTIME
fun main() {
Test.foo({}, <caret>*arrayOf())
}
@@ -0,0 +1,18 @@
// FILE: test.before.kt
// "Add a spread operator before an array passing as vararg" "false"
// ACTION: Add explicit type arguments
// ACTION: Introduce import alias
// ACTION: Introduce local variable
// ACTION: Put arguments on separate lines
// WITH_RUNTIME
fun main() {
Test.foo({}, <caret>arrayOf())
}
// FILE: Test.java
public class Test {
public static String foo(Runnable r, String... strs) {
return null;
}
}
@@ -324,6 +324,34 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
}
@TestMetadata("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AddSpreadOperatorForArrayAsVarargAfterSam extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
public void testAllFilesPresentInAddSpreadOperatorForArrayAsVarargAfterSam() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true);
}
@TestMetadata("withError.test")
public void testWithError() throws Exception {
runTest("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam/withError.test");
}
@TestMetadata("withWarning.test")
public void testWithWarning() throws Exception {
runTest("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam/withWarning.test");
}
@TestMetadata("withoutWarning.test")
public void testWithoutWarning() throws Exception {
runTest("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam/withoutWarning.test");
}
}
@TestMetadata("idea/testData/quickfix/addStarProjections")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1174,6 +1174,19 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AddSpreadOperatorForArrayAsVarargAfterSam extends AbstractQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInAddSpreadOperatorForArrayAsVarargAfterSam() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addSpreadOperatorForArrayAsVarargAfterSam"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
}
@TestMetadata("idea/testData/quickfix/addStarProjections")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)