Skip to content

Commit 863c3c0

Browse files
committed
Add partial support for type parameters
1 parent baace3a commit 863c3c0

File tree

3 files changed

+64
-29
lines changed

3 files changed

+64
-29
lines changed

tests/neg-macros/annot-codegen.check

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,32 @@
44
| ^^^^^^^^^^^^^^^^^^^^^^^^
55
| Replace the underline code by:
66
| data.generated[Bar]()
7+
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:9:6 --------------------------------------------------------------
8+
9 | def withThree(three: Int): Bar // error: body needs to be data.generate[Bar]()
9+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
| Replace the underline code by:
11+
| def withThree(three: scala.Int): Bar = data.generated[Bar]()
712
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:1:80 -------------------------------------------------------------
813
1 |@data class Bar(val one: String, val two: Int, val three: Int, val four: Double): // error: additional methods needed
914
| ^
1015
| @data requires the following additional method(s):
1116
|
12-
| def withThree(three: scala.Int): Bar = data.generated[Bar]()
1317
| def withFour(four: scala.Double): Bar = data.generated[Bar]()
18+
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:12:32 ------------------------------------------------------------
19+
12 | def withOne(one: T): Baz[T] = ??? // error
20+
| ^^^
21+
| Replace the underline code by:
22+
| data.generated[Baz[Baz.this.T]]()
23+
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:13:6 -------------------------------------------------------------
24+
13 | def withTwo(two: Int): Baz[T] // error
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
| Replace the underline code by:
27+
| def withTwo(two: scala.Int): Baz[Baz.this.T] = data.generated[Baz[Baz.this.T]]()
28+
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:14:38 ------------------------------------------------------------
29+
14 | def withThree(three: Int): Baz[T] = data.generated[Baz[T]]() // error // FIXME: should be supported
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^
31+
| constructor Baz in class Baz does not take parameters
32+
-- Error: tests/neg-macros/annot-codegen/Test_2.scala:15:36 ------------------------------------------------------------
33+
15 | def withFour(four: Int): Baz[T] = data.generated[Nothing]() // error // FIXME: wrong error
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
35+
| constructor Baz in class Baz does not take parameters

tests/neg-macros/annot-codegen/Macro_1.scala

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,40 @@ class data extends MacroAnnotation:
1212
val classPatches = ArrayBuffer[String]()
1313

1414
val cls = tree.symbol
15-
val expectedBody = s"data.generated[${cls.name}]()" // FIXME: handle type parameters
15+
val typeParams = cdef.body.map(_.symbol).filter(_.isType)
16+
val clsTpe =
17+
if typeParams.isEmpty then cls.typeRef
18+
else AppliedType(cls.typeRef, typeParams.map(_.typeRef))
19+
clsTpe.asType match
20+
case '[t] =>
21+
val expectedBody = '{ data.generated[t]() }.show
1622

17-
val params = paramNames(cls)
18-
for param <- params do
19-
val withParam = With(param)
20-
val paramType = cls.declaredField(param).info
21-
val existingOpt =
22-
cdef.body.find(stat =>
23-
val paramss = stat.symbol.paramSymss
24-
stat.symbol.name == withParam
25-
&& paramss.size == 1 && paramss(0).size == 1
26-
&& paramss(0)(0).name == param // FIXME: if the parameter name is incorrect, propose rewriting it
27-
&& paramss(0)(0).info == paramType // FIXME: if the parameter type changed, propose rewriting it
28-
)
29-
existingOpt match
30-
case Some(tree: DefDef) =>
31-
tree.rhs match
32-
case Some(rhs) => rhs.asExpr match
33-
case '{data.generated[t]()} =>
34-
// The correct method is already present, nothing to do
35-
case _ =>
36-
report.error(s"Replace the underline code by:\n$expectedBody", rhs.pos)
23+
val params = paramNames(cls)
24+
for param <- params do
25+
val withParam = With(param)
26+
val paramType = cls.declaredField(param).info
27+
val existingOpt =
28+
cdef.body.find(stat =>
29+
val paramss = stat.symbol.paramSymss
30+
stat.symbol.name == withParam
31+
&& paramss.size == 1 && paramss(0).size == 1
32+
&& paramss(0)(0).name == param // FIXME: if the parameter name is incorrect, propose rewriting it
33+
&& paramss(0)(0).info == paramType // FIXME: if the parameter type changed, propose rewriting it
34+
)
35+
existingOpt match
36+
case Some(tree: DefDef) =>
37+
tree.rhs match
38+
case Some(rhs) => rhs.asExpr match
39+
case '{data.generated[`t`]()} =>
40+
// The correct method is already present, nothing to do
41+
case _ =>
42+
report.error(s"Replace the underline code by:\n$expectedBody", rhs.pos)
43+
case _ =>
44+
report.error(s"Replace the underline code by:\n${tree.show} = $expectedBody", tree.pos)
3745
case _ =>
38-
report.error(s"Replace the underline code by:\n${tree.show} = $expectedBody", tree.pos)
39-
case _ =>
40-
// The method is not present
41-
classPatches +=
42-
s"def $withParam($param: ${paramType.show}): ${cls.name} = $expectedBody"
46+
// The method is not present
47+
classPatches +=
48+
s"def $withParam($param: ${paramType.show}): ${Type.show[t]} = $expectedBody"
4349

4450

4551
val ctr = cdef.constructor
@@ -53,6 +59,7 @@ class data extends MacroAnnotation:
5359
List(tree)
5460

5561
object data:
62+
// TODO: Is T necessary? Could this be transparent?
5663
inline def generated[T](): T = ${generatedImpl[T]}
5764
private def generatedImpl[T: Type](using Quotes): Expr[T] =
5865
import quotes.reflect.*
@@ -62,15 +69,15 @@ object data:
6269
meth.name match
6370
case With(paramName) =>
6471
val localParam = meth.paramSymss(0)(0)
65-
val body =
72+
val body = // FIXME handle type parameters
6673
Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor),
6774
params.map(p => if p == paramName then Ref(localParam) else Ref(cls.declaredField(p))))
6875
body.asExprOf[T]
6976
case _ =>
7077
report.errorAndAbort("@data.generated used in invalid context")
7178

7279
def paramNames(using Quotes)(cls: quotes.reflect.Symbol): List[String] =
73-
cls.primaryConstructor.paramSymss.head.map(_.name)
80+
cls.primaryConstructor.paramSymss.dropWhile(_.headOption.exists(_.isType)).head.map(_.name)
7481

7582
private object With:
7683
def apply(paramName: String): String =

tests/neg-macros/annot-codegen/Test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@
77
new Bar("", two, 1, 1.0) // error: body needs to be replaced
88

99
def withThree(three: Int): Bar // error: body needs to be data.generate[Bar]()
10+
11+
@data class Baz[T](val one: T, val two: Int, val three: Int, val four: Int):
12+
def withOne(one: T): Baz[T] = ??? // error
13+
def withTwo(two: Int): Baz[T] // error
14+
def withThree(three: Int): Baz[T] = data.generated[Baz[T]]() // error // FIXME: should be supported
15+
def withFour(four: Int): Baz[T] = data.generated[Nothing]() // error // FIXME: wrong error

0 commit comments

Comments
 (0)