Description
Problem
Let's consider the following example:
class MyAnnotation(x: Int) extends scala.annotation.StaticAnnotation
def Test =
val x =
val y = 1
"hello": String @MyAnnotation(y)
And fully print types of identifiers in RefinedPrinter
:
diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index ecc1250cbe..7f52a85021 100644
--- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -882,10 +882,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (ctx.settings.XprintTypes.value && tree.hasType) {
// add type to term nodes; replace type nodes with their types unless -Yprint-pos is also set.
- val tp1 = tree.typeOpt match {
- case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying
- case tp => tp
- }
+ val tp1 = tree.typeOpt
val tp2 = {
val tp = tp1.tryNormalize
if (tp != NoType) tp else tp1
Then we see that y.type
(a.k.a. (y: Int)
) leaks into the outer scope:
sbt:scala3> scalac -Xprint:typer -Xprint-types -Ycheck:all tests/pos/annot-avoid.scala
...
def Test: Unit =
<
{
val x: String @MyAnnotation(<y:(y : Int)>) =
<
{
val y: Int = <1:(1 : Int)>
<<"hello":("hello" : String)> :
String @MyAnnotation(<y:(y : Int)>):
String @MyAnnotation(<y:(y : Int)>)>
}
:String @MyAnnotation(<y:(y : Int)>)>
val x2: String @MyAnnotation(<y:(y : Int)>) =
<x:(x : String @MyAnnotation(<y:(y : Int)>))>
<():Unit>
}
:Unit>
...
Cause
The root cause is that escapingRefs
uses NamedPartsAccumulator
to collect references to local symbols, which does not traverse annotations (see TypeAccumulator.applyToAnnot
).
-Ycheck:all
does not detect the leaked symbols because the tree checker also doesn't recurse into annotated types argument trees.
Solution
We might override applyToAnnot
in NamedPartsAccumulator
to traverse the annotation tree, but I am not sure how much it would impact other parts that use NamedPartsAccumulator
. Also, TypeAccumulator.applyToAnnot
was introduced specifically to avoid traversing annotated types to improve performance in 691bae2.
This is an other issue showing that using trees as annotated types argument is fragile, and might require to be changed in the future.