def apply()

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
    }
  }