Skip to content

Commit

Permalink
Merge pull request #224 from disneystreaming/dfrancoeur/scala3
Browse files Browse the repository at this point in the history
Add scala3
  • Loading branch information
daddykotex authored Dec 20, 2023
2 parents 2fd0853 + 5b6de1a commit 8111fdd
Show file tree
Hide file tree
Showing 21 changed files with 271 additions and 234 deletions.
7 changes: 2 additions & 5 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import $file.buildDeps

import mill._
import buildSetup._
import buildSetup.ScalaVersions._
import coursier.maven.MavenRepository
import mill.contrib.scalapblib.ScalaPBModule
import mill.contrib.buildinfo
Expand All @@ -17,10 +18,6 @@ import mill.contrib.buildinfo.BuildInfo

import scala.Ordering.Implicits._

val scala212 = "2.12.18"
val scala213 = "2.13.12"
val scalaVersions = List(scala213, scala212)

object `compiler-core` extends Cross[CompilerCoreModule](scalaVersions)
trait CompilerCoreModule
extends CrossScalaModule
Expand Down Expand Up @@ -304,7 +301,7 @@ trait ProtoModule
def ivyDeps = super.ivyDeps() ++ Agg(
buildDeps.smithy.build,
buildDeps.scalapb.compilerPlugin,
buildDeps.scalapb.protocCache
buildDeps.scalapb.protocCache.withDottyCompat(scalaVersion())
)
def scalaPBVersion = buildDeps.scalapb.version

Expand Down
26 changes: 18 additions & 8 deletions buildSetup.sc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import mill.scalalib.api.ZincWorkerUtil

import scala.Ordering.Implicits._

object ScalaVersions {
val scala212 = "2.12.18"
val scala213 = "2.13.12"
val scala3 = "3.3.1"

val scalaVersions = List(scala213, scala212, scala3)
}

trait BaseModule extends Module with HeaderModule {
def millSourcePath: os.Path = {
val originalRelativePath = super.millSourcePath.relativeTo(os.pwd)
Expand Down Expand Up @@ -99,14 +107,17 @@ trait BasePublishModule extends BaseModule with CiReleaseModule {
}

trait BaseScala213Module extends BaseScalaModule with ScalafmtModule {
override def scalaVersion = T.input("2.13.12")
override def scalaVersion = T.input(ScalaVersions.scala213)
}

trait BaseScalaModule extends ScalaModule with BaseModule {

override def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg(
ivy"org.typelevel:::kind-projector:0.13.2"
)
override def scalacPluginIvyDeps = T {
val sv = scalaVersion()
val plugins =
if (sv.startsWith("2.")) Agg(ivy"org.typelevel:::kind-projector:0.13.2")
else Agg.empty
super.scalacPluginIvyDeps() ++ plugins
}

def scalacOptions = T {
super.scalacOptions() ++ scalacOptionsFor(scalaVersion())
Expand Down Expand Up @@ -163,9 +174,8 @@ case class ScalacOption(

// format: off
private val allScalacOptions = Seq(
ScalacOption("-Xsource:3", isSupported = version => v211 <= version || version < v300), // Treat compiler input as Scala source for the specified version, see scala/bug#8126.
ScalacOption("-Xsource:3", isSupported = version => v211 <= version && version < v300), // Treat compiler input as Scala source for the specified version, see scala/bug#8126.
ScalacOption("-deprecation", isSupported = version => version < v213 || v300 <= version), // Emit warning and location for usages of deprecated APIs. Not really removed but deprecated in 2.13.
ScalacOption("-migration", isSupported = v300 <= _), // Emit warning and location for migration issues from Scala 2.
ScalacOption("-explaintypes", isSupported = _ < v300), // Explain type errors in more detail.
ScalacOption("-explain-types", isSupported = v300 <= _), // Explain type errors in more detail.
ScalacOption("-explain", isSupported = v300 <= _), // Explain errors in more detail.
Expand All @@ -177,7 +187,7 @@ private val allScalacOptions = Seq(
ScalacOption("-language:existentials,experimental.macros,higherKinds,implicitConversions", isSupported = v300 <= _), // the four options above, dotty style
ScalacOption("-unchecked"), // Enable additional warnings where generated code depends on assumptions.
ScalacOption("-Xcheckinit", isSupported = _ < v300), // Wrap field accessors to throw an exception on uninitialized access.
ScalacOption("-Xfatal-warnings"), // Fail the compilation if there are any warnings.
ScalacOption("-Xfatal-warnings", isSupported = _ < v300), // Fail the compilation if there are any warnings. Disabled for scala3 because some warnings can't be `nowarn`ed
ScalacOption("-Xlint", isSupported = _ < v211), // Used to mean enable all linting options but now the syntax for that is different (-Xlint:_ I think)
ScalacOption("-Xlint:adapted-args", isSupported = version => v211 <= version && version < v300), // Warn if an argument list is modified to match the receiver.
ScalacOption("-Xlint:by-name-right-associative", isSupported = version => v211 <= version && version < v213), // By-name parameter of right associative operator.
Expand Down
294 changes: 160 additions & 134 deletions modules/compiler-core/src/internals/IModelToSmithy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,131 +44,164 @@ import scala.jdk.CollectionConverters._
private[compiler] final class IModelToSmithy(useEnumTraitSyntax: Boolean)
extends (IModel => Model) {

def toStructure(s: Structure): JShape = {
val members = s.localFields.map { case Field(id, tpe, hints) =>
val memName = id.memberName.value.toString
val nameWillNeedChange = sanitizeMemberName(memName) != memName
def isHeaderOrQuery = hints.exists {
case Header(_) => true
case QueryParam(_) => true
case _ => false
}
val jsonNameHint =
if (nameWillNeedChange && !isHeaderOrQuery)
List(Hint.JsonName(memName))
else List.empty

val memberBuilder = MemberShape
.builder()
.id(id.toSmithy)
.target(tpe.toSmithy)

hintsToTraits(hints ++ jsonNameHint).foreach(memberBuilder.addTrait(_))
memberBuilder.build()
}
val mixins = s.hints.collect { case Hint.HasMixin(defId) =>
StructureShape.builder.id(defId.toSmithy).build()
}.asJava
val builder = StructureShape
.builder()
.id(s.id.toSmithy)

hintsToTraits(s.hints).foreach(builder.addTrait(_))
members.foreach(builder.addMember(_))
mixins.forEach { m =>
val _ = builder.addMixin(m)
}
builder.build()
}

def toMap(m: MapDef): JShape = {
val builder = MapShape
.builder()
.id(m.id.toSmithy)
.key(m.key.toSmithy)
.value(m.value.toSmithy)

hintsToTraits(m.hints).foreach(builder.addTrait(_))
builder.build()
}

def toList(l: ListDef): JShape = {
val builder = ListShape
.builder()
.id(l.id.toSmithy)
.member(l.member.toSmithy)
hintsToTraits(l.hints).foreach(builder.addTrait(_))
builder.build()
}

def toSet(s: SetDef): JShape = {
val builder = ListShape
.builder()
.id(s.id.toSmithy)
.member(s.member.toSmithy)
.addTrait(new UniqueItemsTrait())

hintsToTraits(s.hints).foreach(builder.addTrait(_))
builder.build()
}

def toUnion(u: Union): JShape = {
val builder =
UnionShape.builder().id(u.id.toSmithy)
u.alts.foreach { alt =>
val memName = alt.id.memberName.value.toString
val nameWillNeedChange = sanitizeMemberName(memName) != memName
val jsonNameHint =
if (nameWillNeedChange)
List(Hint.JsonName(memName))
else List.empty
val memberBuilder = MemberShape
.builder()
.id(alt.id.toSmithy)
.target(alt.tpe.toSmithy)

hintsToTraits(alt.hints ++ jsonNameHint)
.foreach(memberBuilder.addTrait(_))
builder.addMember(memberBuilder.build())
}
u.kind match {
case UnionKind.Discriminated(d) =>
builder.addTrait(new DiscriminatedUnionTrait(d))
case UnionKind.Untagged => builder.addTrait(new UntaggedUnionTrait())
case UnionKind.ContentTypeDiscriminated =>
builder.addTrait(new ContentTypeDiscriminatedTrait())
case UnionKind.Tagged => ()
}
hintsToTraits(u.hints).foreach(builder.addTrait(_))
builder.build()
}

def toPrimitive(n: Newtype): JShape = {
val builder
: AbstractShapeBuilder[_ <: AbstractShapeBuilder[_, _], _ <: JShape] =
n.target.name.segments.last.value.toString match {
case "String" => StringShape.builder()
case "Integer" => IntegerShape.builder()
case "Long" => LongShape.builder()
case "BigInteger" => BigIntegerShape.builder()
case "BigDecimal" => BigDecimalShape.builder()
case "Short" => ShortShape.builder()
case "Float" => FloatShape.builder()
case "Double" => DoubleShape.builder()
case "Boolean" => BooleanShape.builder()
case "Byte" => ByteShape.builder()
case "Timestamp" => TimestampShape.builder()
case "Document" => DocumentShape.builder()
case "UUID" => StringShape.builder().addTrait(new UuidFormatTrait())
case "Null" =>
StructureShape.builder().addTrait(new NullFormatTrait())
case other =>
sys.error(
s"error processing ${n.id}, found $other"
)
}
hintsToTraits(n.hints).foreach(builder.addTrait(_))
builder.id(n.id.toSmithy)
builder.build()
}

def toOperation(op: OperationDef): JShape = {
val builder =
OperationShape.builder().id(op.id.toSmithy)
op.output.foreach(o => builder.output(o.toSmithy))
op.input.foreach(i => builder.input(i.toSmithy))
hintsToTraits(op.hints).foreach(builder.addTrait(_))
op.errors.foreach(e => builder.addError(e.toSmithy))
builder.build()
}

def toService(s: ServiceDef): JShape = {
val builder = ServiceShape
.builder()
.id(s.id.toSmithy)
hintsToTraits(s.hints).foreach(builder.addTrait(_))
s.operations.foreach(o => builder.addOperation(o.toSmithy))
builder.build()
}

def apply(iModel: IModel): Model = {
val shapes = iModel.definitions.map {
case Structure(id, fields, _, structHints) =>
val members = fields.map { case Field(id, tpe, hints) =>
val memName = id.memberName.value.toString
val nameWillNeedChange = sanitizeMemberName(memName) != memName
def isHeaderOrQuery = hints.exists {
case Header(_) => true
case QueryParam(_) => true
case _ => false
}
val jsonNameHint =
if (nameWillNeedChange && !isHeaderOrQuery)
List(Hint.JsonName(memName))
else List.empty

MemberShape
.builder()
.id(id.toSmithy)
.target(tpe.toSmithy)
.addHints(hints ++ jsonNameHint)
.build()
}
val mixins = structHints.collect { case Hint.HasMixin(defId) =>
StructureShape.builder.id(defId.toSmithy).build()
}.asJava
val builder = StructureShape
.builder()
.id(id.toSmithy)
.addHints(structHints)
members.foreach(builder.addMember(_))
mixins.forEach { m =>
val _ = builder.addMixin(m)
}
builder.build()
case MapDef(id, key, value, hints) =>
MapShape
.builder()
.id(id.toSmithy)
.key(key.toSmithy)
.value(value.toSmithy)
.addHints(hints)
.build()
case ListDef(id, member, hints) =>
ListShape
.builder()
.id(id.toSmithy)
.member(member.toSmithy)
.addHints(hints)
.build()
case SetDef(id, member, hints) =>
ListShape
.builder()
.id(id.toSmithy)
.member(member.toSmithy)
.addHints(hints)
.addTrait(new UniqueItemsTrait())
.build()
case Union(id, altNames, unionKind, hints) =>
val builder =
UnionShape.builder().id(id.toSmithy).addHints(hints)
altNames.foreach { alt =>
val memName = alt.id.memberName.value.toString
val nameWillNeedChange = sanitizeMemberName(memName) != memName
val jsonNameHint =
if (nameWillNeedChange)
List(Hint.JsonName(memName))
else List.empty
val member = MemberShape
.builder()
.id(alt.id.toSmithy)
.target(alt.tpe.toSmithy)
.addHints(alt.hints ++ jsonNameHint)
.build()
builder.addMember(member)
}
unionKind match {
case UnionKind.Discriminated(d) =>
builder.addTrait(new DiscriminatedUnionTrait(d))
case UnionKind.Untagged => builder.addTrait(new UntaggedUnionTrait())
case UnionKind.ContentTypeDiscriminated =>
builder.addTrait(new ContentTypeDiscriminatedTrait())
case UnionKind.Tagged => ()
}
builder.build()
case Newtype(id, target, hints) =>
val builder = target.name.segments.last.value.toString match {
case "String" => StringShape.builder()
case "Integer" => IntegerShape.builder()
case "Long" => LongShape.builder()
case "BigInteger" => BigIntegerShape.builder()
case "BigDecimal" => BigDecimalShape.builder()
case "Short" => ShortShape.builder()
case "Float" => FloatShape.builder()
case "Double" => DoubleShape.builder()
case "Boolean" => BooleanShape.builder()
case "Byte" => ByteShape.builder()
case "Timestamp" => TimestampShape.builder()
case "Document" => DocumentShape.builder()
case "UUID" => StringShape.builder().addTrait(new UuidFormatTrait())
case "Null" =>
StructureShape.builder().addTrait(new NullFormatTrait())
case other => sys.error(s"error processing $id, found $other")
}
builder.id(id.toSmithy)
hintsToTraits(hints).foreach(builder.addTrait(_))
builder.build()
case OperationDef(id, input, output, errors, hints) =>
val builder =
OperationShape.builder().id(id.toSmithy)
output.foreach(o => builder.output(o.toSmithy))
input.foreach(i => builder.input(i.toSmithy))
hintsToTraits(hints).foreach(builder.addTrait(_))
errors.foreach(e => builder.addError(e.toSmithy))
builder.build()
case ServiceDef(id, operations, hints) =>
val builder = ServiceShape
.builder()
.id(id.toSmithy)
.addHints(hints)
operations.foreach(o => builder.addOperation(o.toSmithy))
builder.build()
case e: Enumeration => buildEnum(e)
case other =>
throw new IllegalArgumentException(s"Unexpected input: $other")
val shapes: Vector[JShape] = iModel.definitions.map {
case s: Structure => toStructure(s)
case m: MapDef => toMap(m)
case l: ListDef => toList(l)
case s: SetDef => toSet(s)
case u: Union => toUnion(u)
case n: Newtype => toPrimitive(n)
case op: OperationDef => toOperation(op)
case s: ServiceDef => toService(s)
case e: Enumeration => buildEnum(e)
}
val builder = Model.builder()
if (iModel.suppressions.nonEmpty) {
Expand Down Expand Up @@ -198,7 +231,9 @@ private[compiler] final class IModelToSmithy(useEnumTraitSyntax: Boolean)
val name = sanitizeEnumMember(value, idx)
enumBuilder.addMember(name, value)
}
enumBuilder.addHints(hints).build()

hintsToTraits(hints).foreach(enumBuilder.addTrait(_))
enumBuilder.build()
}

}
Expand Down Expand Up @@ -396,13 +431,4 @@ private[compiler] final class IModelToSmithy(useEnumTraitSyntax: Boolean)
case _ => List.empty
}

implicit class ShapeBuilderOps[A <: AbstractShapeBuilder[A, S], S <: JShape](
builder: AbstractShapeBuilder[A, S]
) {
final def addHints(hints: List[Hint]): A = {
hintsToTraits(hints).foreach(builder.addTrait)
builder.asInstanceOf[A]
}
}

}
Loading

0 comments on commit 8111fdd

Please sign in to comment.