Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scala3 #224

Merged
merged 16 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading