[IR] Add diagnostics to forbid annotations for MFVC-typed elements
Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com> #KT-1179
This commit is contained in:
committed by
Space Team
parent
adee33d3e5
commit
fa4ceb4ef4
+7
@@ -1406,6 +1406,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET) { firDiagnostic ->
|
||||
AnnotationOnIllegalMultiFieldValueClassTypedTargetImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.NONE_APPLICABLE) { firDiagnostic ->
|
||||
NoneApplicableImpl(
|
||||
firDiagnostic.a.map { firBasedSymbol ->
|
||||
|
||||
+5
@@ -1012,6 +1012,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = ValueClassCannotBeCloneable::class
|
||||
}
|
||||
|
||||
abstract class AnnotationOnIllegalMultiFieldValueClassTypedTarget : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = AnnotationOnIllegalMultiFieldValueClassTypedTarget::class
|
||||
abstract val name: String
|
||||
}
|
||||
|
||||
abstract class NoneApplicable : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = NoneApplicable::class
|
||||
abstract val candidates: List<KtSymbol>
|
||||
|
||||
+6
@@ -1214,6 +1214,12 @@ internal class ValueClassCannotBeCloneableImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.ValueClassCannotBeCloneable(), KtAbstractFirDiagnostic<KtDeclaration>
|
||||
|
||||
internal class AnnotationOnIllegalMultiFieldValueClassTypedTargetImpl(
|
||||
override val name: String,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.AnnotationOnIllegalMultiFieldValueClassTypedTarget(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||
|
||||
internal class NoneApplicableImpl(
|
||||
override val candidates: List<KtSymbol>,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
|
||||
+6
@@ -33612,6 +33612,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/valueClasses"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/valueClasses/annotations.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithUniversal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("basicValueClassDeclaration.kt")
|
||||
public void testBasicValueClassDeclaration() throws Exception {
|
||||
|
||||
+6
@@ -33708,6 +33708,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/valueClasses"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/valueClasses/annotations.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithUniversal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("basicValueClassDeclaration.kt")
|
||||
public void testBasicValueClassDeclaration() throws Exception {
|
||||
|
||||
+6
@@ -33612,6 +33612,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/valueClasses"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/valueClasses/annotations.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithUniversal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("basicValueClassDeclaration.kt")
|
||||
public void testBasicValueClassDeclaration() throws Exception {
|
||||
|
||||
+3
@@ -434,6 +434,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS by error<KtTypeReference>()
|
||||
val INNER_CLASS_INSIDE_VALUE_CLASS by error<KtDeclaration>(PositioningStrategy.INNER_MODIFIER)
|
||||
val VALUE_CLASS_CANNOT_BE_CLONEABLE by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER)
|
||||
val ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET by error<KtAnnotationEntry> {
|
||||
parameter<String>("name")
|
||||
}
|
||||
}
|
||||
|
||||
val APPLICABILITY by object : DiagnosticGroup("Applicability") {
|
||||
|
||||
+6
-4
@@ -5,21 +5,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.jvm.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.JvmFieldApplicabilityProblem.*
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.needsMultiFieldValueClassFlattening
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
@@ -65,7 +66,8 @@ object FirJvmFieldApplicabilityChecker : FirPropertyChecker() {
|
||||
}
|
||||
containingClassSymbol == null && isInsideJvmMultifileClassFile(context) ->
|
||||
TOP_LEVEL_PROPERTY_OF_MULTIFILE_FACADE
|
||||
declaration.returnTypeRef.isInlineClassThatRequiresMangling(session) -> RETURN_TYPE_IS_INLINE_CLASS
|
||||
declaration.returnTypeRef.isInlineClassThatRequiresMangling(session) -> RETURN_TYPE_IS_VALUE_CLASS
|
||||
declaration.returnTypeRef.needsMultiFieldValueClassFlattening(session) -> RETURN_TYPE_IS_VALUE_CLASS
|
||||
else -> return
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -48,7 +48,7 @@ object FirJvmNameChecker : FirBasicDeclarationChecker() {
|
||||
if (
|
||||
declaration.isOverride ||
|
||||
declaration.isOpen ||
|
||||
containingClass?.isInlineThatRequiresMangling() == true
|
||||
containingClass?.isValueClassThatRequiresMangling() == true
|
||||
) {
|
||||
reporter.reportOn(jvmName.source, FirJvmErrors.INAPPLICABLE_JVM_NAME, context)
|
||||
}
|
||||
@@ -66,7 +66,8 @@ object FirJvmNameChecker : FirBasicDeclarationChecker() {
|
||||
return containingClass != null || !function.symbol.callableId.isLocal
|
||||
}
|
||||
|
||||
private fun FirRegularClass.isInlineThatRequiresMangling(): Boolean {
|
||||
private fun FirRegularClass.isValueClassThatRequiresMangling(): Boolean {
|
||||
// value classes have inline modifiers in FIR
|
||||
return isInline && name != StandardClassIds.Result.shortClassName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ object FirErrors {
|
||||
val TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS by error0<KtTypeReference>()
|
||||
val INNER_CLASS_INSIDE_VALUE_CLASS by error0<KtDeclaration>(SourceElementPositioningStrategies.INNER_MODIFIER)
|
||||
val VALUE_CLASS_CANNOT_BE_CLONEABLE by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER)
|
||||
val ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET by error1<KtAnnotationEntry, String>()
|
||||
|
||||
// Applicability
|
||||
val NONE_APPLICABLE by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.containingClassForLocalAttr
|
||||
@@ -98,7 +97,33 @@ fun FirClassSymbol<*>.isSupertypeOf(other: FirClassSymbol<*>, session: FirSessio
|
||||
return isSupertypeOf(other, mutableSetOf())
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isInlineClass(session: FirSession): Boolean = toRegularClassSymbol(session)?.isInline == true
|
||||
fun ConeKotlinType.isValueClass(session: FirSession): Boolean {
|
||||
// Value classes have inline modifier in FIR
|
||||
return toRegularClassSymbol(session)?.isInline == true
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isSingleFieldValueClass(session: FirSession): Boolean = with(session.typeContext) {
|
||||
isRecursiveSingleFieldValueClassType(session) || typeConstructor().isInlineClass()
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isRecursiveSingleFieldValueClassType(session: FirSession) =
|
||||
isRecursiveValueClassType(hashSetOf(), session, onlyInline = true)
|
||||
|
||||
fun ConeKotlinType.isRecursiveValueClassType(session: FirSession) =
|
||||
isRecursiveValueClassType(hashSetOf(), session, onlyInline = false)
|
||||
|
||||
private fun ConeKotlinType.isRecursiveValueClassType(visited: HashSet<ConeKotlinType>, session: FirSession, onlyInline: Boolean): Boolean {
|
||||
|
||||
val asRegularClass = this.toRegularClassSymbol(session)?.takeIf { it.isInlineOrValueClass() } ?: return false
|
||||
val primaryConstructor = asRegularClass.declarationSymbols
|
||||
.firstOrNull { it is FirConstructorSymbol && it.isPrimary } as FirConstructorSymbol?
|
||||
?: return false
|
||||
|
||||
if (primaryConstructor.valueParameterSymbols.size > 1 && onlyInline) return false
|
||||
return !visited.add(this) || primaryConstructor.valueParameterSymbols.any {
|
||||
it.resolvedReturnTypeRef.coneType.isRecursiveValueClassType(visited, session, onlyInline)
|
||||
}.also { visited.remove(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirRegularClass associated with this
|
||||
@@ -678,6 +703,6 @@ private fun findDefaultValue(source: KtLightSourceElement): KtLightSourceElement
|
||||
}
|
||||
|
||||
fun ConeKotlinType.getInlineClassUnderlyingType(session: FirSession): ConeKotlinType {
|
||||
require(this.isInlineClass(session))
|
||||
require(this.isSingleFieldValueClass(session))
|
||||
return toRegularClassSymbol(session)!!.primaryConstructorSymbol()!!.valueParameterSymbols[0].resolvedReturnTypeRef.coneType
|
||||
}
|
||||
+53
-12
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.hasValOrVar
|
||||
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
@@ -70,6 +72,44 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMultiFieldValueClassAnnotationRestrictions(
|
||||
declaration: FirDeclaration,
|
||||
annotation: FirAnnotation,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val (hint, type) = when (annotation.useSiteTarget) {
|
||||
FIELD, PROPERTY_DELEGATE_FIELD -> "fields" to ((declaration as? FirProperty)?.backingField?.returnTypeRef ?: return)
|
||||
FILE, PROPERTY, PROPERTY_SETTER -> return
|
||||
PROPERTY_GETTER -> "getters" to ((declaration as? FirPropertyAccessor)?.returnTypeRef ?: return)
|
||||
RECEIVER -> "receivers" to ((declaration as? FirCallableDeclaration)?.receiverTypeRef ?: return)
|
||||
CONSTRUCTOR_PARAMETER, SETTER_PARAMETER -> "parameters" to (declaration as? FirValueParameter ?: return).returnTypeRef
|
||||
null -> when {
|
||||
declaration is FirProperty && !declaration.isLocal -> {
|
||||
val allowedAnnotationTargets = annotation.getAllowedAnnotationTargets(context.session)
|
||||
when {
|
||||
declaration.fromPrimaryConstructor == true && allowedAnnotationTargets.contains(KotlinTarget.VALUE_PARAMETER) -> return // handled in FirValueParameter case
|
||||
allowedAnnotationTargets.contains(KotlinTarget.PROPERTY) -> return
|
||||
allowedAnnotationTargets.contains(KotlinTarget.FIELD) -> "fields" to declaration.returnTypeRef
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
declaration is FirField -> "fields" to declaration.returnTypeRef
|
||||
declaration is FirValueParameter -> "parameters" to declaration.returnTypeRef
|
||||
declaration is FirVariable -> "variables" to declaration.returnTypeRef
|
||||
declaration is FirPropertyAccessor && declaration.isGetter &&
|
||||
declaration.receiverTypeRef == null &&
|
||||
declaration.contextReceivers.isEmpty() ->
|
||||
"getters" to declaration.returnTypeRef
|
||||
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
if (type.needsMfvcFlattening(context.session)) {
|
||||
reporter.reportOn(annotation.source, FirErrors.ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET, hint, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAnnotationTarget(
|
||||
declaration: FirDeclaration,
|
||||
annotation: FirAnnotation,
|
||||
@@ -96,6 +136,7 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
|
||||
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || checkWithUseSiteTargets()) {
|
||||
checkMultiFieldValueClassAnnotationRestrictions(declaration, annotation, context, reporter)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,28 +169,28 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
) {
|
||||
if (annotation.source?.kind == KtFakeSourceElementKind.FromUseSiteTarget) return
|
||||
when (target) {
|
||||
AnnotationUseSiteTarget.PROPERTY,
|
||||
AnnotationUseSiteTarget.PROPERTY_GETTER -> {
|
||||
PROPERTY,
|
||||
PROPERTY_GETTER -> {
|
||||
}
|
||||
AnnotationUseSiteTarget.FIELD -> {
|
||||
FIELD -> {
|
||||
if (annotated is FirProperty && annotated.delegateFieldSymbol != null && !annotated.hasBackingField) {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD, context)
|
||||
}
|
||||
}
|
||||
AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD -> {
|
||||
PROPERTY_DELEGATE_FIELD -> {
|
||||
if (annotated is FirProperty && annotated.delegateFieldSymbol == null) {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE, context)
|
||||
}
|
||||
}
|
||||
AnnotationUseSiteTarget.PROPERTY_SETTER,
|
||||
AnnotationUseSiteTarget.SETTER_PARAMETER -> {
|
||||
PROPERTY_SETTER,
|
||||
SETTER_PARAMETER -> {
|
||||
if (annotated !is FirProperty || annotated.isLocal) {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_ON_PROPERTY, target.renderName, context)
|
||||
} else if (!annotated.isVar) {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, target.renderName, context)
|
||||
}
|
||||
}
|
||||
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> when {
|
||||
CONSTRUCTOR_PARAMETER -> when {
|
||||
annotated is FirValueParameter -> {
|
||||
val container = context.containingDeclarations.lastOrNull()
|
||||
if (container is FirConstructor && container.isPrimary) {
|
||||
@@ -164,13 +205,13 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
else -> reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_PARAM_TARGET, context)
|
||||
}
|
||||
AnnotationUseSiteTarget.FILE -> {
|
||||
FILE -> {
|
||||
// NB: report once?
|
||||
if (annotated !is FirFile) {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_FILE_TARGET, context)
|
||||
}
|
||||
}
|
||||
AnnotationUseSiteTarget.RECEIVER -> {
|
||||
RECEIVER -> {
|
||||
// NB: report once?
|
||||
// annotation with use-site target `receiver` can be only on type reference, but not on declaration
|
||||
reporter.reportOn(
|
||||
@@ -245,9 +286,9 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
|
||||
val propertyAnnotations = mapOf(
|
||||
AnnotationUseSiteTarget.PROPERTY_GETTER to property.getter?.getAnnotationTypes(),
|
||||
AnnotationUseSiteTarget.PROPERTY_SETTER to property.setter?.getAnnotationTypes(),
|
||||
AnnotationUseSiteTarget.SETTER_PARAMETER to property.setter?.valueParameters?.single().getAnnotationTypes()
|
||||
PROPERTY_GETTER to property.getter?.getAnnotationTypes(),
|
||||
PROPERTY_SETTER to property.setter?.getAnnotationTypes(),
|
||||
SETTER_PARAMETER to property.setter?.valueParameters?.single().getAnnotationTypes()
|
||||
)
|
||||
|
||||
val isError = context.session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitRepeatedUseSiteTargetAnnotations)
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isValueClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.valOrVarKeyword
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
@@ -66,7 +66,7 @@ object FirFunctionParameterChecker : FirFunctionChecker() {
|
||||
for (varargParameter in varargParameters) {
|
||||
val varargParameterType = varargParameter.returnTypeRef.coneType.arrayElementType() ?: continue
|
||||
if (AbstractTypeChecker.isSubtypeOf(context.session.typeContext, varargParameterType, nullableNothingType) ||
|
||||
(varargParameterType.isInlineClass(context.session) && !varargParameterType.isUnsignedTypeOrNullableUnsignedType)
|
||||
(varargParameterType.isValueClass(context.session) && !varargParameterType.isUnsignedTypeOrNullableUnsignedType)
|
||||
// Note: comparing with FE1.0, we skip checking if the type is not primitive because primitive types are not inline. That
|
||||
// is any primitive values are already allowed by the inline check.
|
||||
) {
|
||||
|
||||
+5
-6
@@ -11,15 +11,14 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineClassDeclarationChecker.isRecursiveInlineClassType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getInlineClassUnderlyingType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isRecursiveValueClassType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isSingleFieldValueClass
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInline
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
|
||||
@@ -65,7 +64,7 @@ object FirInapplicableLateinitChecker : FirPropertyChecker() {
|
||||
reporter.reportError(declaration.source, "is not allowed on properties with a custom getter or setter", context)
|
||||
}
|
||||
|
||||
if (declaration.returnTypeRef.coneType.isInlineClass(context.session)) {
|
||||
if (declaration.returnTypeRef.coneType.isSingleFieldValueClass(context.session)) {
|
||||
val declarationType = declaration.returnTypeRef.coneType
|
||||
val variables = if (declaration.isLocal) "local variables" else "properties"
|
||||
when {
|
||||
@@ -93,14 +92,14 @@ object FirInapplicableLateinitChecker : FirPropertyChecker() {
|
||||
fun isForbiddenTypeForLateinit(type: ConeKotlinType): Boolean {
|
||||
if (type.isPrimitiveOrNullablePrimitive) return true
|
||||
if (type.hasNullableUpperBound) return true
|
||||
if (type.isInlineClass(session)) {
|
||||
if (type.isSingleFieldValueClass(session)) {
|
||||
return isForbiddenTypeForLateinit(type.getInlineClassUnderlyingType(session))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// prevent infinite recursion
|
||||
if (type.isRecursiveInlineClassType(session)) return false
|
||||
if (type.isRecursiveValueClassType(session)) return false
|
||||
return isForbiddenTypeForLateinit(type.getInlineClassUnderlyingType(session))
|
||||
}
|
||||
|
||||
|
||||
+2
-17
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isRecursiveValueClassType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
@@ -23,7 +24,6 @@ import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.isEquals
|
||||
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -197,7 +197,7 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
|
||||
)
|
||||
}
|
||||
|
||||
primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) -> {
|
||||
primaryConstructorParameter.returnTypeRef.coneType.isRecursiveValueClassType(context.session) -> {
|
||||
reporter.reportOn(
|
||||
primaryConstructorParameter.returnTypeRef.source, FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE,
|
||||
context
|
||||
@@ -276,21 +276,6 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
|
||||
arrayElementType.isGenericArrayOfTypeParameter()
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isRecursiveInlineClassType(session: FirSession) =
|
||||
isRecursiveInlineClassType(hashSetOf(), session)
|
||||
|
||||
private fun ConeKotlinType.isRecursiveInlineClassType(visited: HashSet<ConeKotlinType>, session: FirSession): Boolean {
|
||||
|
||||
val asRegularClass = this.toRegularClassSymbol(session)?.takeIf { it.isInlineOrValueClass() } ?: return false
|
||||
val primaryConstructor = asRegularClass.declarationSymbols
|
||||
.firstOrNull { it is FirConstructorSymbol && it.isPrimary } as FirConstructorSymbol?
|
||||
?: return false
|
||||
|
||||
return !visited.add(this) || primaryConstructor.valueParameterSymbols.any {
|
||||
it.resolvedReturnTypeRef.coneType.isRecursiveInlineClassType(visited, session)
|
||||
}.also { visited.remove(this) }
|
||||
}
|
||||
|
||||
private fun FirRegularClass.isSubtypeOfCloneable(session: FirSession): Boolean {
|
||||
if (classId.isCloneableId()) return true
|
||||
|
||||
|
||||
+5
-4
@@ -18,11 +18,8 @@ import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.isBoolean
|
||||
import org.jetbrains.kotlin.fir.types.isNothing
|
||||
import org.jetbrains.kotlin.fir.types.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
internal fun isInsideExpectClass(containingClass: FirClass, context: CheckerContext): Boolean {
|
||||
@@ -131,6 +128,10 @@ fun FirSimpleFunction.isTypedEqualsInValueClass(session: FirSession): Boolean =
|
||||
}
|
||||
} ?: false
|
||||
|
||||
fun FirTypeRef.needsMultiFieldValueClassFlattening(session: FirSession) = with(session.typeContext) {
|
||||
coneType.typeConstructor().isMultiFieldValueClass() && !coneType.isNullable
|
||||
}
|
||||
|
||||
val FirCallableSymbol<*>.hasExplicitReturnType: Boolean
|
||||
get() {
|
||||
val returnTypeRef = resolvedReturnTypeRef
|
||||
|
||||
+7
@@ -68,6 +68,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_ARGUME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_CLASS_CONSTRUCTOR_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_CLASS_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_IN_WHERE_CLAUSE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_ON_SUPERCLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_USED_AS_ANNOTATION_ARGUMENT
|
||||
@@ -1290,6 +1291,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
map.put(TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS, "Type arguments for typed value class equals must be only star projections")
|
||||
map.put(INNER_CLASS_INSIDE_VALUE_CLASS, "Value class cannot have inner classes")
|
||||
map.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable")
|
||||
map.put(
|
||||
ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET,
|
||||
"Annotations on ''{0}'' of multi-field value class type are not supported",
|
||||
STRING
|
||||
)
|
||||
|
||||
|
||||
// Inline
|
||||
map.put(
|
||||
|
||||
+4
-2
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.checkers
|
||||
|
||||
import org.jetbrains.kotlin.JvmFieldApplicabilityProblem.*
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.fileClasses.isInsideJvmMultifileClassFile
|
||||
@@ -30,9 +31,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
|
||||
import org.jetbrains.kotlin.JvmFieldApplicabilityProblem.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.isInlineClassThatRequiresMangling
|
||||
import org.jetbrains.kotlin.resolve.needsMfvcFlattening
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
|
||||
@@ -63,7 +64,8 @@ class JvmFieldApplicabilityChecker : DeclarationChecker {
|
||||
}
|
||||
DescriptorUtils.isTopLevelDeclaration(descriptor) && declaration.isInsideJvmMultifileClassFile() ->
|
||||
TOP_LEVEL_PROPERTY_OF_MULTIFILE_FACADE
|
||||
descriptor.returnType?.isInlineClassThatRequiresMangling() == true -> RETURN_TYPE_IS_INLINE_CLASS
|
||||
descriptor.returnType?.isInlineClassThatRequiresMangling() == true -> RETURN_TYPE_IS_VALUE_CLASS
|
||||
descriptor.returnType?.needsMfvcFlattening() == true -> RETURN_TYPE_IS_VALUE_CLASS
|
||||
else -> return
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findSynchronizedAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
|
||||
@@ -157,6 +158,8 @@ class JvmNameAnnotationChecker : DeclarationChecker {
|
||||
diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry))
|
||||
} else if (descriptor.containingDeclaration.isInlineClassThatRequiresMangling()) {
|
||||
diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry))
|
||||
} else if (descriptor.containingDeclaration.isMultiFieldValueClass()) {
|
||||
diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +430,9 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtContextReceiverList> VALUE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtNamedFunction, KotlinType> INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
|
||||
DiagnosticFactory1<KtAnnotationEntry, String> ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET =
|
||||
DiagnosticFactory1.create(ERROR);
|
||||
|
||||
// Result class
|
||||
|
||||
|
||||
+1
@@ -806,6 +806,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS,
|
||||
"Overriding ''equals'' from ''Any'' in value class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
RENDER_TYPE);
|
||||
MAP.put(ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET, "Annotations on {0} of multi-field value class type are not supported", 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);
|
||||
|
||||
@@ -27,6 +27,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
KClassWithIncorrectTypeArgumentChecker,
|
||||
SuspendLimitationsChecker,
|
||||
ValueClassDeclarationChecker,
|
||||
MultiFieldValueClassAnnotationsChecker,
|
||||
PropertiesWithBackingFieldsInsideValueClass(),
|
||||
InnerClassInsideValueClass(),
|
||||
AnnotationClassTargetAndRetentionChecker(),
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||
import org.jetbrains.kotlin.resolve.needsMfvcFlattening
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
object MultiFieldValueClassAnnotationsChecker : DeclarationChecker {
|
||||
private fun report(context: DeclarationCheckerContext, name: String, type: KotlinType, annotationEntry: KtAnnotationEntry) {
|
||||
if (!type.needsMfvcFlattening()) return
|
||||
context.trace.report(Errors.ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET.on(annotationEntry, name))
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
fun report(name: String, type: KotlinType, annotations: Annotations) {
|
||||
for (annotationDescriptor in annotations) {
|
||||
report(context, name, type, annotationDescriptor.source.getPsi() as KtAnnotationEntry)
|
||||
}
|
||||
}
|
||||
|
||||
when (descriptor) {
|
||||
is PropertyDescriptor -> {
|
||||
descriptor.backingField?.let { report("fields", descriptor.type, it.annotations) }
|
||||
val delegateType = (declaration as? KtProperty)?.delegateExpression?.getType(context.trace.bindingContext)
|
||||
descriptor.delegateField?.let {
|
||||
if (delegateType == null) return@let
|
||||
report("delegate fields", delegateType, it.annotations)
|
||||
}
|
||||
descriptor.getter?.let { getterDescriptor ->
|
||||
if (getterDescriptor.contextReceiverParameters.isNotEmpty() || getterDescriptor.extensionReceiverParameter != null) return@let
|
||||
val type = getterDescriptor.returnType ?: return@let
|
||||
report("getters", type, getterDescriptor.annotations)
|
||||
}
|
||||
descriptor.setter?.valueParameters?.single()?.let { report("parameters", it.type, it.annotations) }
|
||||
descriptor.extensionReceiverParameter?.let { report("receivers", it.type, it.annotations) }
|
||||
descriptor.contextReceiverParameters.forEach { report("receivers", it.type, it.annotations) }
|
||||
}
|
||||
is PropertyAccessorDescriptor -> Unit
|
||||
is LocalVariableDescriptor -> {
|
||||
report("variables", descriptor.type, descriptor.annotations)
|
||||
}
|
||||
is CallableDescriptor -> {
|
||||
descriptor.extensionReceiverParameter?.let { report("receivers", it.type, it.annotations) }
|
||||
descriptor.contextReceiverParameters.forEach { report("receivers", it.type, it.annotations) }
|
||||
descriptor.valueParameters.forEach { report("parameters", it.type, it.annotations) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// !LANGUAGE: +ValueClasses
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// FIR_IDENTICAL
|
||||
|
||||
@Repeatable
|
||||
annotation class Ann
|
||||
|
||||
@[Ann Ann]
|
||||
@JvmInline
|
||||
value class A @Ann constructor(
|
||||
@[Ann Ann]
|
||||
@param:[Ann Ann]
|
||||
@property:[Ann Ann]
|
||||
@field:[Ann Ann]
|
||||
@get:[Ann Ann]
|
||||
val x: Int,
|
||||
@[Ann Ann]
|
||||
@param:[Ann Ann]
|
||||
@property:[Ann Ann]
|
||||
@field:[Ann Ann]
|
||||
@get:[Ann Ann]
|
||||
val y: Int,
|
||||
)
|
||||
|
||||
@[Ann Ann]
|
||||
@JvmInline
|
||||
value class B @Ann constructor(
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@param:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@property:[Ann Ann]
|
||||
@field:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@get:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val x: A,
|
||||
@[Ann Ann]
|
||||
@param:[Ann Ann]
|
||||
@property:[Ann Ann]
|
||||
@field:[Ann Ann]
|
||||
@get:[Ann Ann]
|
||||
val y: A?,
|
||||
) {
|
||||
<!INAPPLICABLE_JVM_NAME!>@JvmName("otherName")<!>
|
||||
fun f() = Unit
|
||||
}
|
||||
|
||||
@[Ann Ann]
|
||||
class C @Ann constructor(
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@param:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@property:[Ann Ann]
|
||||
@field:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@get:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
@set:[Ann Ann]
|
||||
@setparam:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
var x: A,
|
||||
@[Ann Ann]
|
||||
@param:[Ann Ann]
|
||||
@property:[Ann Ann]
|
||||
@field:[Ann Ann]
|
||||
@get:[Ann Ann]
|
||||
@set:[Ann Ann]
|
||||
@setparam:[Ann Ann]
|
||||
var y: A?,
|
||||
) {
|
||||
@delegate:[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val z by lazy { A(-100, -200) }
|
||||
|
||||
<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET, INAPPLICABLE_JVM_FIELD!>@JvmField<!>
|
||||
val e = x
|
||||
|
||||
init {
|
||||
if (2 + 2 == 4) {
|
||||
@[Ann Ann]
|
||||
val x = 4
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val y = A(1, 2)
|
||||
}
|
||||
|
||||
|
||||
fun f() {
|
||||
if (2 + 2 == 4) {
|
||||
@[Ann Ann]
|
||||
val x = 4
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val y = A(1, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@[Ann Ann]
|
||||
fun @receiver:[Ann Ann] A.t(@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>] a: A, @[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>] b: B, @[Ann Ann] c: C) {
|
||||
if (2 + 2 == 4) {
|
||||
@[Ann Ann]
|
||||
val x = 4
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val y = A(1, 2)
|
||||
}
|
||||
|
||||
fun f() {
|
||||
if (2 + 2 == 4) {
|
||||
@[Ann Ann]
|
||||
val x1 = 4
|
||||
@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>]
|
||||
val y1 = A(1, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@[Ann Ann]
|
||||
fun @receiver:[Ann Ann] C.t(@[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>] a: A, @[<!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!> <!ANNOTATION_ON_ILLEGAL_MULTI_FIELD_VALUE_CLASS_TYPED_TARGET!>Ann<!>] b: B, @[Ann Ann] c: C) = 4
|
||||
Generated
+6
@@ -33708,6 +33708,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/valueClasses"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/valueClasses/annotations.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithUniversal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("basicValueClassDeclaration.kt")
|
||||
public void testBasicValueClassDeclaration() throws Exception {
|
||||
|
||||
@@ -18,5 +18,5 @@ enum class JvmFieldApplicabilityProblem(val errorMessage: String) {
|
||||
NOT_PUBLIC_VAL_WITH_JVMFIELD("JvmField could be applied only if all interface companion properties are 'public final val' with '@JvmField' annotation"),
|
||||
TOP_LEVEL_PROPERTY_OF_MULTIFILE_FACADE("JvmField cannot be applied to top level property of a file annotated with ${JVM_MULTIFILE_CLASS_SHORT}"),
|
||||
DELEGATE("JvmField cannot be applied to delegated property"),
|
||||
RETURN_TYPE_IS_INLINE_CLASS("JvmField cannot be applied to a property of an inline class type")
|
||||
RETURN_TYPE_IS_VALUE_CLASS("JvmField cannot be applied to a property of a value class type"),
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isNullableType
|
||||
|
||||
val JVM_INLINE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmInline")
|
||||
val JVM_INLINE_ANNOTATION_CLASS_ID = ClassId.topLevel(JVM_INLINE_ANNOTATION_FQ_NAME)
|
||||
@@ -42,6 +43,9 @@ fun KotlinType.unsubstitutedUnderlyingTypes(): List<KotlinType> {
|
||||
|
||||
fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false
|
||||
|
||||
fun KotlinType.needsMfvcFlattening(): Boolean =
|
||||
constructor.declarationDescriptor?.run { isMultiFieldValueClass() && !isNullableType() } == true
|
||||
|
||||
fun KotlinType.substitutedUnderlyingType(): KotlinType? =
|
||||
unsubstitutedUnderlyingType()?.let { TypeSubstitutor.create(this).substitute(it, Variance.INVARIANT) }
|
||||
|
||||
|
||||
+2
-3
@@ -12,8 +12,7 @@ import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isValueClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
@@ -481,7 +480,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val ConeKotlinType.isUnsupportedInlineType: Boolean
|
||||
get() = isInlineClass(session) && !isPrimitiveOrNullablePrimitive
|
||||
get() = isValueClass(session) && !isPrimitiveOrNullablePrimitive
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
|
||||
Reference in New Issue
Block a user