Skip to content

Commit 77b5944

Browse files
authored
Enable capture checking (#20)
For today's 3.8.0 nightly
1 parent 494c651 commit 77b5944

File tree

9 files changed

+212
-130
lines changed

9 files changed

+212
-130
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ jobs:
1919
with:
2020
distribution: temurin
2121
java-version: 17
22+
- uses: sbt/setup-sbt@v1
2223
- name: Test
2324
run: sbt test

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,6 @@ local.properties
204204

205205
### Nix template
206206
# Ignore build outputs from performing a nix-build or `nix build` command
207-
result
208-
result-*
207+
/result
208+
/result-*
209209

build.sbt

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,50 @@
1-
val scala3Version = "3.3.4"
1+
val scala3Version = "3.8.0-RC1-bin-20250818-aaa39c5-NIGHTLY"
2+
resolvers += ("Artifactory" at "https://repo.scala-lang.org/artifactory/maven-nightlies/")
23

3-
inThisBuild(List(
4-
organization := "ch.epfl.lamp",
5-
homepage := Some(url("https://lampepfl.github.io/steps")),
6-
licenses := List("Apache-2.0" -> url(s"https://github.com/lampepfl/steps/blob/v${version.value}/LICENSE")),
7-
developers := List(
8-
Developer("natsukagami", "Natsu Kagami", "natsukagami@gmail.com", url("https://github.com/natsukagami")),
9-
Developer("hamzaremmal", "Hamza Remmal", "hamza@remmal.net", url("https://remmal.net")),
10-
Developer("bracevac", "Oliver Bračevac", "oliver@bracevac.org", url("https://bracevac.org"))
4+
inThisBuild(
5+
List(
6+
organization := "ch.epfl.lamp",
7+
homepage := Some(url("https://lampepfl.github.io/steps")),
8+
licenses := List(
9+
"Apache-2.0" -> url(
10+
s"https://github.com/lampepfl/steps/blob/v${version.value}/LICENSE"
11+
)
12+
),
13+
developers := List(
14+
Developer(
15+
"natsukagami",
16+
"Natsu Kagami",
17+
"natsukagami@gmail.com",
18+
url("https://github.com/natsukagami")
19+
),
20+
Developer(
21+
"hamzaremmal",
22+
"Hamza Remmal",
23+
"hamza@remmal.net",
24+
url("https://remmal.net")
25+
),
26+
Developer(
27+
"bracevac",
28+
"Oliver Bračevac",
29+
"oliver@bracevac.org",
30+
url("https://bracevac.org")
31+
)
32+
)
1133
)
12-
))
13-
34+
)
1435

1536
lazy val root = project
1637
.in(file("."))
1738
.settings(
1839
name := "steps",
1940
scalaVersion := scala3Version,
20-
libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test,
41+
libraryDependencies ++= Seq(
42+
// "org.scala-lang" %% "scala2-library-cc-tasty-experimental" % scala3Version,
43+
"org.scalameta" %% "munit" % "0.7.29" % Test
44+
),
45+
scalacOptions ++= Seq(
46+
// "-Xprint:cc"
47+
),
2148
Compile / doc / scalacOptions ++= Seq(
2249
"-groups"
2350
)

flake.lock

Lines changed: 13 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

project/project/metals.sbt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// format: off
2+
// DO NOT EDIT! This file is auto-generated.
3+
4+
// This plugin enables semantic information to be produced by sbt.
5+
// It also adds support for debugging using the Debug Adapter Protocol
6+
7+
addSbtPlugin("org.scalameta" % "sbt-metals" % "1.6.0")
8+
9+
// This plugin makes sure that the JDI tools are in the sbt classpath.
10+
// JDI tools are used by the debug adapter server.
11+
12+
addSbtPlugin("com.github.sbt" % "sbt-jdi-tools" % "1.2.0")
13+
14+
// format: on

src/main/scala/steps/annotation/toString.scala

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,61 @@ package steps.annotation
33
import scala.annotation.{experimental, MacroAnnotation}
44
import scala.quoted.*
55

6-
7-
/**
8-
* A macro annotation that automatically generates a custom `toString` method for a class.
9-
*
10-
* The `@toString` annotation can be applied to a class, object, or trait. When applied, it overrides the `toString`
11-
* method to include the class name and the values of all fields marked as `ParamAccessor`.
12-
*
13-
* If the class already defines or overrides the `toString` method, the annotation will emit a warning indicating that
14-
* the annotation is not necessary. The existing `toString` method will remain unchanged.
15-
*
16-
* Example usage:
17-
* {{{
18-
* @toString
19-
* class MyClass(val a: Int, val b: String)
20-
*
21-
* val instance = MyClass(1, "hello")
22-
* println(instance.toString) // Output: MyClass(1, hello)
23-
* }}}
24-
*
25-
* The generated `toString` method produces output in the format: `ClassName(field1, field2, ...)`.
26-
*
27-
* @note This annotation requires Scala's `experimental` flag to be enabled, or it can be used within a scope
28-
* marked as experimental (i.e., using `import scala.annotation.experimental`). This is necessary because it
29-
* relies on experimental macro annotation features.
30-
*/
6+
/** A macro annotation that automatically generates a custom `toString` method
7+
* for a class.
8+
*
9+
* The `@toString` annotation can be applied to a class, object, or trait. When
10+
* applied, it overrides the `toString` method to include the class name and
11+
* the values of all fields marked as `ParamAccessor`.
12+
*
13+
* If the class already defines or overrides the `toString` method, the
14+
* annotation will emit a warning indicating that the annotation is not
15+
* necessary. The existing `toString` method will remain unchanged.
16+
*
17+
* Example usage:
18+
* {{{
19+
* @toString
20+
* class MyClass(val a: Int, val b: String)
21+
*
22+
* val instance = MyClass(1, "hello")
23+
* println(instance.toString) // Output: MyClass(1, hello)
24+
* }}}
25+
*
26+
* The generated `toString` method produces output in the format:
27+
* `ClassName(field1, field2, ...)`.
28+
*
29+
* @note
30+
* This annotation requires Scala's `experimental` flag to be enabled, or it
31+
* can be used within a scope marked as experimental (i.e., using `import
32+
* scala.annotation.experimental`). This is necessary because it relies on
33+
* experimental macro annotation features.
34+
*/
3135
@experimental
3236
final class toString extends MacroAnnotation:
3337

34-
/**
35-
* Transforms the annotated class to add a custom `toString` method.
36-
*
37-
* If the class already overrides `toString`, a warning is emitted and no changes are made.
38-
* Otherwise, this annotation adds a new `toString` method that returns a string representation of
39-
* the class name and its fields.
40-
*
41-
* @param tree The abstract syntax tree (AST) of the annotated class, object, or trait.
42-
* @return The transformed class definition, with the generated `toString` method if applicable.
43-
*/
44-
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
38+
/** Transforms the annotated class to add a custom `toString` method.
39+
*
40+
* If the class already overrides `toString`, a warning is emitted and no
41+
* changes are made. Otherwise, this annotation adds a new `toString` method
42+
* that returns a string representation of the class name and its fields.
43+
*
44+
* @param tree
45+
* The abstract syntax tree (AST) of the annotated class, object, or trait.
46+
* @return
47+
* The transformed class definition, with the generated `toString` method
48+
* if applicable.
49+
*/
50+
override def transform(using quotes: Quotes)(
51+
tree: quotes.reflect.Definition,
52+
_companion: Option[quotes.reflect.Definition]
53+
): List[quotes.reflect.Definition] =
4554
import quotes.reflect.*
4655
val toStringSym = Symbol.requiredMethod("java.lang.Object.toString")
4756
tree match
4857
case _: ClassDef if toStringSym.overridingSymbol(tree.symbol).exists =>
49-
report.warning(s"@toString is not necessary since toString is defined in ${tree.symbol}")
58+
report.warning(
59+
s"@toString is not necessary since toString is defined in ${tree.symbol}"
60+
)
5061
List(tree)
5162
case cls: ClassDef if cls.symbol.flags.is(Flags.Trait) =>
5263
report.error(s"@toString is not supported on traits")
@@ -62,31 +73,44 @@ final class toString extends MacroAnnotation:
6273
Select(This(cls), vdef.symbol).asExpr
6374
}
6475

65-
val toStringOverrideSym = Symbol.newMethod(cls, toStringSym.name, toStringSym.info, Flags.Override, Symbol.noSymbol)
76+
val toStringOverrideSym = Symbol.newMethod(
77+
cls,
78+
toStringSym.name,
79+
toStringSym.info,
80+
Flags.Override,
81+
Symbol.noSymbol
82+
)
6683

6784
def toStringOverrideDefBody(argss: List[List[Tree]]): Option[Term] =
6885
given Quotes = toStringOverrideSym.asQuotes
6986
Some(toStringExpr(className, fields).asTerm)
7087

7188
val toStringDef = DefDef(toStringOverrideSym, toStringOverrideDefBody)
72-
List(ClassDef.copy(tree)(className, ctr, parents, self, toStringDef :: body))
89+
List(
90+
ClassDef
91+
.copy(tree)(className, ctr, parents, self, toStringDef :: body)
92+
)
7393
case _ =>
7494
report.errorAndAbort("@toString is only supported on class")
7595
end transform
7696

77-
/**
78-
* Helper method to create the string representation of the class.
79-
*
80-
* Constructs a string in the format: `ClassName(field1, field2, ...)`.
81-
*
82-
* @param className The name of the class.
83-
* @param thisFields The list of expressions representing the class fields.
84-
* @return A quoted expression representing the final string.
85-
*/
86-
private def toStringExpr(className: String, thisFields: List[Expr[Any]])(using Quotes): Expr[String] =
97+
/** Helper method to create the string representation of the class.
98+
*
99+
* Constructs a string in the format: `ClassName(field1, field2, ...)`.
100+
*
101+
* @param className
102+
* The name of the class.
103+
* @param thisFields
104+
* The list of expressions representing the class fields.
105+
* @return
106+
* A quoted expression representing the final string.
107+
*/
108+
private def toStringExpr(className: String, thisFields: List[Expr[Any]])(using
109+
Quotes
110+
): Expr[String] =
87111
val fieldsSeq = Expr.ofSeq(thisFields)
88112
val prefix = Expr(className + "(")
89113
'{ $fieldsSeq.mkString($prefix, ", ", ")") }
90114
end toStringExpr
91115

92-
end toString
116+
end toString

src/main/scala/steps/result/fromStd.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package steps.result
22

3+
import language.experimental.captureChecking
4+
35
import scala.util.{Try, Success, Failure}
46

57
/** Provides extension methods convert Scala API optional value containers into

0 commit comments

Comments
 (0)