in scrooge-generator/src/main/scala/com/twitter/scrooge/frontend/TypeResolver.scala [350:449]
def apply(c: RHS, fieldType: FieldType): RHS = {
@scala.annotation.tailrec
def loop(fieldType: FieldType, m: MapRHS): RHS = {
fieldType match {
case at: AnnotatedFieldType => loop(at.unwrap, m)
case MapType(keyType, valType, _) =>
m.copy(elems = m.elems.map { case (k, v) => (apply(k, keyType), apply(v, valType)) })
case st @ StructType(structLike: StructLike, _) =>
val fieldMultiMap: Map[String, Seq[(String, RHS)]] = m.elems
.collect {
case (StringLiteral(fieldName), value) => (fieldName, value)
}
.groupBy { case (fieldName, _) => fieldName }
val fieldMap: Map[String, RHS] = fieldMultiMap.collect {
case (fieldName: String, values: Seq[(String, RHS)]) if values.length == 1 =>
values.head
case (fieldName: String, _: Seq[(String, RHS)]) =>
throw TypeMismatchException(
s"Duplicate default values for $fieldName found for $fieldType",
m
)
// Can't have 0 elements here because fieldMultiMap is built by groupBy.
}
structLike match {
case u: Union =>
val definedFields = u.fields.collect {
case field if fieldMap.contains(field.sid.name) =>
(field, fieldMap(field.sid.name))
}
if (definedFields.isEmpty)
throw UndefinedConstantException(
s"Constant value missing for union ${u.originalName}",
m
)
if (definedFields.length > 1)
throw UndefinedConstantException(
s"Multiple constant values for union ${u.originalName}",
m
)
val (field, rhs) = definedFields.head
val resolvedRhs = apply(rhs, field.fieldType)
UnionRHS(sid = st.sid, field = field, initializer = resolvedRhs)
case struct: StructLike =>
val structMap = Map.newBuilder[Field, RHS]
struct.fields.foreach { field =>
val fieldName = field.sid.name
if (fieldMap.contains(fieldName)) {
val resolvedRhs = apply(fieldMap(fieldName), field.fieldType)
structMap += field -> resolvedRhs
} else if (!field.requiredness.isOptional && field.default.isEmpty) {
throw TypeMismatchException(s"Value required for $fieldName in $fieldType", m)
}
}
StructRHS(sid = st.sid, elems = structMap.result())
}
case _ => throw TypeMismatchException("Expecting " + fieldType + ", found " + m, m)
}
}
c match {
// list values and map values look the same in Thrift, but different in Java and Scala
// So we need type information in order to generated correct code.
case l @ ListRHS(elems) =>
fieldType match {
case ListType(eltType, _) => l.copy(elems = elems.map(e => apply(e, eltType)))
case SetType(eltType, _) => SetRHS(elems.map(e => apply(e, eltType)).toSet)
case _ => throw TypeMismatchException("Expecting " + fieldType + ", found " + l, c)
}
case m @ MapRHS(elems) =>
loop(fieldType, m)
case i @ IdRHS(id) =>
val (constFieldType, constRHS) = id match {
case sid: SimpleID =>
// When the rhs value is a simpleID, it can only be a constant
// defined in the same file
resolveConst(sid)
case qid @ QualifiedID(names) =>
fieldType match {
case EnumType(enum, _) =>
val resolvedFieldType = resolveFieldType(qid.qualifier)
val value = enum.values
.find(_.sid.name == names.last)
.getOrElse(throw UndefinedSymbolException(qid.fullName, qid))
(resolvedFieldType, EnumRHS(enum, value))
case t => resolveConst(qid)
}
}
if (constFieldType != fieldType)
throw TypeMismatchException(
s"Type mismatch: Expecting $fieldType, found ${id.fullName}: $constFieldType",
id
)
constRHS
case _ => c
}
}