From 8e06c68de970868bebdb2fc0661d7a2f38c2ebe5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Sep 2021 15:25:24 +0200 Subject: [PATCH 1/7] Clean up printing of annotations Always print annotation arguments if there are some, except for Body annotations --- .../dotty/tools/dotc/core/Annotations.scala | 12 +++++-- .../tools/dotc/printing/PlainPrinter.scala | 7 ++-- .../dotty/tools/dotc/printing/Printer.scala | 3 ++ .../tools/dotc/printing/RefinedPrinter.scala | 32 ++++++++++++------- tests/neg/annot-printing.check | 7 ++++ tests/neg/annot-printing.scala | 6 ++++ 6 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 tests/neg/annot-printing.check create mode 100644 tests/neg/annot-printing.scala diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 4245ae3ef0ec..8a7df9302782 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -7,6 +7,8 @@ import StdNames._ import dotty.tools.dotc.ast.tpd import scala.util.Try import util.Spans.Span +import printing.{Showable, Printer} +import printing.Texts.Text object Annotations { @@ -14,7 +16,7 @@ object Annotations { if (tree.symbol.isConstructor) tree.symbol.owner else tree.tpe.typeSymbol - abstract class Annotation { + abstract class Annotation extends Showable { def tree(using Context): Tree def symbol(using Context): Symbol = annotClass(tree) @@ -44,15 +46,18 @@ object Annotations { /** The tree evaluation has finished. */ def isEvaluated: Boolean = true + /** A string representation of the annotation. Overridden in BodyAnnotation. + */ + def toText(printer: Printer): Text = printer.annotText(this) + def ensureCompleted(using Context): Unit = tree def sameAnnotation(that: Annotation)(using Context): Boolean = symbol == that.symbol && tree.sameTree(that.tree) } - case class ConcreteAnnotation(t: Tree) extends Annotation { + case class ConcreteAnnotation(t: Tree) extends Annotation: def tree(using Context): Tree = t - } abstract class LazyAnnotation extends Annotation { protected var mySym: Symbol | (Context ?=> Symbol) @@ -98,6 +103,7 @@ object Annotations { if (tree eq this.tree) this else ConcreteBodyAnnotation(tree) override def arguments(using Context): List[Tree] = Nil override def ensureCompleted(using Context): Unit = () + override def toText(printer: Printer): Text = "@Body" } class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation { diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index c412afaf0487..6dac1e8db03c 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -539,7 +539,10 @@ class PlainPrinter(_ctx: Context) extends Printer { case _ => literalText(String.valueOf(const.value)) } - def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now + /** Usual target for `Annotation#toText`, overridden in RefinedPrinter */ + def annotText(annot: Annotation): Text = s"@${annot.symbol.name}" + + def toText(annot: Annotation): Text = annot.toText(this) def toText(param: LambdaParam): Text = varianceSign(param.paramVariance) @@ -570,7 +573,7 @@ class PlainPrinter(_ctx: Context) extends Printer { Text() nodeName ~ "(" ~ elems ~ tpSuffix ~ ")" ~ (Str(tree.sourcePos.toString) provided printDebug) - }.close // todo: override in refined printer + }.close def toText(pos: SourcePosition): Text = if (!pos.exists) "" diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 642f582035e6..550bdb94af4f 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -119,6 +119,9 @@ abstract class Printer { /** A description of sym's location */ def extendedLocationText(sym: Symbol): Text + /** Textual description of regular annotation in terms of its tree */ + def annotText(annot: Annotation): Text + /** Textual representation of denotation */ def toText(denot: Denotation): Text diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 759ce471772a..126cfa92d669 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -21,7 +21,7 @@ import typer.ProtoTypes._ import Trees._ import TypeApplications._ import Decorators._ -import NameKinds.WildcardParamName +import NameKinds.{WildcardParamName, DefaultGetterName} import util.Chars.isOperatorPart import transform.TypeUtils._ import transform.SymUtils._ @@ -607,7 +607,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tree: Template => toTextTemplate(tree) case Annotated(arg, annot) => - toTextLocal(arg) ~~ annotText(annot) + toTextLocal(arg) ~~ toText(annot) case EmptyTree => "" case TypedSplice(t) => @@ -964,14 +964,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordStr("package ") ~ toTextPackageId(tree.pid) ~ bodyText } + /** Textual representation of an instance creation expression without the leading `new` */ protected def constrText(tree: untpd.Tree): Text = toTextLocal(tree).stripPrefix(keywordStr("new ")) // DD - protected def annotText(tree: untpd.Tree): Text = "@" ~ constrText(tree) // DD - - override def annotsText(sym: Symbol): Text = - Text(sym.annotations.map(ann => - if ann.symbol == defn.BodyAnnot then Str(simpleNameString(ann.symbol)) - else annotText(ann.tree))) + protected def annotText(sym: Symbol, tree: untpd.Tree): Text = + def recur(t: untpd.Tree): Text = t match + case Apply(fn, Nil) => recur(fn) + case Apply(fn, args) => + val explicitArgs = args.filterNot(_.symbol.name.is(DefaultGetterName)) + recur(fn) ~ "(" ~ toTextGlobal(explicitArgs, ", ") ~ ")" + case TypeApply(fn, args) => recur(fn) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" + case _ => s"@${sym.orElse(tree.symbol).name}" + recur(tree) protected def modText(mods: untpd.Modifiers, sym: Symbol, kw: String, isType: Boolean): Text = { // DD val suppressKw = if (enclDefIsClass) mods.isAllOf(LocalParam) else mods.is(Param) @@ -984,12 +988,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (rawFlags.is(Param)) flagMask = flagMask &~ Given &~ Erased val flags = rawFlags & flagMask var flagsText = toTextFlags(sym, flags) - val annotations = - if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree) - else mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol)) - Text(annotations.map(annotText), " ") ~~ flagsText ~~ (Str(kw) provided !suppressKw) + val annotTexts = + if sym.exists then + sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(toText) + else + mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol)).map(annotText(NoSymbol, _)) + Text(annotTexts, " ") ~~ flagsText ~~ (Str(kw) provided !suppressKw) } + override def annotText(annot: Annotation): Text = annotText(annot.symbol, annot.tree) + def optText(name: Name)(encl: Text => Text): Text = if (name.isEmpty) "" else encl(toText(name)) diff --git a/tests/neg/annot-printing.check b/tests/neg/annot-printing.check new file mode 100644 index 000000000000..8c413e37a07e --- /dev/null +++ b/tests/neg/annot-printing.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg/annot-printing.scala:5:46 ----------------------------------------------------- +5 |def x: Int @nowarn @main @Foo @Bar("hello") = "abc" // error + | ^^^^^ + | Found: ("abc" : String) + | Required: Int @nowarn() @main @Foo @Bar("hello") + +longer explanation available when compiling with `-explain` diff --git a/tests/neg/annot-printing.scala b/tests/neg/annot-printing.scala new file mode 100644 index 000000000000..b917422801ac --- /dev/null +++ b/tests/neg/annot-printing.scala @@ -0,0 +1,6 @@ +import scala.annotation.* +class Foo() extends Annotation +class Bar(s: String) extends Annotation + +def x: Int @nowarn @main @Foo @Bar("hello") = "abc" // error + From 00badaa911902e37c3d465394fb08f0c7fa4bef9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Sep 2021 15:42:47 +0200 Subject: [PATCH 2/7] Avoid awkward special cases when type mapping AnnotatedTypes A TypeMap previously mapped the annotation only if it returned a different result for the parent type. There is no good reason for this behavior. We now map always, but allow the mapping operation to be defined in the annotation. The change uncovers a bug illustrated in annotDepMethType.scala, which is fixed in the next commit. --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 10 ++++---- .../dotty/tools/dotc/core/Annotations.scala | 25 ++++++++++++++++++- .../src/dotty/tools/dotc/core/TypeOps.scala | 10 +++++--- .../src/dotty/tools/dotc/core/Types.scala | 12 ++++----- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 7d04f23d9e8d..b53c30e11d48 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -113,11 +113,11 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => 0 } - /** The (last) list of arguments of an application */ - def arguments(tree: Tree): List[Tree] = unsplice(tree) match { - case Apply(_, args) => args - case TypeApply(fn, _) => arguments(fn) - case Block(_, expr) => arguments(expr) + /** All term arguments of an application in a single flattened list */ + def allArguments(tree: Tree): List[Tree] = unsplice(tree) match { + case Apply(fn, args) => allArguments(fn) ::: args + case TypeApply(fn, _) => allArguments(fn) + case Block(_, expr) => allArguments(expr) case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 8a7df9302782..f10efcaa24e8 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -9,6 +9,7 @@ import scala.util.Try import util.Spans.Span import printing.{Showable, Printer} import printing.Texts.Text +import annotation.internal.sharable object Annotations { @@ -28,7 +29,8 @@ object Annotations { def derivedAnnotation(tree: Tree)(using Context): Annotation = if (tree eq this.tree) this else Annotation(tree) - def arguments(using Context): List[Tree] = ast.tpd.arguments(tree) + /** All arguments to this annotation in a single flat list */ + def arguments(using Context): List[Tree] = ast.tpd.allArguments(tree) def argument(i: Int)(using Context): Option[Tree] = { val args = arguments @@ -46,6 +48,25 @@ object Annotations { /** The tree evaluation has finished. */ def isEvaluated: Boolean = true + /** Normally, map type map over all tree nodes of this annotation, but can + * be overridden. Returns EmptyAnnotation if type type map produces a range + * type, since ranges cannot be types of trees. + */ + def mapWith(tm: TypeMap)(using Context) = + val args = arguments + if args.isEmpty then this + else + val findDiff = new TreeAccumulator[Type]: + def apply(x: Type, tree: Tree)(using Context): Type = + if tm.isRange(x) then x + else + val tp1 = tm(tree.tpe) + foldOver(if tp1 =:= tree.tpe then x else tp1, tree) + val diff = findDiff(NoType, args) + if tm.isRange(diff) then EmptyAnnotation + else if diff.exists then derivedAnnotation(tm.mapOver(tree)) + else this + /** A string representation of the annotation. Overridden in BodyAnnotation. */ def toText(printer: Printer): Text = printer.annotText(this) @@ -200,6 +221,8 @@ object Annotations { apply(defn.SourceFileAnnot, Literal(Constant(path))) } + @sharable val EmptyAnnotation = Annotation(EmptyTree) + def ThrowsAnnotation(cls: ClassSymbol)(using Context): Annotation = { val tref = cls.typeRef Annotation(defn.ThrowsAnnot.typeRef.appliedTo(tref), Ident(tref)) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 75a5816c3164..d01ccd3c58c6 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -162,9 +162,13 @@ object TypeOps: // with Nulls (which have no base classes). Under -Yexplicit-nulls, we take // corrective steps, so no widening is wanted. simplify(l, theMap) | simplify(r, theMap) - case AnnotatedType(parent, annot) - if annot.symbol == defn.UncheckedVarianceAnnot && !ctx.mode.is(Mode.Type) && !theMap.isInstanceOf[SimplifyKeepUnchecked] => - simplify(parent, theMap) + case tp @ AnnotatedType(parent, annot) => + val parent1 = simplify(parent, theMap) + if annot.symbol == defn.UncheckedVarianceAnnot + && !ctx.mode.is(Mode.Type) + && !theMap.isInstanceOf[SimplifyKeepUnchecked] + then parent1 + else tp.derivedAnnotatedType(parent1, annot) case _: MatchType => val normed = tp.tryNormalize if (normed.exists) normed else mapOver diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0d01668be2f5..7f8c92dddff8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5380,6 +5380,8 @@ object Types { variance = saved derivedLambdaType(tp)(ptypes1, this(restpe)) + def isRange(tp: Type): Boolean = tp.isInstanceOf[Range] + /** Map this function over given type */ def mapOver(tp: Type): Type = { record(s"TypeMap mapOver ${getClass}") @@ -5423,8 +5425,9 @@ object Types { case tp @ AnnotatedType(underlying, annot) => val underlying1 = this(underlying) - if (underlying1 eq underlying) tp - else derivedAnnotatedType(tp, underlying1, mapOver(annot)) + val annot1 = annot.mapWith(this) + if annot1 eq EmptyAnnotation then underlying1 + else derivedAnnotatedType(tp, underlying1, annot1) case _: ThisType | _: BoundType @@ -5496,9 +5499,6 @@ object Types { else newScopeWith(elems1: _*) } - def mapOver(annot: Annotation): Annotation = - annot.derivedAnnotation(mapOver(annot.tree)) - def mapOver(tree: Tree): Tree = treeTypeMap(tree) /** Can be overridden. By default, only the prefix is mapped. */ @@ -5545,8 +5545,6 @@ object Types { protected def emptyRange = range(defn.NothingType, defn.AnyType) - protected def isRange(tp: Type): Boolean = tp.isInstanceOf[Range] - protected def lower(tp: Type): Type = tp match { case tp: Range => tp.lo case _ => tp From 8434e578d31a89683d984ea74206e67b5c799d84 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Sep 2021 15:59:37 +0200 Subject: [PATCH 3/7] Fix problems handling annotations in dependent function types Fix problems handling function types that are dependent through their result annotations. --- .../src/dotty/tools/dotc/core/Annotations.scala | 13 +++++++++++++ compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++++++- tests/pos/dependent-annot.scala | 7 +++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/pos/dependent-annot.scala diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index f10efcaa24e8..0e7fd31c51c1 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -67,6 +67,19 @@ object Annotations { else if diff.exists then derivedAnnotation(tm.mapOver(tree)) else this + /** Does this annotation refer to a parameter of `tl`? + * Overridden in ConcreteAnnotation + */ + def refersToParamOf(tl: TermLambda)(using Context): Boolean = + val args = arguments + if args.isEmpty then false + else tree.existsSubTree { + case id: Ident => id.tpe match + case TermParamRef(tl1, _) => tl eq tl1 + case _ => false + case _ => false + } + /** A string representation of the annotation. Overridden in BodyAnnotation. */ def toText(printer: Printer): Text = printer.annotText(this) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7f8c92dddff8..6e60652194c2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3604,6 +3604,9 @@ object Types { case tp: AppliedType => tp.fold(status, compute(_, _, theAcc)) case tp: TypeVar if !tp.isInstantiated => combine(status, Provisional) case tp: TermParamRef if tp.binder eq thisLambdaType => TrueDeps + case AnnotatedType(parent, ann) => + if ann.refersToParamOf(thisLambdaType) then TrueDeps + else compute(status, parent, theAcc) case _: ThisType | _: BoundType | NoPrefix => status case _ => (if theAcc != null then theAcc else DepAcc()).foldOver(status, tp) @@ -3656,8 +3659,10 @@ object Types { if (isResultDependent) { val dropDependencies = new ApproximatingTypeMap { def apply(tp: Type) = tp match { - case tp @ TermParamRef(thisLambdaType, _) => + case tp @ TermParamRef(`thisLambdaType`, _) => range(defn.NothingType, atVariance(1)(apply(tp.underlying))) + case AnnotatedType(parent, ann) if ann.refersToParamOf(thisLambdaType) => + mapOver(parent) case _ => mapOver(tp) } } diff --git a/tests/pos/dependent-annot.scala b/tests/pos/dependent-annot.scala new file mode 100644 index 000000000000..28f0f8bd59e6 --- /dev/null +++ b/tests/pos/dependent-annot.scala @@ -0,0 +1,7 @@ +class C +class ann(x: Any*) extends annotation.Annotation + +def f(y: C, z: C) = + def g(): C @ann(y, z) = ??? + val ac: ((x: C) => Array[String @ann(x)]) = ??? + val dc = ac(g()) From d9215985f3c52103ffe270601432a40aa5a92728 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Sep 2021 16:20:01 +0200 Subject: [PATCH 4/7] Fix typos --- compiler/src/dotty/tools/dotc/core/Annotations.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 0e7fd31c51c1..b8d62210ce26 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -48,7 +48,7 @@ object Annotations { /** The tree evaluation has finished. */ def isEvaluated: Boolean = true - /** Normally, map type map over all tree nodes of this annotation, but can + /** Normally, type map over all tree nodes of this annotation, but can * be overridden. Returns EmptyAnnotation if type type map produces a range * type, since ranges cannot be types of trees. */ @@ -67,9 +67,7 @@ object Annotations { else if diff.exists then derivedAnnotation(tm.mapOver(tree)) else this - /** Does this annotation refer to a parameter of `tl`? - * Overridden in ConcreteAnnotation - */ + /** Does this annotation refer to a parameter of `tl`? */ def refersToParamOf(tl: TermLambda)(using Context): Boolean = val args = arguments if args.isEmpty then false From 692cbe5fb6f858f1637033859ac8c61bc60f120b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Sep 2021 14:21:49 +0200 Subject: [PATCH 5/7] Fix printing annotations in trees --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 126cfa92d669..f730ea9c8dac 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -607,7 +607,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tree: Template => toTextTemplate(tree) case Annotated(arg, annot) => - toTextLocal(arg) ~~ toText(annot) + toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot) case EmptyTree => "" case TypedSplice(t) => From 046f52a25d2029d82e2b32c8a4e16a0e310df184 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Sep 2021 17:02:09 +0200 Subject: [PATCH 6/7] Fix printing untyped annotations --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f730ea9c8dac..ef11ec9434ae 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -974,7 +974,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val explicitArgs = args.filterNot(_.symbol.name.is(DefaultGetterName)) recur(fn) ~ "(" ~ toTextGlobal(explicitArgs, ", ") ~ ")" case TypeApply(fn, args) => recur(fn) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" - case _ => s"@${sym.orElse(tree.symbol).name}" + case Select(qual, nme.CONSTRUCTOR) => recur(qual) + case New(tpt) => recur(tpt) + case _ => + val annotSym = sym.orElse(tree.symbol.enclosingClass) + s"@${if annotSym.exists then annotSym.name.toString else t.show}" recur(tree) protected def modText(mods: untpd.Modifiers, sym: Symbol, kw: String, isType: Boolean): Text = { // DD From 00c9adbb5e2574517a90787ca7ede23dc1a88e4b Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 21 Sep 2021 10:15:37 +0200 Subject: [PATCH 7/7] test printing of annotations --- .../tools/dotc/printing/PrintingTest.scala | 23 +++++++++++------ tests/printing/annot-printing.check | 22 ++++++++++++++++ tests/printing/annot-printing.scala | 7 ++++++ tests/printing/dependent-annot.check | 25 +++++++++++++++++++ tests/printing/dependent-annot.scala | 7 ++++++ tests/printing/untyped/annot-printing.check | 9 +++++++ tests/printing/untyped/annot-printing.scala | 7 ++++++ tests/printing/untyped/dependent-annot.check | 13 ++++++++++ tests/printing/untyped/dependent-annot.scala | 7 ++++++ 9 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 tests/printing/annot-printing.check create mode 100644 tests/printing/annot-printing.scala create mode 100644 tests/printing/dependent-annot.check create mode 100644 tests/printing/dependent-annot.scala create mode 100644 tests/printing/untyped/annot-printing.check create mode 100644 tests/printing/untyped/annot-printing.scala create mode 100644 tests/printing/untyped/dependent-annot.check create mode 100644 tests/printing/untyped/dependent-annot.scala diff --git a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala index cd0aee578742..9c260cb75cc8 100644 --- a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala +++ b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala @@ -19,17 +19,18 @@ import scala.io.Source import org.junit.Test class PrintingTest { - val testsDir = "tests/printing" - val options = List("-Xprint:typer", "-color:never", "-classpath", TestConfiguration.basicClasspath) - private def compileFile(path: JPath): Boolean = { + def options(phase: String) = + List(s"-Xprint:$phase", "-color:never", "-classpath", TestConfiguration.basicClasspath) + + private def compileFile(path: JPath, phase: String): Boolean = { val baseFilePath = path.toString.stripSuffix(".scala") val checkFilePath = baseFilePath + ".check" val byteStream = new ByteArrayOutputStream() val reporter = TestReporter.reporter(new PrintStream(byteStream), INFO) try { - Main.process((path.toString::options).toArray, reporter, null) + Main.process((path.toString::options(phase)).toArray, reporter, null) } catch { case e: Throwable => println(s"Compile $path exception:") @@ -40,11 +41,10 @@ class PrintingTest { FileDiff.checkAndDump(path.toString, actualLines.toIndexedSeq, checkFilePath) } - @Test - def printing: Unit = { + def testIn(testsDir: String, phase: String) = val res = Directory(testsDir).list.toList .filter(f => f.extension == "scala") - .map { f => compileFile(f.jpath) } + .map { f => compileFile(f.jpath, phase) } val failed = res.filter(!_) @@ -53,5 +53,12 @@ class PrintingTest { assert(failed.length == 0, msg) println(msg) - } + + end testIn + + @Test + def printing: Unit = testIn("tests/printing", "typer") + + @Test + def untypedPrinting: Unit = testIn("tests/printing/untyped", "parser") } diff --git a/tests/printing/annot-printing.check b/tests/printing/annot-printing.check new file mode 100644 index 000000000000..fc71f5730d78 --- /dev/null +++ b/tests/printing/annot-printing.check @@ -0,0 +1,22 @@ +[[syntax trees at end of typer]] // tests/printing/annot-printing.scala +package { + import scala.annotation.* + class Foo() extends annotation.Annotation() {} + class Bar(s: String) extends annotation.Annotation() { + private[this] val s: String + } + class Xyz(i: Int) extends annotation.Annotation() { + private[this] val i: Int + } + final lazy module val Xyz: Xyz = new Xyz() + final module class Xyz() extends AnyRef() { this: Xyz.type => + def $lessinit$greater$default$1: Int @uncheckedVariance = 23 + } + final lazy module val annot-printing$package: annot-printing$package = + new annot-printing$package() + final module class annot-printing$package() extends Object() { + this: annot-printing$package.type => + def x: Int @nowarn() @main @Xyz() @Foo @Bar("hello") = ??? + } +} + diff --git a/tests/printing/annot-printing.scala b/tests/printing/annot-printing.scala new file mode 100644 index 000000000000..7ef44b0190f3 --- /dev/null +++ b/tests/printing/annot-printing.scala @@ -0,0 +1,7 @@ +import scala.annotation.* + +class Foo() extends Annotation +class Bar(s: String) extends Annotation +class Xyz(i: Int = 23) extends Annotation + +def x: Int @nowarn @main @Xyz() @Foo @Bar("hello") = ??? diff --git a/tests/printing/dependent-annot.check b/tests/printing/dependent-annot.check new file mode 100644 index 000000000000..393d444d5350 --- /dev/null +++ b/tests/printing/dependent-annot.check @@ -0,0 +1,25 @@ +[[syntax trees at end of typer]] // tests/printing/dependent-annot.scala +package { + class C() extends Object() {} + class ann(x: Seq[Any] @Repeated) extends annotation.Annotation() { + private[this] val x: Seq[Any] @Repeated + } + final lazy module val dependent-annot$package: dependent-annot$package = + new dependent-annot$package() + final module class dependent-annot$package() extends Object() { + this: dependent-annot$package.type => + def f(y: C, z: C): Unit = + { + def g(): C @ann([y,z : Any]*) = ??? + val ac: + (C => Array[String]) + { + def apply(x: C): Array[String @ann([x : Any]*)] + } + = ??? + val dc: Array[String] = ac.apply(g()) + () + } + } +} + diff --git a/tests/printing/dependent-annot.scala b/tests/printing/dependent-annot.scala new file mode 100644 index 000000000000..28f0f8bd59e6 --- /dev/null +++ b/tests/printing/dependent-annot.scala @@ -0,0 +1,7 @@ +class C +class ann(x: Any*) extends annotation.Annotation + +def f(y: C, z: C) = + def g(): C @ann(y, z) = ??? + val ac: ((x: C) => Array[String @ann(x)]) = ??? + val dc = ac(g()) diff --git a/tests/printing/untyped/annot-printing.check b/tests/printing/untyped/annot-printing.check new file mode 100644 index 000000000000..b9ff9670cadd --- /dev/null +++ b/tests/printing/untyped/annot-printing.check @@ -0,0 +1,9 @@ +[[syntax trees at end of parser]] // tests/printing/untyped/annot-printing.scala +package { + import scala.annotation.* + class Foo() extends Annotation {} + class Bar(private[this] val s: String) extends Annotation {} + class Xyz(private[this] val i: Int = 23) extends Annotation {} + def x: Int @nowarn @main @Xyz @Foo @Bar("hello") = ??? +} + diff --git a/tests/printing/untyped/annot-printing.scala b/tests/printing/untyped/annot-printing.scala new file mode 100644 index 000000000000..7ef44b0190f3 --- /dev/null +++ b/tests/printing/untyped/annot-printing.scala @@ -0,0 +1,7 @@ +import scala.annotation.* + +class Foo() extends Annotation +class Bar(s: String) extends Annotation +class Xyz(i: Int = 23) extends Annotation + +def x: Int @nowarn @main @Xyz() @Foo @Bar("hello") = ??? diff --git a/tests/printing/untyped/dependent-annot.check b/tests/printing/untyped/dependent-annot.check new file mode 100644 index 000000000000..9322f9286fbf --- /dev/null +++ b/tests/printing/untyped/dependent-annot.check @@ -0,0 +1,13 @@ +[[syntax trees at end of parser]] // tests/printing/untyped/dependent-annot.scala +package { + class C {} + class ann(private[this] val x: Any *) extends annotation.Annotation {} + def f(y: C, z: C) = + { + def g(): C @ann(y, z) = ??? + val ac: ((x: C) => Array[String @ann(x)]) = ??? + val dc = ac(g()) + + } +} + diff --git a/tests/printing/untyped/dependent-annot.scala b/tests/printing/untyped/dependent-annot.scala new file mode 100644 index 000000000000..28f0f8bd59e6 --- /dev/null +++ b/tests/printing/untyped/dependent-annot.scala @@ -0,0 +1,7 @@ +class C +class ann(x: Any*) extends annotation.Annotation + +def f(y: C, z: C) = + def g(): C @ann(y, z) = ??? + val ac: ((x: C) => Array[String @ann(x)]) = ??? + val dc = ac(g())