K1: introduce synthetic assignment checker with deprecation for KT-54305
#KT-54305 Fixed Related to KT-54309
This commit is contained in:
committed by
Space Team
parent
7bd512fbb3
commit
80fa765333
+6
@@ -807,6 +807,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("syntheticSetFalsePositive.kt")
|
||||||
|
public void testSyntheticSetFalsePositive() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticSetFalsePositive.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("tailRecBasic.kt")
|
@TestMetadata("tailRecBasic.kt")
|
||||||
public void testTailRecBasic() throws Exception {
|
public void testTailRecBasic() throws Exception {
|
||||||
|
|||||||
+6
@@ -807,6 +807,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("syntheticSetFalsePositive.kt")
|
||||||
|
public void testSyntheticSetFalsePositive() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticSetFalsePositive.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("tailRecBasic.kt")
|
@TestMetadata("tailRecBasic.kt")
|
||||||
public void testTailRecBasic() throws Exception {
|
public void testTailRecBasic() throws Exception {
|
||||||
|
|||||||
+6
@@ -807,6 +807,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("syntheticSetFalsePositive.kt")
|
||||||
|
public void testSyntheticSetFalsePositive() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticSetFalsePositive.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("tailRecBasic.kt")
|
@TestMetadata("tailRecBasic.kt")
|
||||||
public void testTailRecBasic() throws Exception {
|
public void testTailRecBasic() throws Exception {
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.resolve.jvm.checkers
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.tower.isSynthesized
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||||
|
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.types.IndexedParametersSubstitution
|
||||||
|
import org.jetbrains.kotlin.types.TypeProjection
|
||||||
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
import org.jetbrains.kotlin.types.expressions.BasicExpressionTypingVisitor
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
|
|
||||||
|
object JvmSyntheticAssignmentChecker : CallChecker {
|
||||||
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
|
if (!resultingDescriptor.isSynthesized) return
|
||||||
|
if (resultingDescriptor !is SyntheticJavaPropertyDescriptor) return
|
||||||
|
if (reportOn !is KtNameReferenceExpression) return
|
||||||
|
val binaryExpression = reportOn.getParentOfType<KtBinaryExpression>(strict = true) ?: return
|
||||||
|
if (!BasicExpressionTypingVisitor.isLValue(reportOn, binaryExpression)) return
|
||||||
|
val receiverType = resolvedCall.extensionReceiver?.type ?: return
|
||||||
|
val unsubstitutedReceiverType = resolvedCall.candidateDescriptor.extensionReceiverParameter?.type ?: return
|
||||||
|
if (receiverType.constructor !== unsubstitutedReceiverType.constructor) return
|
||||||
|
val propertyType = resolvedCall.candidateDescriptor.returnType ?: return
|
||||||
|
|
||||||
|
val substitutionParameters = mutableListOf<TypeParameterDescriptor>()
|
||||||
|
val substitutionArguments = mutableListOf<TypeProjection>()
|
||||||
|
for ((unsubstitutedArgument, substitutedArgument) in unsubstitutedReceiverType.arguments.zip(receiverType.arguments)) {
|
||||||
|
val typeParameter = unsubstitutedArgument.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: continue
|
||||||
|
substitutionParameters += typeParameter
|
||||||
|
substitutionArguments += substitutedArgument
|
||||||
|
}
|
||||||
|
val substitutor = TypeSubstitutor.create(
|
||||||
|
IndexedParametersSubstitution(
|
||||||
|
substitutionParameters.toTypedArray(), substitutionArguments.toTypedArray(), approximateContravariantCapturedTypes = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val substitutedPropertyType = substitutor.substitute(propertyType.unwrap(), Variance.IN_VARIANCE)
|
||||||
|
if (substitutedPropertyType == null || !substitutedPropertyType.isNothing()) return
|
||||||
|
context.trace.report(ErrorsJvm.SYNTHETIC_SETTER_PROJECTED_OUT.on(binaryExpression.left ?: reportOn, resultingDescriptor))
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -143,6 +143,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
|||||||
MAP.put(INAPPLICABLE_JVM_FIELD_WARNING, "{0}. This warning will become an error in further releases", STRING);
|
MAP.put(INAPPLICABLE_JVM_FIELD_WARNING, "{0}. This warning will become an error in further releases", STRING);
|
||||||
|
|
||||||
MAP.put(JVM_SYNTHETIC_ON_DELEGATE, "'@JvmSynthetic' annotation cannot be used on delegated properties");
|
MAP.put(JVM_SYNTHETIC_ON_DELEGATE, "'@JvmSynthetic' annotation cannot be used on delegated properties");
|
||||||
|
MAP.put(SYNTHETIC_SETTER_PROJECTED_OUT, "Use of setter for ''{0}'' is unsafe due to the type projection in receiver." +
|
||||||
|
" Workaround: use explicit cast on receiver. See https://youtrack.jetbrains.com/issue/KT-54309", NAME);
|
||||||
|
|
||||||
MAP.put(STRICTFP_ON_CLASS, "'@Strictfp' annotation on classes is unsupported yet");
|
MAP.put(STRICTFP_ON_CLASS, "'@Strictfp' annotation on classes is unsupported yet");
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public interface ErrorsJvm {
|
|||||||
|
|
||||||
DiagnosticFactory0<KtAnnotationEntry> JVM_SYNTHETIC_ON_DELEGATE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> JVM_SYNTHETIC_ON_DELEGATE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
|
DiagnosticFactory1<KtExpression, DeclarationDescriptor> SYNTHETIC_SETTER_PROJECTED_OUT = DiagnosticFactory1.create(WARNING);
|
||||||
|
|
||||||
DiagnosticFactory0<KtAnnotationEntry> STRICTFP_ON_CLASS = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<KtAnnotationEntry> STRICTFP_ON_CLASS = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
DiagnosticFactory0<KtAnnotationEntry> VOLATILE_ON_VALUE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> VOLATILE_ON_VALUE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+1
@@ -62,6 +62,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase(
|
|||||||
SamInterfaceConstructorReferenceCallChecker,
|
SamInterfaceConstructorReferenceCallChecker,
|
||||||
EnumDeclaringClassDeprecationChecker,
|
EnumDeclaringClassDeprecationChecker,
|
||||||
UpperBoundViolatedInTypealiasConstructorChecker,
|
UpperBoundViolatedInTypealiasConstructorChecker,
|
||||||
|
JvmSyntheticAssignmentChecker,
|
||||||
),
|
),
|
||||||
|
|
||||||
additionalTypeCheckers = listOf(
|
additionalTypeCheckers = listOf(
|
||||||
|
|||||||
+4
-4
@@ -20,7 +20,7 @@ public class Wrapper<W> {
|
|||||||
|
|
||||||
fun foo(container: Container<*>, wrapper: Wrapper<String>) {
|
fun foo(container: Container<*>, wrapper: Wrapper<String>) {
|
||||||
container.w = <!TYPE_MISMATCH!>wrapper<!>
|
container.w = <!TYPE_MISMATCH!>wrapper<!>
|
||||||
container.wrapper = wrapper
|
<!SYNTHETIC_SETTER_PROJECTED_OUT!>container.wrapper<!> = wrapper
|
||||||
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,16 +42,16 @@ fun dif(container: Container<String>, wrapper: Wrapper<Int>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun out(container: Container<out Any>, wrapper: Wrapper<String>) {
|
fun out(container: Container<out Any>, wrapper: Wrapper<String>) {
|
||||||
container.wrapper = wrapper
|
<!SYNTHETIC_SETTER_PROJECTED_OUT!>container.wrapper<!> = wrapper
|
||||||
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inn(container: Container<in String>, wrapper: Wrapper<Any>) {
|
fun inn(container: Container<in String>, wrapper: Wrapper<Any>) {
|
||||||
container.wrapper = wrapper
|
<!SYNTHETIC_SETTER_PROJECTED_OUT!>container.wrapper<!> = wrapper
|
||||||
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> generic(container: Container<out T>, wrapper: Wrapper<out T>) {
|
fun <T> generic(container: Container<out T>, wrapper: Wrapper<out T>) {
|
||||||
container.wrapper = wrapper
|
<!SYNTHETIC_SETTER_PROJECTED_OUT!>container.wrapper<!> = wrapper
|
||||||
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
container.setWrapper(<!TYPE_MISMATCH!>wrapper<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// FILE: JavaClass.java
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface JavaClass<E> {
|
||||||
|
List<? extends E> getFoo();
|
||||||
|
void setFoo(List<? extends E> l);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
fun foo(x: JavaClass<in CharSequence>, l: MutableList<out CharSequence>) {
|
||||||
|
x.setFoo(l) // OK
|
||||||
|
x.foo = l // Should be OK, too
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun foo(/*0*/ x: JavaClass<in kotlin.CharSequence>, /*1*/ l: kotlin.collections.MutableList<out kotlin.CharSequence>): kotlin.Unit
|
||||||
|
|
||||||
|
public interface JavaClass</*0*/ E : kotlin.Any!> {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract fun getFoo(): (kotlin.collections.MutableList<out E!>..kotlin.collections.List<E!>?)
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public abstract fun setFoo(/*0*/ l: (kotlin.collections.MutableList<out E!>..kotlin.collections.List<E!>?)): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
Generated
+6
@@ -807,6 +807,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticSet.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("syntheticSetFalsePositive.kt")
|
||||||
|
public void testSyntheticSetFalsePositive() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticSetFalsePositive.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("tailRecBasic.kt")
|
@TestMetadata("tailRecBasic.kt")
|
||||||
public void testTailRecBasic() throws Exception {
|
public void testTailRecBasic() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user