Skip to content

Commit

Permalink
Merge pull request #15 from jatcwang/scala3
Browse files Browse the repository at this point in the history
Scala3
  • Loading branch information
jatcwang authored Aug 22, 2021
2 parents b837d40 + 367695d commit 54c762d
Show file tree
Hide file tree
Showing 24 changed files with 495 additions and 140 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.13.6]
scala: [2.13.6, 3.0.1]
java: [adopt@1.11]
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -115,6 +115,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.0.1)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-3.0.1-${{ matrix.java }}

- name: Inflate target directories (3.0.1)
run: |
tar xf targets.tar
rm targets.tar
- name: Setup ruby
uses: actions/setup-ruby@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ RUNNING_PID
metals.sbt
.bsp
TempGo.scala
metals.sbt
1 change: 1 addition & 0 deletions .jvmopts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Xmx3G
3 changes: 2 additions & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
version=2.3.2
version="3.0.0-RC3"
maxColumn = 120
trailingCommas = always
continuationIndent.defnSite = 2

rewrite.rules = [PreferCurlyFors]
rewrite.redundantBraces.stringInterpolation = true
runner.dialect = scala3
45 changes: 29 additions & 16 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ val catsVersion = "2.6.1"
val scalatestVersion = "3.2.9"

val scala213 = "2.13.6"
val scala3 = "3.0.0"
val scala3 = "3.0.1"

val isScala3 = Def.setting {
// doesn't work well with >= 3.0.0 for `3.0.0-M1`
scalaVersion.value.startsWith("3")
}

inThisBuild(
List(
scalaVersion := scala213,
crossScalaVersions := Seq(scala213 /*, scala3*/ ),
crossScalaVersions := Seq(scala213, scala3),
organization := "com.github.jatcwang",
homepage := Some(url("https://github.com/jatcwang/difflicious")),
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
Expand All @@ -31,14 +36,16 @@ lazy val core = Project("difflicious-core", file("modules/core"))
.settings(commonSettings)
.settings(
libraryDependencies ++= Seq(
"com.propensive" %% "magnolia" % "0.17.0",
"dev.zio" %% "izumi-reflect" % "1.1.1",
"com.lihaoyi" %% "fansi" % "0.2.12",
if (isScala3.value) "com.softwaremill.magnolia1_3" %% "magnolia" % "1.0.0-M4"
// if (isScala3.value) "com.softwaremill.magnolia" %% "magnolia-core" % "2.0.0-M7-SNAPSHOT"
else "com.softwaremill.magnolia" %% "magnolia-core" % "1.0.0-M4",
"dev.zio" %% "izumi-reflect" % "1.1.2",
"com.lihaoyi" %% "fansi" % "0.2.14",
) ++ (
if (scalaVersion.value.startsWith("2"))
Seq("org.scala-lang" % "scala-reflect" % "2.13.5")
else
if (isScala3.value)
Seq.empty
else
Seq("org.scala-lang" % "scala-reflect" % scala213)
),
Compile / sourceGenerators += Def.task {
val file = (Compile / sourceManaged).value / "difflicious" / "TupleDifferInstances.scala"
Expand Down Expand Up @@ -86,12 +93,12 @@ lazy val coretest = Project("coretest", file("modules/coretest"))
),
// Test deps
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % munitVersion,
"org.scalameta" %% "munit" % munitVersion,
"org.scalameta" %% "munit-scalacheck" % munitVersion,
).map(_ % Test),
)

lazy val docs = project
lazy val docs: Project = project
.dependsOn(core, coretest, cats, munit, scalatest)
.enablePlugins(MicrositesPlugin)
.settings(
Expand All @@ -101,7 +108,13 @@ lazy val docs = project
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % scalatestVersion,
"org.scalameta" %% "mdoc" % "2.2.21",
),
makeMicrosite := Def.taskDyn {
val orig = (ThisProject / makeMicrosite).taskValue
if (isScala3.value) Def.task({})
else Def.task(orig.value)
}.value
)
.settings(
mdocIn := file("docs/docs"),
Expand All @@ -119,7 +132,7 @@ lazy val docs = project
micrositeGithubToken := sys.env.get("GITHUB_TOKEN"),
)
.settings(
// Disble any2stringAdd deprecation in md files. Seems like mdoc macro generates code which
// Disable any2stringAdd deprecation in md files. Seems like mdoc macro generates code which
// use implicit conversion to string
scalacOptions ~= { opts =>
val extraOpts =
Expand All @@ -143,18 +156,18 @@ lazy val benchmarks = Project("benchmarks", file("modules/benchmarks"))

lazy val commonSettings = Seq(
scalacOptions --= {
if (sys.env.get("CI").isDefined) {
if (sys.env.get("CI").isDefined && !isScala3.value) { // TODO: Reenable Scala 3 fatal warnings once nowarn is supported
Seq.empty
} else {
Seq("-Xfatal-warnings")
}
},
versionScheme := Some("early-semver"),
scalacOptions ++= Seq("-Wmacros:after"),
scalacOptions ++= (if (isScala3.value) Seq.empty[String] else Seq("-Wmacros:after")),
libraryDependencies ++= Seq(
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.0" cross CrossVersion.full),
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
).filterNot(_ => scalaVersion.value.startsWith("3")),
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.0" cross CrossVersion.full),
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
).filterNot(_ => isScala3.value),
)

lazy val noPublishSettings = Seq(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import difflicious.DiffResult.MismatchTypeResult
import difflicious.differ.RecordDiffer
import difflicious.internal.EitherGetSyntax._
import difflicious.utils.TypeName.SomeTypeName
import magnolia.{TypeName => MTypeName, _}
import difflicious.utils._
import magnolia._

import scala.collection.mutable
import scala.collection.immutable.ListMap
Expand Down Expand Up @@ -45,15 +44,18 @@ trait DifferGen {
case DiffInput.ExpectedOnly(expected) =>
ctx.dispatch(expected)(sub => sub.typeclass.diff(DiffInput.ExpectedOnly(sub.cast(expected))))
case DiffInput.Both(obtained, expected) => {
ctx.dispatch(obtained) { actualSubtype =>
ctx.dispatch(obtained) { obtainedSubtype =>
ctx.dispatch(expected) { expectedSubtype =>
if (actualSubtype.typeName.short == expectedSubtype.typeName.short) {
actualSubtype.typeclass
.diff(actualSubtype.cast(obtained), expectedSubtype.cast(expected).asInstanceOf[actualSubtype.SType])
if (obtainedSubtype.typeName.short == expectedSubtype.typeName.short) {
obtainedSubtype.typeclass
.diff(
obtainedSubtype.cast(obtained),
expectedSubtype.cast(expected).asInstanceOf[obtainedSubtype.SType]
)
} else {
MismatchTypeResult(
obtained = actualSubtype.typeclass.diff(DiffInput.ObtainedOnly(actualSubtype.cast(obtained))),
obtainedTypeName = toDiffliciousTypeName(actualSubtype.typeName),
obtained = obtainedSubtype.typeclass.diff(DiffInput.ObtainedOnly(obtainedSubtype.cast(obtained))),
obtainedTypeName = toDiffliciousTypeName(obtainedSubtype.typeName),
expected = expectedSubtype.typeclass.diff(DiffInput.ExpectedOnly(expectedSubtype.cast(expected))),
expectedTypeName = toDiffliciousTypeName(expectedSubtype.typeName),
pairType = PairType.Both,
Expand Down Expand Up @@ -132,8 +134,8 @@ trait DifferGen {

def derived[T]: Differ[T] = macro Magnolia.gen[T]

private def toDiffliciousTypeName(typeName: MTypeName): SomeTypeName = {
TypeName(
private def toDiffliciousTypeName(typeName: magnolia.TypeName): SomeTypeName = {
difflicious.utils.TypeName(
long = typeName.full,
short = typeName.short,
typeArguments = typeName.typeArguments
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package difflicious.internal

import difflicious.ConfigureOp.PairBy
import difflicious.internal.EitherGetSyntax.EitherExtensionOps
import difflicious.{ConfigurePath, Differ}
import difflicious.utils.Pairable
import difflicious.Differ

import scala.collection.mutable
import scala.annotation.{tailrec, nowarn}
import scala.annotation.{nowarn, tailrec}
import scala.reflect.macros.blackbox

trait ConfigureMethods[T] { this: Differ[T] =>
Expand All @@ -20,20 +17,6 @@ trait ConfigureMethods[T] { this: Differ[T] =>
macro ConfigureMacro.replace_impl[T, U]
}

// pairBy has to be defined differently for better type inference.
final class PairByOps[F[_], A](differ: Differ[F[A]]) {
def pairBy[B](f: A => B): Differ[F[A]] =
differ.configureRaw(ConfigurePath.current, PairBy.ByFunc(f)).unsafeGet

def pairByIndex: Differ[F[A]] =
differ.configureRaw(ConfigurePath.current, PairBy.Index).unsafeGet
}

trait ToPairByOps {
@nowarn("msg=.*never used.*")
implicit def toPairByOps[F[_]: Pairable, A](differ: Differ[F[A]]): PairByOps[F, A] = new PairByOps(differ)
}

// Implementation inspired by quicklen's path macro.
// See https://github.com/softwaremill/quicklens/blob/c2fd335b80f3d4d55a76d146d8308d95575dd749/quicklens/src/main/scala-2/com/softwaremill/quicklens/QuicklensMacros.scala
object ConfigureMacro {
Expand Down

This file was deleted.

120 changes: 120 additions & 0 deletions modules/core/src/main/scala-3/difflicious/DifferGen.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package difflicious
import difflicious.DiffResult.MismatchTypeResult
import difflicious.differ.RecordDiffer
import difflicious.utils.TypeName
import difflicious.utils.TypeName.SomeTypeName
import difflicious.DiffResult
import difflicious.internal.EitherGetSyntax._

import scala.collection.immutable.ListMap
import magnolia1._

import scala.collection.mutable

trait DifferGen extends Derivation[Differ]:
override def join[T](ctx: CaseClass[Differ, T]): Differ[T] =
new RecordDiffer[T](
ctx.params.map { p =>
val getter = p.deref
p.label -> Tuple2(getter.asInstanceOf[(T => Any)], p.typeclass.asInstanceOf[Differ[Any]])
}.to(ListMap),
isIgnored = false,
typeName = toDiffliciousTypeName(ctx.typeInfo)
)

override def split[T](ctx: SealedTrait[Differ, T]): Differ[T] =
new SealedTraitDiffer(ctx, isIgnored = false)

final class SealedTraitDiffer[T](ctx: SealedTrait[Differ, T], isIgnored: Boolean) extends Differ[T]:
override type R = DiffResult

override def diff(inputs: DiffInput[T]): DiffResult = inputs match
case DiffInput.ObtainedOnly(obtained) =>
ctx.choose(obtained)(sub => sub.typeclass.diff(DiffInput.ObtainedOnly(sub.cast(obtained))))
case DiffInput.ExpectedOnly(expected) =>
ctx.choose(expected)(sub => sub.typeclass.diff(DiffInput.ExpectedOnly(sub.cast(expected))))
case DiffInput.Both(obtained, expected) =>
ctx.choose(obtained) { obtainedSubtype =>
ctx.choose(expected) { expectedSubtype =>
if obtainedSubtype.typeInfo.short == expectedSubtype.typeInfo.short then
obtainedSubtype.typeclass.asInstanceOf[Differ[T]].diff(obtainedSubtype.value, expectedSubtype.value)
else MismatchTypeResult(
obtained = obtainedSubtype.typeclass.diff(DiffInput.ObtainedOnly(obtainedSubtype.cast(obtained))),
obtainedTypeName = toDiffliciousTypeName(obtainedSubtype.typeInfo),
expected = expectedSubtype.typeclass.diff(DiffInput.ExpectedOnly(expectedSubtype.cast(expected))),
expectedTypeName = toDiffliciousTypeName(expectedSubtype.typeInfo),
pairType = PairType.Both,
isIgnored = isIgnored,
)
}
}


override def configureIgnored(newIgnored: Boolean): Differ[T] =
val newSubtypes = mutable.ArrayBuffer.empty[SealedTrait.Subtype[Differ, T, Any]]
ctx.subtypes.map { sub =>
newSubtypes += SealedTrait.Subtype[Differ, T, Any](
typeInfo = sub.typeInfo,
annotations = sub.annotations,
typeAnnotations = sub.typeAnnotations,
isObject = sub.isObject,
index = sub.index,
callByNeed =
CallByNeed(sub.typeclass.configureRaw(ConfigurePath.current, ConfigureOp.SetIgnored(newIgnored)).unsafeGet.asInstanceOf[Differ[Any]]),
isType = sub.cast.isDefinedAt,
asType = sub.cast.apply,
)
}
val newSealedTrait = new SealedTrait(
typeInfo = ctx.typeInfo,
subtypes = IArray(newSubtypes.toArray: _*),
annotations = ctx.annotations,
typeAnnotations = ctx.typeAnnotations,
)
new SealedTraitDiffer[T](newSealedTrait, isIgnored = newIgnored)

protected def configurePath(
step: String,
nextPath: ConfigurePath,
op: ConfigureOp
): Either[ConfigureError, Differ[T]] =
ctx.subtypes.zipWithIndex.find{ (sub, _) => sub.typeInfo.short == step} match {
case Some((sub, idx)) =>
sub.typeclass
.configureRaw(nextPath, op)
.map { newDiffer =>
val newSubtype = SealedTrait.Subtype[Differ, T, Any](
typeInfo = sub.typeInfo,
annotations = sub.annotations,
typeAnnotations = sub.typeAnnotations,
isObject = sub.isObject,
index = sub.index,
callByNeed = CallByNeed(newDiffer.asInstanceOf[Differ[Any]]),
isType = sub.cast.isDefinedAt,
asType = sub.cast.apply,
)
val newSubtypes = ctx.subtypes.updated(idx, newSubtype)
val newSealedTrait = new SealedTrait(
typeInfo = ctx.typeInfo,
subtypes = newSubtypes,
annotations = ctx.annotations,
typeAnnotations = ctx.typeAnnotations,
)
new SealedTraitDiffer[T](newSealedTrait, isIgnored)
}
case None =>
Left(ConfigureError.UnrecognizedSubType(nextPath, ctx.subtypes.map(_.typeInfo.short).toVector))
}

protected def configurePairBy(path: ConfigurePath, op: ConfigureOp.PairBy[_]): Either[ConfigureError, Differ[T]] =
Left(ConfigureError.InvalidConfigureOp(path, op, "SealedTraitDiffer"))

end SealedTraitDiffer

private def toDiffliciousTypeName(typeInfo: TypeInfo): SomeTypeName = {
TypeName(
long = s"${typeInfo.owner}.${typeInfo.short}",
short = typeInfo.short,
typeArguments = typeInfo.typeParams.map(toDiffliciousTypeName).toList
)
}
Loading

0 comments on commit 54c762d

Please sign in to comment.