@@ -809,8 +809,6 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
809
809
}
810
810
}
811
811
812
- private case class FieldAnnotations (partiallyMappedName : Option [String ], transient : Boolean , stringified : Boolean )
813
-
814
812
private case class DecoderMethodKey (tpe : TypeRepr , isStringified : Boolean , useDiscriminator : Boolean )
815
813
816
814
private case class EncoderMethodKey (tpe : TypeRepr , isStringified : Boolean , discriminatorKeyValue : Option [(String , String )])
@@ -846,15 +844,22 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
846
844
private val unitTpe = defn.UnitClass .typeRef
847
845
private val anyTpe = defn.AnyClass .typeRef
848
846
private val arrayOfAnyTpe = defn.ArrayClass .typeRef.appliedTo(anyTpe)
849
- private val iArrayOfAnyRefTpe = TypeRepr .of[IArray [AnyRef ]]
850
847
private val stringTpe = TypeRepr .of[String ]
848
+ private val optionTpe = TypeRepr .of[Option [? ]]
851
849
private val tupleTpe = TypeRepr .of[Tuple ]
850
+ private val iterableTpe = TypeRepr .of[Iterable [? ]]
851
+ private val iteratorTpe = TypeRepr .of[Iterator [? ]]
852
+ private val arrayTpe = TypeRepr .of[Array [? ]]
853
+ private val iArrayOfAnyRefTpe = TypeRepr .of[IArray [AnyRef ]]
854
+ private val namedTpe = TypeRepr .of[named]
855
+ private val stringifiedTpe = TypeRepr .of[stringified]
856
+ private val transientTpe = TypeRepr .of[transient]
852
857
private val jsonKeyCodecTpe = TypeRepr .of[JsonKeyCodec ]
853
858
private val jsonValueCodecTpe = TypeRepr .of[JsonValueCodec ]
854
859
private val newArray = Select (New (TypeIdent (defn.ArrayClass )), defn.ArrayClass .primaryConstructor)
855
860
private val newArrayOfAny = TypeApply (newArray, List (Inferred (anyTpe)))
856
861
private val fromIArrayMethod = Select .unique(Ref (Symbol .requiredModule(" scala.runtime.TupleXXL" )), " fromIArray" )
857
- private val asInstanceOfMethod = anyTpe.typeSymbol.methodMember (" asInstanceOf" ).head
862
+ private val asInstanceOfMethod = anyTpe.typeSymbol.declaredMethod (" asInstanceOf" ).head
858
863
private val inferredKeyCodecs = new mutable.HashMap [TypeRepr , Option [Expr [JsonKeyCodec [? ]]]]
859
864
private val inferredValueCodecs = new mutable.HashMap [TypeRepr , Option [Expr [JsonValueCodec [? ]]]]
860
865
private val inferredOrderings = new mutable.HashMap [TypeRepr , Term ]
@@ -959,17 +964,17 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
959
964
else tpe.typeSymbol.companionModule
960
965
}
961
966
962
- private def isOption (tpe : TypeRepr , types : List [TypeRepr ]): Boolean = tpe <:< TypeRepr .of[ Option [ ? ]] &&
963
- (cfg.skipNestedOptionValues || ! types.headOption.exists(_ <:< TypeRepr .of[ Option [ ? ]] ))
967
+ private def isOption (tpe : TypeRepr , types : List [TypeRepr ]): Boolean = tpe <:< optionTpe &&
968
+ (cfg.skipNestedOptionValues || ! types.headOption.exists(_ <:< optionTpe ))
964
969
965
970
private def isNullable (tpe : TypeRepr ): Boolean = tpe match
966
971
case OrType (left, right) => isNullable(right) || isNullable(left)
967
972
case _ => tpe =:= TypeRepr .of[Null ]
968
973
969
974
private def isIArray (tpe : TypeRepr ): Boolean = tpe.typeSymbol.fullName == " scala.IArray$package$.IArray"
970
975
971
- private def isCollection (tpe : TypeRepr ): Boolean = tpe <:< TypeRepr .of[ Iterable [ ? ]] ||
972
- tpe <:< TypeRepr .of[ Iterator [ ? ]] || tpe <:< TypeRepr .of[ Array [ ? ]] || isIArray(tpe)
976
+ private def isCollection (tpe : TypeRepr ): Boolean =
977
+ tpe <:< iterableTpe || tpe <:< iteratorTpe || tpe <:< arrayTpe || isIArray(tpe)
973
978
974
979
private def isJavaEnum (tpe : TypeRepr ): Boolean = tpe <:< TypeRepr .of[java.lang.Enum [? ]]
975
980
@@ -1121,43 +1126,18 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
1121
1126
})
1122
1127
1123
1128
private def getClassInfo (tpe : TypeRepr ): ClassInfo = classInfos.getOrElseUpdate(tpe, {
1124
- def hasSupportedAnnotation (m : Symbol ): Boolean = m.annotations.exists { a =>
1125
- val tpe = a.tpe
1126
- tpe =:= TypeRepr .of[named] || tpe =:= TypeRepr .of[transient] || tpe =:= TypeRepr .of[stringified] ||
1127
- (cfg.scalaTransientSupport && tpe =:= TypeRepr .of[scala.transient])
1128
- }
1129
-
1130
1129
def supportedTransientTypeNames : String =
1131
- if (cfg.scalaTransientSupport) s " ' ${Type .show[transient] }' (or ' ${Type .show [scala.transient]}') "
1132
- else s " ' ${Type .show[transient] }') "
1130
+ if (cfg.scalaTransientSupport) s " ' ${transientTpe .show}' (or ' ${TypeRepr .of [scala.transient].show }') "
1131
+ else s " ' ${transientTpe .show}') "
1133
1132
1134
1133
val tpeTypeArgs = typeArgs(tpe)
1135
1134
val tpeClassSym = tpe.classSymbol.getOrElse(fail(s " Expected that ${tpe.show} has classSymbol " ))
1136
1135
val primaryConstructor = tpeClassSym.primaryConstructor
1137
- var annotations = Map .empty[String , FieldAnnotations ]
1138
1136
val caseFields = tpeClassSym.caseFields
1139
- var companionRefAndMembers : (Ref , List [Symbol ]) = null
1140
1137
var fieldMembers : List [Symbol ] = null
1138
+ var companionRefAndClass : (Ref , Symbol ) = null
1141
1139
var methodMembers : List [Symbol ] = null
1142
1140
1143
- tpeClassSym.fieldMembers.foreach {
1144
- case m : Symbol if hasSupportedAnnotation(m) =>
1145
- val name = m.name
1146
- val named = m.annotations.count(_.tpe =:= TypeRepr .of[named])
1147
- if (named > 1 ) fail(s " Duplicated ' ${TypeRepr .of[named].show}' defined for ' $name' of ' ${tpe.show}'. " )
1148
- val trans = m.annotations.count(a => a.tpe =:= TypeRepr .of[transient] ||
1149
- (cfg.scalaTransientSupport && a.tpe =:= TypeRepr .of[scala.transient]))
1150
- if (trans > 1 ) warn(s " Duplicated $supportedTransientTypeNames defined for ' $name' of ' ${tpe.show}'. " )
1151
- val strings = m.annotations.count(_.tpe =:= TypeRepr .of[stringified])
1152
- if (strings > 1 ) warn(s " Duplicated ' ${TypeRepr .of[stringified].show}' defined for ' $name' of ' ${tpe.show}'. " )
1153
- if ((named > 0 || strings > 0 ) && trans > 0 )
1154
- warn(s " Both $supportedTransientTypeNames and ' ${Type .show[named]}' or " +
1155
- s " $supportedTransientTypeNames and ' ${Type .show[stringified]}' defined for ' $name' of ' ${tpe.show}'. " )
1156
- val partiallyMappedName = namedValueOpt(m.annotations.find(_.tpe =:= TypeRepr .of[named]), tpe)
1157
- annotations = annotations.updated(name, new FieldAnnotations (partiallyMappedName, trans > 0 , strings > 0 ))
1158
- case _ =>
1159
- }
1160
-
1161
1141
def createFieldInfos (params : List [Symbol ], typeParams : List [Symbol ], fieldIndex : Boolean => Int ): List [FieldInfo ] = params.map {
1162
1142
var i = 0
1163
1143
symbol =>
@@ -1171,20 +1151,18 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
1171
1151
case _ : TypeBounds =>
1172
1152
fail(s " Type bounds are not supported for type ' ${tpe.show}' with field type for $name ' ${fieldTpe.show}' " )
1173
1153
case _ =>
1174
- val defaultValue = if (! cfg.requireDefaultFields && symbol.flags.is(Flags .HasDefault )) {
1175
- val dvMemberName = " $lessinit$greater$default$" + i
1176
- if (companionRefAndMembers eq null ) {
1154
+ val defaultValue = if (! cfg.requireDefaultFields && symbol.flags.is(Flags .HasDefault )) new Some ({
1155
+ if (companionRefAndClass eq null ) {
1177
1156
val typeSymbol = tpe.typeSymbol
1178
- companionRefAndMembers = (Ref (typeSymbol.companionModule), typeSymbol.companionClass.methodMembers)
1179
- }
1180
- companionRefAndMembers._2.collectFirst { case methodSymbol if methodSymbol.name == dvMemberName =>
1181
- val dvSelectNoTypes = Select (companionRefAndMembers._1, methodSymbol)
1182
- methodSymbol.paramSymss match
1183
- case Nil => dvSelectNoTypes
1184
- case List (params) if params.exists(_.isTypeParam) => TypeApply (dvSelectNoTypes, tpeTypeArgs.map(Inferred (_)))
1185
- case paramss => fail(s " Default method for $name of class ${tpe.show} have a complex parameter list: $paramss" )
1157
+ companionRefAndClass = (Ref (typeSymbol.companionModule), typeSymbol.companionClass)
1186
1158
}
1187
- } else None
1159
+ val methodSymbol = companionRefAndClass._2.declaredMethod(" $lessinit$greater$default$" + i).head
1160
+ val dvSelectNoTypes = Select (companionRefAndClass._1, methodSymbol)
1161
+ methodSymbol.paramSymss match
1162
+ case Nil => dvSelectNoTypes
1163
+ case List (params) if params.exists(_.isTypeParam) => TypeApply (dvSelectNoTypes, tpeTypeArgs.map(Inferred (_)))
1164
+ case paramss => fail(s " Default method for $name of class ${tpe.show} have a complex parameter list: $paramss" )
1165
+ }) else None
1188
1166
val getterOrField = caseFields.find(_.name == name) match
1189
1167
case Some (caseField) => caseField
1190
1168
case _ =>
@@ -1199,10 +1177,31 @@ private class JsonCodecMakerInstance(cfg: CodecMakerConfig)(using Quotes) {
1199
1177
if (! getterOrField.exists || getterOrField.flags.is(Flags .PrivateLocal )) {
1200
1178
fail(s " Getter or field ' $name' of ' ${tpe.show}' is private. It should be defined as 'val' or 'var' in the primary constructor. " )
1201
1179
}
1202
- val annotationOption = annotations.get(name)
1203
- val mappedName = annotationOption.flatMap(_.partiallyMappedName).getOrElse(cfg.fieldNameMapper(name).getOrElse(name))
1204
- val isStringified = annotationOption.exists(_.stringified)
1205
- val isTransient = annotationOption.exists(_.transient)
1180
+ var named : Option [Term ] = None
1181
+ var isStringified : Boolean = false
1182
+ var isTransient : Boolean = false
1183
+ getterOrField.annotations.foreach { annotation =>
1184
+ val aTpe = annotation.tpe
1185
+ if (aTpe =:= namedTpe) {
1186
+ if (named eq None ) named = new Some (annotation)
1187
+ else fail(s " Duplicated ' ${namedTpe.show}' defined for ' $name' of ' ${tpe.show}'. " )
1188
+ } else if (aTpe =:= stringifiedTpe) {
1189
+ if (isStringified) warn(s " Duplicated ' ${stringifiedTpe.show}' defined for ' $name' of ' ${tpe.show}'. " )
1190
+ isStringified = true
1191
+ } else if (aTpe =:= transientTpe || (cfg.scalaTransientSupport && aTpe =:= TypeRepr .of[scala.transient])) {
1192
+ if (isTransient) warn(s " Duplicated $supportedTransientTypeNames defined for ' $name' of ' ${tpe.show}'. " )
1193
+ isTransient = true
1194
+ }
1195
+ }
1196
+ if (((named ne None ) || isStringified) && isTransient) {
1197
+ warn(s " Both $supportedTransientTypeNames and ' ${namedTpe.show}' or " +
1198
+ s " $supportedTransientTypeNames and ' ${stringifiedTpe.show}' defined for ' $name' of ' ${tpe.show}'. " )
1199
+ }
1200
+ val mappedName = namedValueOpt(named, tpe) match
1201
+ case Some (name1) => name1
1202
+ case _ => cfg.fieldNameMapper(name) match
1203
+ case Some (name2) => name2
1204
+ case _ => name
1206
1205
val index = fieldIndex(isTransient)
1207
1206
new FieldInfo (symbol, mappedName, getterOrField, defaultValue, fieldTpe, isTransient, isStringified, index)
1208
1207
}
0 commit comments