Support of custom 'equals' and 'hashCode' in inline classes
'equals' from any made available for overriding in inline classes 'typed' equals made available for definition in inline classes 'typed' equals definition made compulsory if 'untyped' is overridden 'operator' keyword is allowed in 'typed' equals definition ^KT-24874: Fixed
This commit is contained in:
committed by
teamcity
parent
527e8dde27
commit
e0c8142106
@@ -428,6 +428,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INLINE_CLASS_DEPRECATED = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<KtContextReceiverList> INLINE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtDeclaration, String> INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE);
|
||||
|
||||
// Result class
|
||||
|
||||
|
||||
+3
@@ -787,6 +787,9 @@ public class DefaultErrorMessages {
|
||||
MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable");
|
||||
MAP.put(INLINE_CLASS_DEPRECATED, "'inline' modifier is deprecated. Use 'value' instead");
|
||||
MAP.put(INLINE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS, "Inline classes cannot have context receivers");
|
||||
MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
|
||||
"Overriding ''equals'' from ''Any'' in inline class alongside with lack of ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
STRING);
|
||||
|
||||
MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type");
|
||||
MAP.put(RESULT_CLASS_WITH_NULLABLE_OPERATOR, "Expression of type ''kotlin.Result'' cannot be used as a left operand of ''{0}''", STRING);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTypedEqualsInInlineClass
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -44,11 +45,15 @@ object OperatorModifierChecker {
|
||||
|
||||
val checkResult = OperatorChecks.check(functionDescriptor)
|
||||
if (checkResult.isSuccess) {
|
||||
when (functionDescriptor.name) {
|
||||
in REM_TO_MOD_OPERATION_NAMES.keys ->
|
||||
when {
|
||||
functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.keys ->
|
||||
checkSupportsFeature(LanguageFeature.OperatorRem, languageVersionSettings, diagnosticHolder, modifier)
|
||||
OperatorNameConventions.PROVIDE_DELEGATE ->
|
||||
|
||||
functionDescriptor.name == OperatorNameConventions.PROVIDE_DELEGATE ->
|
||||
checkSupportsFeature(LanguageFeature.OperatorProvideDelegate, languageVersionSettings, diagnosticHolder, modifier)
|
||||
|
||||
functionDescriptor.isTypedEqualsInInlineClass() ->
|
||||
checkSupportsFeature(LanguageFeature.CustomEqualsInInlineClasses, languageVersionSettings, diagnosticHolder, modifier)
|
||||
}
|
||||
|
||||
if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values &&
|
||||
|
||||
+21
-2
@@ -161,6 +161,21 @@ object InlineClassDeclarationChecker : DeclarationChecker {
|
||||
trace.report(Errors.VALUE_CLASS_CANNOT_BE_CLONEABLE.on(inlineOrValueKeyword))
|
||||
return
|
||||
}
|
||||
|
||||
fun getFunctionDescriptor(declaration: KtNamedFunction): SimpleFunctionDescriptor? =
|
||||
context.trace.bindingContext.get(BindingContext.FUNCTION, declaration)
|
||||
|
||||
fun isUntypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.overridesEqualsFromAny() ?: false
|
||||
fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInInlineClass() ?: false
|
||||
fun KtClass.namedFunctions() = declarations.filterIsInstance<KtNamedFunction>()
|
||||
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
|
||||
declaration.namedFunctions().singleOrNull { isUntypedEquals(it) }?.apply {
|
||||
if (declaration.namedFunctions().none { isTypedEquals(it) }) {
|
||||
trace.report(Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS.on(this@apply, descriptor.name.asString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.isInapplicableParameterType() =
|
||||
@@ -213,7 +228,8 @@ class InnerClassInsideInlineClass : DeclarationChecker {
|
||||
class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
|
||||
|
||||
companion object {
|
||||
private val reservedFunctions = setOf("box", "unbox", "equals", "hashCode")
|
||||
private val boxAndUnboxNames = setOf("box", "unbox")
|
||||
private val equalsAndHashCodeNames = setOf("equals", "hashCode")
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
@@ -226,7 +242,10 @@ class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
|
||||
is SimpleFunctionDescriptor -> {
|
||||
val ktFunction = declaration as? KtFunction ?: return
|
||||
val functionName = descriptor.name.asString()
|
||||
if (functionName in reservedFunctions) {
|
||||
if (functionName in boxAndUnboxNames
|
||||
|| (functionName in equalsAndHashCodeNames
|
||||
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses))
|
||||
) {
|
||||
val nameIdentifier = ktFunction.nameIdentifier ?: return
|
||||
context.trace.report(Errors.RESERVED_MEMBER_INSIDE_VALUE_CLASS.on(nameIdentifier, functionName))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user