Synthesized 'copy' in data classes cannot override anything since 1.3

Synthesized 'copy' introduces default values for parameters, which is
prohibited for regular overrides.
Report warning in language version 1.2-, error in 1.3+.
This commit is contained in:
Dmitry Petrov
2017-09-12 15:10:03 +03:00
parent 9ae6feb2c5
commit 4c2cfd3ea9
17 changed files with 226 additions and 1 deletions
@@ -394,6 +394,10 @@ public interface Errors {
DiagnosticFactory2<PsiElement, CallableMemberDescriptor, DeclarationDescriptor> DATA_CLASS_OVERRIDE_CONFLICT =
DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, CallableMemberDescriptor, DeclarationDescriptor> DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING =
DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, CallableMemberDescriptor, DeclarationDescriptor> DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR =
DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<KtDeclaration, CallableMemberDescriptor> CANNOT_INFER_VISIBILITY =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
@@ -290,6 +290,8 @@ public class DefaultErrorMessages {
MAP.put(VIRTUAL_MEMBER_HIDDEN, "''{0}'' hides member of supertype ''{2}'' and needs ''override'' modifier", NAME, NAME, NAME);
MAP.put(DATA_CLASS_OVERRIDE_CONFLICT, "Function ''{0}'' generated for the data class conflicts with member of supertype ''{1}''", NAME, NAME);
MAP.put(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING, "Function ''{0}'' generated for the data class has default values for parameters, and conflicts with member of supertype ''{1}''", NAME, NAME);
MAP.put(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR, "Function ''{0}'' generated for the data class has default values for parameters, and conflicts with member of supertype ''{1}''", NAME, NAME);
MAP.put(CANNOT_OVERRIDE_INVISIBLE_MEMBER, "''{0}'' has no access to ''{1}'', so it cannot override it", FQ_NAMES_IN_TYPES,
FQ_NAMES_IN_TYPES);
@@ -22,6 +22,8 @@ import com.intellij.psi.PsiElement
import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.SmartHashSet
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE
@@ -41,7 +43,8 @@ import java.util.*
class OverrideResolver(
private val trace: BindingTrace,
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper,
private val languageVersionSettings: LanguageVersionSettings
) {
fun check(c: TopDownAnalysisContext) {
@@ -244,6 +247,9 @@ class OverrideResolver(
if (DataClassDescriptorResolver.isComponentLike(declared.name)) {
checkOverrideForComponentFunction(declared)
}
else if (declared.name == DataClassDescriptorResolver.COPY_METHOD_NAME) {
checkOverrideForCopyFunction(declared)
}
return
}
@@ -341,6 +347,20 @@ class OverrideResolver(
})
}
private fun checkOverrideForCopyFunction(copyFunction: CallableMemberDescriptor) {
val overridden = copyFunction.overriddenDescriptors.firstOrNull()
if (overridden != null) {
val baseClassifier = overridden.containingDeclaration
val dataModifier = findDataModifierForDataClass(copyFunction.containingDeclaration)
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDataClassesOverridingCopy)) {
trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR.on(dataModifier, copyFunction, baseClassifier))
}
else {
trace.report(DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING.on(dataModifier, copyFunction, baseClassifier))
}
}
}
private fun checkParameterOverridesForAllClasses(c: TopDownAnalysisContext) {
for (classDescriptor in c.declaredClasses.values) {
for (member in DescriptorUtils.getAllDescriptors(classDescriptor.defaultType.memberScope)) {
@@ -1,3 +1,5 @@
// LANGUAGE_VERSION: 1.1
fun box(): String {
val a: A = B(1)
a.copy(1)
@@ -0,0 +1,9 @@
// !LANGUAGE: +ProhibitDataClassesOverridingCopy
interface WithCopy<T> {
fun copy(str: T): WithCopy<T>
}
<!DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR!>data<!> class <!CONFLICTING_JVM_DECLARATIONS, CONFLICTING_JVM_DECLARATIONS!>Test(val str: String)<!> : WithCopy<String> {
<!CONFLICTING_OVERLOADS!>override fun copy(str: String)<!> = Test(str)
}
@@ -0,0 +1,19 @@
package
public final data class Test : WithCopy<kotlin.String> {
public constructor Test(/*0*/ str: kotlin.String)
public final val str: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public open override /*1*/ fun copy(/*0*/ str: kotlin.String): Test
public final override /*1*/ /*synthesized*/ fun copy(/*0*/ str: kotlin.String = ...): Test
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public interface WithCopy</*0*/ T> {
public abstract fun copy(/*0*/ str: T): WithCopy<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,9 @@
// !LANGUAGE: +ProhibitDataClassesOverridingCopy
interface WithCopy<T> {
fun copy(str: T): WithCopy<T>
}
<!DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR!>data<!> class <!CONFLICTING_JVM_DECLARATIONS, CONFLICTING_JVM_DECLARATIONS, CONFLICTING_JVM_DECLARATIONS!>Test(val str: String)<!> : WithCopy<String> {
<!CONFLICTING_OVERLOADS!>override fun copy(str: String = <!DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE!>this.str<!>)<!> = Test(str)
}
@@ -0,0 +1,19 @@
package
public final data class Test : WithCopy<kotlin.String> {
public constructor Test(/*0*/ str: kotlin.String)
public final val str: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public open override /*1*/ fun copy(/*0*/ str: kotlin.String = ...): Test
public final override /*1*/ /*synthesized*/ fun copy(/*0*/ str: kotlin.String = ...): Test
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public interface WithCopy</*0*/ T> {
public abstract fun copy(/*0*/ str: T): WithCopy<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,9 @@
// !LANGUAGE: +ProhibitDataClassesOverridingCopy
interface WithCopy<T> {
fun copy(str: T): WithCopy<T>
}
data class Test(val str: String, val int: Int) : WithCopy<String> {
override fun copy(str: String) = copy(str, int)
}
@@ -0,0 +1,21 @@
package
public final data class Test : WithCopy<kotlin.String> {
public constructor Test(/*0*/ str: kotlin.String, /*1*/ int: kotlin.Int)
public final val int: kotlin.Int
public final val str: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public final operator /*synthesized*/ fun component2(): kotlin.Int
public open override /*1*/ fun copy(/*0*/ str: kotlin.String): Test
public final /*synthesized*/ fun copy(/*0*/ str: kotlin.String = ..., /*1*/ int: kotlin.Int = ...): Test
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public interface WithCopy</*0*/ T> {
public abstract fun copy(/*0*/ str: T): WithCopy<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,7 @@
// !LANGUAGE: -ProhibitDataClassesOverridingCopy
interface WithCopy<T> {
fun copy(str: T): WithCopy<T>
}
<!DATA_CLASS_OVERRIDE_DEFAULT_VALUES_WARNING!>data<!> class Test(val str: String): WithCopy<String>
@@ -0,0 +1,18 @@
package
public final data class Test : WithCopy<kotlin.String> {
public constructor Test(/*0*/ str: kotlin.String)
public final val str: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public final override /*1*/ /*synthesized*/ fun copy(/*0*/ str: kotlin.String = ...): Test
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public interface WithCopy</*0*/ T> {
public abstract fun copy(/*0*/ str: T): WithCopy<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,7 @@
// !LANGUAGE: +ProhibitDataClassesOverridingCopy
interface WithCopy<T> {
fun copy(str: T): WithCopy<T>
}
<!DATA_CLASS_OVERRIDE_DEFAULT_VALUES_ERROR!>data<!> class Test(val str: String): WithCopy<String>
@@ -0,0 +1,18 @@
package
public final data class Test : WithCopy<kotlin.String> {
public constructor Test(/*0*/ str: kotlin.String)
public final val str: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public final override /*1*/ /*synthesized*/ fun copy(/*0*/ str: kotlin.String = ...): Test
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public interface WithCopy</*0*/ T> {
public abstract fun copy(/*0*/ str: T): WithCopy<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -4828,12 +4828,42 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("dataClassExplicitlyOverridingCopyNoDefaults.kt")
public void testDataClassExplicitlyOverridingCopyNoDefaults() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassExplicitlyOverridingCopyNoDefaults.kt");
doTest(fileName);
}
@TestMetadata("dataClassExplicitlyOverridingCopyWithDefaults.kt")
public void testDataClassExplicitlyOverridingCopyWithDefaults() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassExplicitlyOverridingCopyWithDefaults.kt");
doTest(fileName);
}
@TestMetadata("dataClassNoName.kt")
public void testDataClassNoName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassNoName.kt");
doTest(fileName);
}
@TestMetadata("dataClassNotOverridingCopy.kt")
public void testDataClassNotOverridingCopy() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassNotOverridingCopy.kt");
doTest(fileName);
}
@TestMetadata("dataClassOverridingCopy_lv12.kt")
public void testDataClassOverridingCopy_lv12() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassOverridingCopy_lv12.kt");
doTest(fileName);
}
@TestMetadata("dataClassOverridingCopy_lv13.kt")
public void testDataClassOverridingCopy_lv13() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassOverridingCopy_lv13.kt");
doTest(fileName);
}
@TestMetadata("dataClassVarargParam.kt")
public void testDataClassVarargParam() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassVarargParam.kt");
@@ -4828,12 +4828,42 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
doTest(fileName);
}
@TestMetadata("dataClassExplicitlyOverridingCopyNoDefaults.kt")
public void testDataClassExplicitlyOverridingCopyNoDefaults() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassExplicitlyOverridingCopyNoDefaults.kt");
doTest(fileName);
}
@TestMetadata("dataClassExplicitlyOverridingCopyWithDefaults.kt")
public void testDataClassExplicitlyOverridingCopyWithDefaults() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassExplicitlyOverridingCopyWithDefaults.kt");
doTest(fileName);
}
@TestMetadata("dataClassNoName.kt")
public void testDataClassNoName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassNoName.kt");
doTest(fileName);
}
@TestMetadata("dataClassNotOverridingCopy.kt")
public void testDataClassNotOverridingCopy() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassNotOverridingCopy.kt");
doTest(fileName);
}
@TestMetadata("dataClassOverridingCopy_lv12.kt")
public void testDataClassOverridingCopy_lv12() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassOverridingCopy_lv12.kt");
doTest(fileName);
}
@TestMetadata("dataClassOverridingCopy_lv13.kt")
public void testDataClassOverridingCopy_lv13() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassOverridingCopy_lv13.kt");
doTest(fileName);
}
@TestMetadata("dataClassVarargParam.kt")
public void testDataClassVarargParam() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/dataClassVarargParam.kt");
@@ -65,6 +65,7 @@ enum class LanguageFeature(
RestrictionOfValReassignmentViaBackingField(KOTLIN_1_3),
NestedClassesInEnumEntryShouldBeInner(KOTLIN_1_3),
ProhibitDataClassesOverridingCopy(KOTLIN_1_3),
RestrictionOfWrongAnnotationsWithUseSiteTargetsOnTypes(KOTLIN_1_3),
// Experimental features