Skip to content

Commit f91d4ec

Browse files
committed
test exports with metaprogramming tools
1 parent 353b508 commit f91d4ec

File tree

15 files changed

+256
-7
lines changed

15 files changed

+256
-7
lines changed

compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import core.Contexts._
77
class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) {
88
override def compile(classNames: List[String]): Unit = {
99
val units = classNames.map(new TASTYCompilationUnit(_))
10-
println(s"compile units = $units")
1110
compileUnits(units)
1211
}
1312
}

compiler/src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
152152
}
153153

154154
override def transformOther(tree: Tree)(using Context): Tree = tree match {
155-
case tree: (ImportOrExport[_]) => EmptyTree
155+
case tree: ImportOrExport[_] => EmptyTree
156156
case tree: NamedArg => transformAllDeep(tree.arg)
157157
case tree => if (tree.isType) toTypeTree(tree) else tree
158158
}

compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
140140
tpd.patVars(pat).foreach(markSymbol)
141141
mapOverTree(last)
142142

143-
case _: Import =>
143+
case (_:Import | _:Export) =>
144144
tree
145145

146146
case _ =>
@@ -161,4 +161,4 @@ object TreeMapWithStages {
161161
def freshStagingContext(using Context): Context =
162162
ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int])
163163

164-
}
164+
}

compiler/src/scala/quoted/internal/impl/printers/SourceCode.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ object SourceCode {
109109
val stats1 = stats.collect {
110110
case stat: PackageClause => stat
111111
case stat: Definition if !(stat.symbol.flags.is(Flags.Object) && stat.symbol.flags.is(Flags.Lazy)) => stat
112-
case stat @ Import(_, _) => stat
112+
case stat @ (_:Import | _:Export) => stat
113113
}
114114
name match {
115115
case Ident("<empty>") =>
@@ -220,7 +220,7 @@ object SourceCode {
220220
}
221221
val stats1 = stats.collect {
222222
case stat: Definition if keepDefinition(stat) => stat
223-
case stat @ (Import(_, _) | Export(_, _)) => stat
223+
case stat @ (_:Import | _:Export) => stat
224224
case stat: Term => stat
225225
}
226226

library/src-bootstrapped/scala/quoted/ExprMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ trait ExprMap:
1717
transformTerm(tree, TypeRepr.of[Any])
1818
case tree: Definition =>
1919
transformDefinition(tree)
20-
case tree: Import =>
20+
case tree @ (_:Import | _:Export) =>
2121
tree
2222
}
2323
}

library/src/scala/quoted/QuoteContext.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3498,6 +3498,8 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
34983498
foldTrees(foldTrees(foldTrees(foldTrees(foldTree(x, constr), parents), derived), self), body)
34993499
case Import(expr, _) =>
35003500
foldTree(x, expr)
3501+
case Export(expr, _) =>
3502+
foldTree(x, expr)
35013503
case clause @ PackageClause(pid, stats) =>
35023504
foldTrees(foldTree(x, pid), stats)(using clause.symbol.localContext)
35033505
case Inferred() => x
@@ -3565,6 +3567,8 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
35653567
PackageClause.copy(tree)(transformTerm(tree.pid).asInstanceOf[Ref], transformTrees(tree.stats)(using tree.symbol.localContext))
35663568
case tree: Import =>
35673569
Import.copy(tree)(transformTerm(tree.expr), tree.selectors)
3570+
case tree: Export =>
3571+
Export.copy(tree)(transformTerm(tree.expr), tree.selectors)
35683572
case tree: Statement =>
35693573
transformStatement(tree)
35703574
case tree: TypeTree => transformTypeTree(tree)
@@ -3606,6 +3610,8 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
36063610
ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.derived, tree.self, tree.body)
36073611
case tree: Import =>
36083612
Import.copy(tree)(transformTerm(tree.expr), tree.selectors)
3613+
case tree: Export =>
3614+
Export.copy(tree)(transformTerm(tree.expr), tree.selectors)
36093615
}
36103616
}
36113617

tests/run-macros/exports.check

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
visited exports with ExprMap
2+
visited exports with TreeMap
3+
extracted with TreeAccumulator: '{export Messages.{logMessage => log}}, '{export Messages.{count}}
4+
reflection show:
5+
{
6+
lazy val Observer: Observer = new Observer()
7+
object Observer {
8+
export Messages.{count}
9+
final def count: scala.Int = Messages.count
10+
}
11+
()
12+
}
13+
reflection show extractors:
14+
Inlined(None, Nil, Block(List(ValDef("Observer", TypeIdent("Observer$"), Some(Apply(Select(New(TypeIdent("Observer$")), "<init>"), Nil))), ClassDef("Observer$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Observer")), None)), List(Export(Ident("Messages"), List(SimpleSelector(count))), DefDef("count", Nil, Nil, Inferred(), Some(Select(Ident("Messages"), "count")))))), Literal(Constant.Unit())))
15+
visited exports with splice
16+
visited exports with splice inverted
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait Logger {
2+
def log(a: String): Unit
3+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import scala.quoted._
2+
3+
inline def visitExportsTreeAccumulator[T](inline x: T)(inline f: String => Any): Any = ${ traverseExportsImpl('x, 'f) }
4+
inline def visitExportsTreeMap[T](inline x: T)(inline f: T => Any): Any = ${ visitExportsTreeMapImpl('x, 'f) }
5+
inline def visitExportsExprMap[T](inline x: T)(inline f: T => Any): Any = ${ visitExportsExprMapImpl('x, 'f) }
6+
inline def visitExportsShow[T](inline x: T): Any = ${ visitExportsShowImpl('x) }
7+
inline def visitExportsShowExtract[T](inline x: T): Any = ${ visitExportsShowExtractImpl('x) }
8+
inline def visitExportsSplice(inline l: Logger): Logger = ${ mixinLoggerImpl('l) }
9+
inline def visitExportsSpliceInverse(inline op: Logger => Logger): Logger = ${ mixinLoggerInverseImpl('op) }
10+
11+
private def visitExportsExprMapImpl[T: Type](e: Expr[T], f: Expr[T => Any])(using QuoteContext): Expr[Any] =
12+
'{$f(${IdempotentExprMap.transform(e)})}
13+
14+
private def visitExportsTreeMapImpl[T: Type](e: Expr[T], f: Expr[T => Any])(using QuoteContext): Expr[Any] =
15+
import qctx.reflect._
16+
object m extends TreeMap
17+
'{$f(${m.transformTerm(Term.of(e)).asExprOf})}
18+
19+
private def visitExportsShowImpl[T: Type](e: Expr[T])(using QuoteContext): Expr[Any] =
20+
import qctx.reflect._
21+
'{println(${Expr(Term.of(e).show)})}
22+
23+
private def visitExportsShowExtractImpl[T: Type](e: Expr[T])(using QuoteContext): Expr[Any] =
24+
import qctx.reflect._
25+
'{println(${Expr(Term.of(e).showExtractors)})}
26+
27+
private object IdempotentExprMap extends ExprMap {
28+
29+
def transform[T](e: Expr[T])(using QuoteContext, Type[T]): Expr[T] =
30+
transformChildren(e)
31+
32+
}
33+
34+
private def traverseExportsImpl(e: Expr[Any], f: Expr[String => Any])(using QuoteContext): Expr[Any] = {
35+
import qctx.reflect._
36+
import collection.mutable
37+
38+
object ExportAccumulator extends TreeAccumulator[mutable.Buffer[String]] {
39+
def foldTree(x: mutable.Buffer[String], tree: Tree)(using ctx: Context): mutable.Buffer[String] = tree match {
40+
case tree: Export => foldOverTree(x += s"'{${tree.show}}", tree)
41+
case _ => foldOverTree(x, tree)
42+
}
43+
}
44+
45+
val res =
46+
ExportAccumulator.foldTree(mutable.Buffer.empty, Term.of(e)).mkString(", ")
47+
48+
'{ $f(${Expr(res)}) }
49+
}
50+
51+
private def mixinLoggerImpl(l: Expr[Logger])(using QuoteContext): Expr[Logger] =
52+
'{ new Logger {
53+
private val delegate = $l
54+
export delegate._
55+
}}
56+
57+
private def mixinLoggerInverseImpl(op: Expr[Logger => Logger])(using QuoteContext): Expr[Logger] =
58+
'{ $op(new Logger { def log(a: String): Unit = println(a) }) }

tests/run-macros/exports/Test_3.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
object Messages {
2+
private var logs: Int = 0
3+
def logMessage(a: String): Unit =
4+
logs += 1
5+
println(a)
6+
def count = logs
7+
}
8+
9+
@main def Test: Unit =
10+
assert(Messages.count == 0)
11+
visitExportsExprMap(new Logger { export Messages.{logMessage => log} })(
12+
_.log("visited exports with ExprMap")
13+
)
14+
assert(Messages.count == 1)
15+
visitExportsTreeMap(new Logger { export Messages.{logMessage => log} })(
16+
_.log("visited exports with TreeMap")
17+
)
18+
assert(Messages.count == 2)
19+
visitExportsTreeAccumulator(new Logger { export Messages.{logMessage => log}; export Messages.count })(
20+
exportStrings => println(s"extracted with TreeAccumulator: $exportStrings")
21+
)
22+
println("reflection show:")
23+
visitExportsShow({ object Observer { export Messages.count } })
24+
println("reflection show extractors:")
25+
visitExportsShowExtract({ object Observer { export Messages.count } })
26+
val localLogger = new Logger { def log(a: String): Unit = println(a) }
27+
visitExportsSplice(localLogger).log("visited exports with splice")
28+
visitExportsSpliceInverse(logger => new Logger {
29+
private val delegate = logger
30+
export delegate._
31+
}).log("visited exports with splice inverted")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package exports.example
2+
3+
trait Decoder/*<-exports::example::Decoder#*/[+T/*<-exports::example::Decoder#[T]*/] {
4+
def decode/*<-exports::example::Decoder#decode().*/(a/*<-exports::example::Decoder#decode().(a)*/: Array/*->scala::Array#*/[Byte/*->scala::Byte#*/]): T/*->exports::example::Decoder#[T]*/
5+
}
6+
7+
trait Encoder/*<-exports::example::Encoder#*/[-T/*<-exports::example::Encoder#[T]*/] {
8+
def encode/*<-exports::example::Encoder#encode().*/(t/*<-exports::example::Encoder#encode().(t)*/: T/*->exports::example::Encoder#[T]*/): Array/*->scala::Array#*/[Byte/*->scala::Byte#*/]
9+
}
10+
11+
trait Codec/*<-exports::example::Codec#*/[T/*<-exports::example::Codec#[T]*/](decode/*<-exports::example::Codec#decode.*/: Decoder/*->exports::example::Decoder#*/[T/*->exports::example::Codec#[T]*/], encode/*<-exports::example::Codec#encode.*/: Encoder/*->exports::example::Encoder#*/[T/*->exports::example::Codec#[T]*/])
12+
extends Decoder/*->exports::example::Decoder#*/[T/*->exports::example::Codec#[T]*/] with Encoder/*->exports::example::Encoder#*/[T/*->exports::example::Codec#[T]*/] {
13+
export decode/*->exports::example::Codec#decode.*//*->exports::example::Decoder#decode().*//*->exports::example::Codec#decode().(a)*/./*<-exports::example::Codec#decode().*/_
14+
export encode/*->exports::example::Codec#encode.*//*->exports::example::Encoder#encode().*//*->exports::example::Codec#encode().(t)*/./*<-exports::example::Codec#encode().*/_
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package exports.example
2+
3+
trait Decoder[+T] {
4+
def decode(a: Array[Byte]): T
5+
}
6+
7+
trait Encoder[-T] {
8+
def encode(t: T): Array[Byte]
9+
}
10+
11+
trait Codec[T](decode: Decoder[T], encode: Encoder[T])
12+
extends Decoder[T] with Encoder[T] {
13+
export decode._
14+
export encode._
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package exports
2+
3+
/*<-exports::`exports-package$package`.*/export example.{Decoder/*<-exports::`exports-package$package`.Decoder#*/, Encoder/*<-exports::`exports-package$package`.Encoder#*/, Codec/*<-exports::`exports-package$package`.Codec#*/}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package exports
2+
3+
export example.{Decoder, Encoder, Codec}

tests/semanticdb/metac.expect

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,6 +3022,106 @@ Occurrences:
30223022
[4:18..4:21): Int -> scala/Int#
30233023
[4:26..4:30): Unit -> scala/Unit#
30243024

3025+
expect/exports-example-Codec.scala
3026+
----------------------------------
3027+
3028+
Summary:
3029+
Schema => SemanticDB v4
3030+
Uri => exports-example-Codec.scala
3031+
Text => empty
3032+
Language => Scala
3033+
Symbols => 21 entries
3034+
Occurrences => 39 entries
3035+
3036+
Symbols:
3037+
exports/example/Codec# => trait Codec
3038+
exports/example/Codec#[T] => typeparam T
3039+
exports/example/Codec#`<init>`(). => primary ctor <init>
3040+
exports/example/Codec#`<init>`().(decode) => param decode
3041+
exports/example/Codec#`<init>`().(encode) => param encode
3042+
exports/example/Codec#decode(). => final method decode
3043+
exports/example/Codec#decode().(a) => param a
3044+
exports/example/Codec#decode. => val method decode
3045+
exports/example/Codec#encode(). => final method encode
3046+
exports/example/Codec#encode().(t) => param t
3047+
exports/example/Codec#encode. => val method encode
3048+
exports/example/Decoder# => trait Decoder
3049+
exports/example/Decoder#[T] => covariant typeparam T
3050+
exports/example/Decoder#`<init>`(). => primary ctor <init>
3051+
exports/example/Decoder#decode(). => abstract method decode
3052+
exports/example/Decoder#decode().(a) => param a
3053+
exports/example/Encoder# => trait Encoder
3054+
exports/example/Encoder#[T] => contravariant typeparam T
3055+
exports/example/Encoder#`<init>`(). => primary ctor <init>
3056+
exports/example/Encoder#encode(). => abstract method encode
3057+
exports/example/Encoder#encode().(t) => param t
3058+
3059+
Occurrences:
3060+
[0:8..0:15): exports -> exports/
3061+
[0:16..0:23): example <- exports/example/
3062+
[2:6..2:13): Decoder <- exports/example/Decoder#
3063+
[2:13..2:13): <- exports/example/Decoder#`<init>`().
3064+
[2:15..2:16): T <- exports/example/Decoder#[T]
3065+
[3:6..3:12): decode <- exports/example/Decoder#decode().
3066+
[3:13..3:14): a <- exports/example/Decoder#decode().(a)
3067+
[3:16..3:21): Array -> scala/Array#
3068+
[3:22..3:26): Byte -> scala/Byte#
3069+
[3:30..3:31): T -> exports/example/Decoder#[T]
3070+
[6:6..6:13): Encoder <- exports/example/Encoder#
3071+
[6:13..6:13): <- exports/example/Encoder#`<init>`().
3072+
[6:15..6:16): T <- exports/example/Encoder#[T]
3073+
[7:6..7:12): encode <- exports/example/Encoder#encode().
3074+
[7:13..7:14): t <- exports/example/Encoder#encode().(t)
3075+
[7:16..7:17): T -> exports/example/Encoder#[T]
3076+
[7:20..7:25): Array -> scala/Array#
3077+
[7:26..7:30): Byte -> scala/Byte#
3078+
[10:6..10:11): Codec <- exports/example/Codec#
3079+
[10:11..10:11): <- exports/example/Codec#`<init>`().
3080+
[10:12..10:13): T <- exports/example/Codec#[T]
3081+
[10:15..10:21): decode <- exports/example/Codec#decode.
3082+
[10:23..10:30): Decoder -> exports/example/Decoder#
3083+
[10:31..10:32): T -> exports/example/Codec#[T]
3084+
[10:35..10:41): encode <- exports/example/Codec#encode.
3085+
[10:43..10:50): Encoder -> exports/example/Encoder#
3086+
[10:51..10:52): T -> exports/example/Codec#[T]
3087+
[11:10..11:17): Decoder -> exports/example/Decoder#
3088+
[11:18..11:19): T -> exports/example/Codec#[T]
3089+
[11:26..11:33): Encoder -> exports/example/Encoder#
3090+
[11:34..11:35): T -> exports/example/Codec#[T]
3091+
[12:9..12:15): decode -> exports/example/Codec#decode.
3092+
[12:15..12:15): -> exports/example/Decoder#decode().
3093+
[12:15..12:15): -> exports/example/Codec#decode().(a)
3094+
[12:16..12:16): <- exports/example/Codec#decode().
3095+
[13:9..13:15): encode -> exports/example/Codec#encode.
3096+
[13:15..13:15): -> exports/example/Encoder#encode().
3097+
[13:15..13:15): -> exports/example/Codec#encode().(t)
3098+
[13:16..13:16): <- exports/example/Codec#encode().
3099+
3100+
expect/exports-package.scala
3101+
----------------------------
3102+
3103+
Summary:
3104+
Schema => SemanticDB v4
3105+
Uri => exports-package.scala
3106+
Text => empty
3107+
Language => Scala
3108+
Symbols => 4 entries
3109+
Occurrences => 6 entries
3110+
3111+
Symbols:
3112+
exports/`exports-package$package`. => final package object exports
3113+
exports/`exports-package$package`.Codec# => final type Codec
3114+
exports/`exports-package$package`.Decoder# => final type Decoder
3115+
exports/`exports-package$package`.Encoder# => final type Encoder
3116+
3117+
Occurrences:
3118+
[0:8..0:15): exports <- exports/
3119+
[2:0..2:0): <- exports/`exports-package$package`.
3120+
[2:7..2:14): example -> exports/example/
3121+
[2:16..2:23): Decoder <- exports/`exports-package$package`.Decoder#
3122+
[2:25..2:32): Encoder <- exports/`exports-package$package`.Encoder#
3123+
[2:34..2:39): Codec <- exports/`exports-package$package`.Codec#
3124+
30253125
expect/filename with spaces.scala
30263126
---------------------------------
30273127

0 commit comments

Comments
 (0)