Value classes: treat @JvmInline value classes as inline classes

Report error on value classes without @JvmInline annotation.
Do not check for @JvmInline annotation in value classes since
it breaks reflection.
This commit is contained in:
Ilmir Usmanov
2020-11-19 02:20:37 +01:00
parent 6c68660ffd
commit 92f1681de0
100 changed files with 2668 additions and 53 deletions
@@ -362,6 +362,7 @@ public interface Errors {
DiagnosticFactory0<KtTypeReference> INLINE_CLASS_CANNOT_BE_RECURSIVE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_INLINE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION = DiagnosticFactory0.create(ERROR);
// Result class
@@ -717,6 +717,7 @@ public class DefaultErrorMessages {
MAP.put(INLINE_CLASS_CANNOT_BE_RECURSIVE, "Inline class cannot be recursive");
MAP.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, "Secondary constructors with bodies are reserved for for future releases");
MAP.put(VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION, "Value classes without @JvmInline annotation are not supported yet");
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);
@@ -270,7 +270,7 @@ class DeclarationsChecker(
if (declaration is KtPrimaryConstructor &&
!DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass) &&
!constructorDescriptor.constructedClass.isInline
!constructorDescriptor.constructedClass.isInlineClass()
) {
for (parameter in declaration.valueParameters) {
if (parameter.hasValOrVar()) {
@@ -107,7 +107,8 @@ object ModifierCheckerCore {
ANNOTATION_CLASS,
TYPEALIAS
),
FUN_KEYWORD to EnumSet.of(INTERFACE)
FUN_KEYWORD to EnumSet.of(INTERFACE),
VALUE_KEYWORD to EnumSet.of(CLASS_ONLY)
)
private val featureDependencies = mapOf(
@@ -118,7 +119,8 @@ object ModifierCheckerCore {
EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables),
FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion)
FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion),
VALUE_KEYWORD to listOf(LanguageFeature.InlineClasses)
)
private val featureDependenciesTargets = mapOf(
@@ -185,12 +187,13 @@ object ModifierCheckerCore {
result += incompatibilityRegister(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD)
// Abstract + open + final + sealed: incompatible
result += incompatibilityRegister(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, SEALED_KEYWORD)
// data + open, data + inner, data + abstract, data + sealed, data + inline
// data + open, data + inner, data + abstract, data + sealed, data + inline, data + value
result += incompatibilityRegister(DATA_KEYWORD, OPEN_KEYWORD)
result += incompatibilityRegister(DATA_KEYWORD, INNER_KEYWORD)
result += incompatibilityRegister(DATA_KEYWORD, ABSTRACT_KEYWORD)
result += incompatibilityRegister(DATA_KEYWORD, SEALED_KEYWORD)
result += incompatibilityRegister(DATA_KEYWORD, INLINE_KEYWORD)
result += incompatibilityRegister(DATA_KEYWORD, VALUE_KEYWORD)
// open is redundant to abstract & override
result += redundantRegister(ABSTRACT_KEYWORD, OPEN_KEYWORD)
// abstract is redundant to sealed
@@ -9,9 +9,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
@@ -22,15 +22,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object InlineClassDeclarationChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration !is KtClass) return
if (descriptor !is ClassDescriptor || !descriptor.isInline) return
if (descriptor !is ClassDescriptor || !descriptor.isInline && !descriptor.isValue) return
if (descriptor.kind != ClassKind.CLASS) return
val inlineKeyword = declaration.modifierList?.getModifier(KtTokens.INLINE_KEYWORD)
require(inlineKeyword != null) { "Declaration of inline class must have 'inline' keyword" }
val inlineOrValueKeyword = declaration.modifierList?.getModifier(KtTokens.INLINE_KEYWORD)
?: declaration.modifierList?.getModifier(KtTokens.VALUE_KEYWORD)
require(inlineOrValueKeyword != null) { "Declaration of inline class must have 'inline' keyword" }
val trace = context.trace
if (!DescriptorUtils.isTopLevelDeclaration(descriptor)) {
trace.report(Errors.INLINE_CLASS_NOT_TOP_LEVEL.on(inlineKeyword))
trace.report(Errors.INLINE_CLASS_NOT_TOP_LEVEL.on(inlineOrValueKeyword))
return
}
@@ -42,7 +43,7 @@ object InlineClassDeclarationChecker : DeclarationChecker {
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null) {
trace.report(Errors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS.on(inlineKeyword))
trace.report(Errors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS.on(inlineOrValueKeyword))
return
}
@@ -87,6 +88,10 @@ object InlineClassDeclarationChecker : DeclarationChecker {
}
}
}
if (descriptor.isValue && !descriptor.annotations.hasAnnotation(JVM_INLINE_ANNOTATION)) {
trace.report(Errors.VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION.on(inlineOrValueKeyword))
}
}
private fun KotlinType.isInapplicableParameterType() =
@@ -258,7 +258,7 @@ open class LazyClassMemberScope(
name: Name,
fromSupertypes: List<SimpleFunctionDescriptor>
) {
if (!thisDescriptor.isInline) return
if (!thisDescriptor.isInlineClass()) return
addFunctionFromAnyIfNeeded(result, name, fromSupertypes)
}