Skip to content

Commit 54c762d

Browse files
authored
Merge pull request #15 from jatcwang/scala3
Scala3
2 parents b837d40 + 367695d commit 54c762d

File tree

24 files changed

+495
-140
lines changed

24 files changed

+495
-140
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
strategy:
2424
matrix:
2525
os: [ubuntu-latest]
26-
scala: [2.13.6]
26+
scala: [2.13.6, 3.0.1]
2727
java: [adopt@1.11]
2828
runs-on: ${{ matrix.os }}
2929
steps:
@@ -115,6 +115,16 @@ jobs:
115115
tar xf targets.tar
116116
rm targets.tar
117117
118+
- name: Download target directories (3.0.1)
119+
uses: actions/download-artifact@v2
120+
with:
121+
name: target-${{ matrix.os }}-3.0.1-${{ matrix.java }}
122+
123+
- name: Inflate target directories (3.0.1)
124+
run: |
125+
tar xf targets.tar
126+
rm targets.tar
127+
118128
- name: Setup ruby
119129
uses: actions/setup-ruby@v1
120130
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ RUNNING_PID
2424
metals.sbt
2525
.bsp
2626
TempGo.scala
27+
metals.sbt

.jvmopts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xmx3G

.scalafmt.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
version=2.3.2
1+
version="3.0.0-RC3"
22
maxColumn = 120
33
trailingCommas = always
44
continuationIndent.defnSite = 2
55

66
rewrite.rules = [PreferCurlyFors]
77
rewrite.redundantBraces.stringInterpolation = true
8+
runner.dialect = scala3

build.sbt

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ val catsVersion = "2.6.1"
33
val scalatestVersion = "3.2.9"
44

55
val scala213 = "2.13.6"
6-
val scala3 = "3.0.0"
6+
val scala3 = "3.0.1"
7+
8+
val isScala3 = Def.setting {
9+
// doesn't work well with >= 3.0.0 for `3.0.0-M1`
10+
scalaVersion.value.startsWith("3")
11+
}
712

813
inThisBuild(
914
List(
1015
scalaVersion := scala213,
11-
crossScalaVersions := Seq(scala213 /*, scala3*/ ),
16+
crossScalaVersions := Seq(scala213, scala3),
1217
organization := "com.github.jatcwang",
1318
homepage := Some(url("https://github.com/jatcwang/difflicious")),
1419
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
@@ -31,14 +36,16 @@ lazy val core = Project("difflicious-core", file("modules/core"))
3136
.settings(commonSettings)
3237
.settings(
3338
libraryDependencies ++= Seq(
34-
"com.propensive" %% "magnolia" % "0.17.0",
35-
"dev.zio" %% "izumi-reflect" % "1.1.1",
36-
"com.lihaoyi" %% "fansi" % "0.2.12",
39+
if (isScala3.value) "com.softwaremill.magnolia1_3" %% "magnolia" % "1.0.0-M4"
40+
// if (isScala3.value) "com.softwaremill.magnolia" %% "magnolia-core" % "2.0.0-M7-SNAPSHOT"
41+
else "com.softwaremill.magnolia" %% "magnolia-core" % "1.0.0-M4",
42+
"dev.zio" %% "izumi-reflect" % "1.1.2",
43+
"com.lihaoyi" %% "fansi" % "0.2.14",
3744
) ++ (
38-
if (scalaVersion.value.startsWith("2"))
39-
Seq("org.scala-lang" % "scala-reflect" % "2.13.5")
40-
else
45+
if (isScala3.value)
4146
Seq.empty
47+
else
48+
Seq("org.scala-lang" % "scala-reflect" % scala213)
4249
),
4350
Compile / sourceGenerators += Def.task {
4451
val file = (Compile / sourceManaged).value / "difflicious" / "TupleDifferInstances.scala"
@@ -86,12 +93,12 @@ lazy val coretest = Project("coretest", file("modules/coretest"))
8693
),
8794
// Test deps
8895
libraryDependencies ++= Seq(
89-
"org.scalameta" %% "munit" % munitVersion,
96+
"org.scalameta" %% "munit" % munitVersion,
9097
"org.scalameta" %% "munit-scalacheck" % munitVersion,
9198
).map(_ % Test),
9299
)
93100

94-
lazy val docs = project
101+
lazy val docs: Project = project
95102
.dependsOn(core, coretest, cats, munit, scalatest)
96103
.enablePlugins(MicrositesPlugin)
97104
.settings(
@@ -101,7 +108,13 @@ lazy val docs = project
101108
.settings(
102109
libraryDependencies ++= Seq(
103110
"org.scalatest" %% "scalatest" % scalatestVersion,
111+
"org.scalameta" %% "mdoc" % "2.2.21",
104112
),
113+
makeMicrosite := Def.taskDyn {
114+
val orig = (ThisProject / makeMicrosite).taskValue
115+
if (isScala3.value) Def.task({})
116+
else Def.task(orig.value)
117+
}.value
105118
)
106119
.settings(
107120
mdocIn := file("docs/docs"),
@@ -119,7 +132,7 @@ lazy val docs = project
119132
micrositeGithubToken := sys.env.get("GITHUB_TOKEN"),
120133
)
121134
.settings(
122-
// Disble any2stringAdd deprecation in md files. Seems like mdoc macro generates code which
135+
// Disable any2stringAdd deprecation in md files. Seems like mdoc macro generates code which
123136
// use implicit conversion to string
124137
scalacOptions ~= { opts =>
125138
val extraOpts =
@@ -143,18 +156,18 @@ lazy val benchmarks = Project("benchmarks", file("modules/benchmarks"))
143156

144157
lazy val commonSettings = Seq(
145158
scalacOptions --= {
146-
if (sys.env.get("CI").isDefined) {
159+
if (sys.env.get("CI").isDefined && !isScala3.value) { // TODO: Reenable Scala 3 fatal warnings once nowarn is supported
147160
Seq.empty
148161
} else {
149162
Seq("-Xfatal-warnings")
150163
}
151164
},
152165
versionScheme := Some("early-semver"),
153-
scalacOptions ++= Seq("-Wmacros:after"),
166+
scalacOptions ++= (if (isScala3.value) Seq.empty[String] else Seq("-Wmacros:after")),
154167
libraryDependencies ++= Seq(
155-
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.0" cross CrossVersion.full),
156-
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
157-
).filterNot(_ => scalaVersion.value.startsWith("3")),
168+
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.0" cross CrossVersion.full),
169+
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
170+
).filterNot(_ => isScala3.value),
158171
)
159172

160173
lazy val noPublishSettings = Seq(

modules/core/src/main/scala/difflicious/DifferGen.scala renamed to modules/core/src/main/scala-2.13/difflicious/DifferGen.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import difflicious.DiffResult.MismatchTypeResult
33
import difflicious.differ.RecordDiffer
44
import difflicious.internal.EitherGetSyntax._
55
import difflicious.utils.TypeName.SomeTypeName
6-
import magnolia.{TypeName => MTypeName, _}
7-
import difflicious.utils._
6+
import magnolia._
87

98
import scala.collection.mutable
109
import scala.collection.immutable.ListMap
@@ -45,15 +44,18 @@ trait DifferGen {
4544
case DiffInput.ExpectedOnly(expected) =>
4645
ctx.dispatch(expected)(sub => sub.typeclass.diff(DiffInput.ExpectedOnly(sub.cast(expected))))
4746
case DiffInput.Both(obtained, expected) => {
48-
ctx.dispatch(obtained) { actualSubtype =>
47+
ctx.dispatch(obtained) { obtainedSubtype =>
4948
ctx.dispatch(expected) { expectedSubtype =>
50-
if (actualSubtype.typeName.short == expectedSubtype.typeName.short) {
51-
actualSubtype.typeclass
52-
.diff(actualSubtype.cast(obtained), expectedSubtype.cast(expected).asInstanceOf[actualSubtype.SType])
49+
if (obtainedSubtype.typeName.short == expectedSubtype.typeName.short) {
50+
obtainedSubtype.typeclass
51+
.diff(
52+
obtainedSubtype.cast(obtained),
53+
expectedSubtype.cast(expected).asInstanceOf[obtainedSubtype.SType]
54+
)
5355
} else {
5456
MismatchTypeResult(
55-
obtained = actualSubtype.typeclass.diff(DiffInput.ObtainedOnly(actualSubtype.cast(obtained))),
56-
obtainedTypeName = toDiffliciousTypeName(actualSubtype.typeName),
57+
obtained = obtainedSubtype.typeclass.diff(DiffInput.ObtainedOnly(obtainedSubtype.cast(obtained))),
58+
obtainedTypeName = toDiffliciousTypeName(obtainedSubtype.typeName),
5759
expected = expectedSubtype.typeclass.diff(DiffInput.ExpectedOnly(expectedSubtype.cast(expected))),
5860
expectedTypeName = toDiffliciousTypeName(expectedSubtype.typeName),
5961
pairType = PairType.Both,
@@ -132,8 +134,8 @@ trait DifferGen {
132134

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

135-
private def toDiffliciousTypeName(typeName: MTypeName): SomeTypeName = {
136-
TypeName(
137+
private def toDiffliciousTypeName(typeName: magnolia.TypeName): SomeTypeName = {
138+
difflicious.utils.TypeName(
137139
long = typeName.full,
138140
short = typeName.short,
139141
typeArguments = typeName.typeArguments

modules/core/src/main/scala-2.13/difflicious/internal/ConfigureMethods.scala

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package difflicious.internal
22

3-
import difflicious.ConfigureOp.PairBy
4-
import difflicious.internal.EitherGetSyntax.EitherExtensionOps
5-
import difflicious.{ConfigurePath, Differ}
6-
import difflicious.utils.Pairable
3+
import difflicious.Differ
74

85
import scala.collection.mutable
9-
import scala.annotation.{tailrec, nowarn}
6+
import scala.annotation.{nowarn, tailrec}
107
import scala.reflect.macros.blackbox
118

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

23-
// pairBy has to be defined differently for better type inference.
24-
final class PairByOps[F[_], A](differ: Differ[F[A]]) {
25-
def pairBy[B](f: A => B): Differ[F[A]] =
26-
differ.configureRaw(ConfigurePath.current, PairBy.ByFunc(f)).unsafeGet
27-
28-
def pairByIndex: Differ[F[A]] =
29-
differ.configureRaw(ConfigurePath.current, PairBy.Index).unsafeGet
30-
}
31-
32-
trait ToPairByOps {
33-
@nowarn("msg=.*never used.*")
34-
implicit def toPairByOps[F[_]: Pairable, A](differ: Differ[F[A]]): PairByOps[F, A] = new PairByOps(differ)
35-
}
36-
3720
// Implementation inspired by quicklen's path macro.
3821
// See https://github.com/softwaremill/quicklens/blob/c2fd335b80f3d4d55a76d146d8308d95575dd749/quicklens/src/main/scala-2/com/softwaremill/quicklens/QuicklensMacros.scala
3922
object ConfigureMacro {

modules/core/src/main/scala-2.13/difflicious/utils/TypeNamePlatform.scala

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package difflicious
2+
import difflicious.DiffResult.MismatchTypeResult
3+
import difflicious.differ.RecordDiffer
4+
import difflicious.utils.TypeName
5+
import difflicious.utils.TypeName.SomeTypeName
6+
import difflicious.DiffResult
7+
import difflicious.internal.EitherGetSyntax._
8+
9+
import scala.collection.immutable.ListMap
10+
import magnolia1._
11+
12+
import scala.collection.mutable
13+
14+
trait DifferGen extends Derivation[Differ]:
15+
override def join[T](ctx: CaseClass[Differ, T]): Differ[T] =
16+
new RecordDiffer[T](
17+
ctx.params.map { p =>
18+
val getter = p.deref
19+
p.label -> Tuple2(getter.asInstanceOf[(T => Any)], p.typeclass.asInstanceOf[Differ[Any]])
20+
}.to(ListMap),
21+
isIgnored = false,
22+
typeName = toDiffliciousTypeName(ctx.typeInfo)
23+
)
24+
25+
override def split[T](ctx: SealedTrait[Differ, T]): Differ[T] =
26+
new SealedTraitDiffer(ctx, isIgnored = false)
27+
28+
final class SealedTraitDiffer[T](ctx: SealedTrait[Differ, T], isIgnored: Boolean) extends Differ[T]:
29+
override type R = DiffResult
30+
31+
override def diff(inputs: DiffInput[T]): DiffResult = inputs match
32+
case DiffInput.ObtainedOnly(obtained) =>
33+
ctx.choose(obtained)(sub => sub.typeclass.diff(DiffInput.ObtainedOnly(sub.cast(obtained))))
34+
case DiffInput.ExpectedOnly(expected) =>
35+
ctx.choose(expected)(sub => sub.typeclass.diff(DiffInput.ExpectedOnly(sub.cast(expected))))
36+
case DiffInput.Both(obtained, expected) =>
37+
ctx.choose(obtained) { obtainedSubtype =>
38+
ctx.choose(expected) { expectedSubtype =>
39+
if obtainedSubtype.typeInfo.short == expectedSubtype.typeInfo.short then
40+
obtainedSubtype.typeclass.asInstanceOf[Differ[T]].diff(obtainedSubtype.value, expectedSubtype.value)
41+
else MismatchTypeResult(
42+
obtained = obtainedSubtype.typeclass.diff(DiffInput.ObtainedOnly(obtainedSubtype.cast(obtained))),
43+
obtainedTypeName = toDiffliciousTypeName(obtainedSubtype.typeInfo),
44+
expected = expectedSubtype.typeclass.diff(DiffInput.ExpectedOnly(expectedSubtype.cast(expected))),
45+
expectedTypeName = toDiffliciousTypeName(expectedSubtype.typeInfo),
46+
pairType = PairType.Both,
47+
isIgnored = isIgnored,
48+
)
49+
}
50+
}
51+
52+
53+
override def configureIgnored(newIgnored: Boolean): Differ[T] =
54+
val newSubtypes = mutable.ArrayBuffer.empty[SealedTrait.Subtype[Differ, T, Any]]
55+
ctx.subtypes.map { sub =>
56+
newSubtypes += SealedTrait.Subtype[Differ, T, Any](
57+
typeInfo = sub.typeInfo,
58+
annotations = sub.annotations,
59+
typeAnnotations = sub.typeAnnotations,
60+
isObject = sub.isObject,
61+
index = sub.index,
62+
callByNeed =
63+
CallByNeed(sub.typeclass.configureRaw(ConfigurePath.current, ConfigureOp.SetIgnored(newIgnored)).unsafeGet.asInstanceOf[Differ[Any]]),
64+
isType = sub.cast.isDefinedAt,
65+
asType = sub.cast.apply,
66+
)
67+
}
68+
val newSealedTrait = new SealedTrait(
69+
typeInfo = ctx.typeInfo,
70+
subtypes = IArray(newSubtypes.toArray: _*),
71+
annotations = ctx.annotations,
72+
typeAnnotations = ctx.typeAnnotations,
73+
)
74+
new SealedTraitDiffer[T](newSealedTrait, isIgnored = newIgnored)
75+
76+
protected def configurePath(
77+
step: String,
78+
nextPath: ConfigurePath,
79+
op: ConfigureOp
80+
): Either[ConfigureError, Differ[T]] =
81+
ctx.subtypes.zipWithIndex.find{ (sub, _) => sub.typeInfo.short == step} match {
82+
case Some((sub, idx)) =>
83+
sub.typeclass
84+
.configureRaw(nextPath, op)
85+
.map { newDiffer =>
86+
val newSubtype = SealedTrait.Subtype[Differ, T, Any](
87+
typeInfo = sub.typeInfo,
88+
annotations = sub.annotations,
89+
typeAnnotations = sub.typeAnnotations,
90+
isObject = sub.isObject,
91+
index = sub.index,
92+
callByNeed = CallByNeed(newDiffer.asInstanceOf[Differ[Any]]),
93+
isType = sub.cast.isDefinedAt,
94+
asType = sub.cast.apply,
95+
)
96+
val newSubtypes = ctx.subtypes.updated(idx, newSubtype)
97+
val newSealedTrait = new SealedTrait(
98+
typeInfo = ctx.typeInfo,
99+
subtypes = newSubtypes,
100+
annotations = ctx.annotations,
101+
typeAnnotations = ctx.typeAnnotations,
102+
)
103+
new SealedTraitDiffer[T](newSealedTrait, isIgnored)
104+
}
105+
case None =>
106+
Left(ConfigureError.UnrecognizedSubType(nextPath, ctx.subtypes.map(_.typeInfo.short).toVector))
107+
}
108+
109+
protected def configurePairBy(path: ConfigurePath, op: ConfigureOp.PairBy[_]): Either[ConfigureError, Differ[T]] =
110+
Left(ConfigureError.InvalidConfigureOp(path, op, "SealedTraitDiffer"))
111+
112+
end SealedTraitDiffer
113+
114+
private def toDiffliciousTypeName(typeInfo: TypeInfo): SomeTypeName = {
115+
TypeName(
116+
long = s"${typeInfo.owner}.${typeInfo.short}",
117+
short = typeInfo.short,
118+
typeArguments = typeInfo.typeParams.map(toDiffliciousTypeName).toList
119+
)
120+
}

0 commit comments

Comments
 (0)