diff options
Diffstat (limited to 'src/dotty/tools/dotc')
20 files changed, 906 insertions, 567 deletions
diff --git a/src/dotty/tools/dotc/core/Constants.scala b/src/dotty/tools/dotc/core/Constants.scala index cdfb62a18..b2949aae7 100644 --- a/src/dotty/tools/dotc/core/Constants.scala +++ b/src/dotty/tools/dotc/core/Constants.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc package core import Types._, Symbols._, Contexts._ +import printing.Printer object Constants { @@ -189,7 +190,7 @@ object Constants { def stringValue: String = value.toString - def toText(implicit ctx: Context) = ctx.toText(this) + def toText(printer: Printer) = printer.toText(this) def typeValue: Type = value.asInstanceOf[Type] def symbolValue: Symbol = value.asInstanceOf[Symbol] diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index e6bd5a843..d29f8834d 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -8,13 +8,14 @@ import Names._ import Phases._ import Types._ import Symbols._ -import TypeComparers._, printing.Printers._, NameOps._, SymDenotations._, util.Positions._ +import TypeComparers._, NameOps._, SymDenotations._, util.Positions._ import TypedTrees.tpd._, util.FreshNameCreator import config.Settings._ import config.ScalaSettings import reporting._ import collection.mutable import collection.immutable.BitSet +import printing._ import config.{Settings, Platform, JavaPlatform} import language.implicitConversions @@ -43,7 +44,7 @@ object Contexts { with Substituters with TypeOps with Phases - with printing.Printers + with Printers with Symbols with SymDenotations with Reporting @@ -378,7 +379,7 @@ object Contexts { object Context { /** Implicit conversion that injects all printer operations into a context */ - implicit def toPrinter(ctx: Context) = ctx.printer(ctx) + implicit def toPrinter(ctx: Context) = ctx.printer /** implicit conversion that injects all ContextBase members into a context */ implicit def toBase(ctx: Context): ContextBase = ctx.base diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 2f91aa59a..5e2da43e5 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -3,7 +3,7 @@ package core import annotation.tailrec import Symbols._ -import Contexts._, Names._, Phases._, printing.Texts._ +import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer /** This object provides useful implicit decorators for types defined elsewhere */ object Decorators { @@ -14,6 +14,7 @@ object Decorators { def toTermName: TermName = termName(s) def toEncodedTypeName = encodedTypeName(s) def toEncodedTermName = encodedTermName(s) + def toText(printer: Printer): Text = Str(s) } /** Implements a findSymbol method on iterators of Symbols that diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index e30e230b1..97b25be00 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -10,6 +10,7 @@ import Symbols.NoSymbol import Symbols._ import Types._, Periods._, Flags._, Transformers._ import printing.Texts._ +import printing.Printer import io.AbstractFile import Decorators.SymbolIteratorDecorator @@ -288,7 +289,7 @@ object Denotations { } } - def toText(implicit ctx: Context): Text = ctx.toText(this) + def toText(printer: Printer): Text = printer.toText(this) } /** An overloaded denotation consisting of the alternatives of both given denotations. diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 726b13af9..f22c2a0f2 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -362,7 +362,7 @@ object Flags { /** Flags representing source modifiers */ final val ModifierFlags = commonFlags(Private, Protected, Abstract, Final, - Sealed, Case, Implicit, AbsOverride, Lazy) + Sealed, Case, Implicit, Override, AbsOverride, Lazy) /** Flags representing access rights */ final val AccessFlags = Private | Protected | Local @@ -434,6 +434,9 @@ object Flags { /** Labeled private[this] */ final val PrivateLocal = allOf(Private, Local) + /** A local parameter */ + final val ParamAndLocal = allOf(Param, Local) + /** Labeled protected[this] */ final val ProtectedLocal = allOf(Protected, Local) diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index c7fedbc4c..e5535cff3 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -3,6 +3,8 @@ package core import scala.io.Codec import util.NameTransformer +import printing.{Showable, Texts, Printer} +import Texts.Text import Decorators._ import Contexts.Context import collection.IndexedSeqOptimized @@ -17,7 +19,7 @@ object Names { /** A common class for things that can be turned into names. * Instances are both names and strings, the latter via a decorator. */ - trait PreName extends Any { + trait PreName extends Any with Showable { def toTypeName: TypeName def toTermName: TermName } @@ -90,7 +92,7 @@ object Names { override def toString = if (length == 0) "" else new String(chrs, start, length) - def show(implicit ctx: Context): String = ctx.nameString(this) + def toText(printer: Printer): Text = printer.toText(this) /** Write to UTF8 representation of this name to given character array. * Start copying to index `to`. Return index of next free byte in array. diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 426d5a51a..82ec0a30a 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -13,6 +13,7 @@ import Decorators._ import Contexts._ import Denotations._ import printing.Texts._ +import printing.Printer import SymDenotations.NoDenotation object Scopes { @@ -108,7 +109,7 @@ object Scopes { syms } - final def toText(implicit ctx: Context): Text = ctx.toText(this) + final def toText(printer: Printer): Text = printer.toText(this) } /** A subclass of Scope that defines methods for entering and diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 7d9fa9d44..b6efdcd36 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -11,6 +11,7 @@ import Decorators._ import Symbols._ import Contexts._ import SymDenotations._, printing.Texts._ +import printing.Printer import Types._, Annotations._, util.Positions._, StdNames._, Trees._, NameOps._ import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable @@ -382,7 +383,7 @@ object Symbols { if (lastDenot == null) s"Naked$prefixString#$id" else lastDenot.toString +"#"+id // !!! DEBUG - def toText(implicit ctx: Context): Text = ctx.toText(this) + def toText(printer: Printer): Text = printer.toText(this) def showLocated(implicit ctx: Context): String = ctx.locatedText(this).show def showDcl(implicit ctx: Context): Text = ctx.dclText(this).show diff --git a/src/dotty/tools/dotc/core/Trees.scala b/src/dotty/tools/dotc/core/Trees.scala index f99e72196..9ac738929 100644 --- a/src/dotty/tools/dotc/core/Trees.scala +++ b/src/dotty/tools/dotc/core/Trees.scala @@ -8,6 +8,7 @@ import language.higherKinds import collection.mutable import collection.mutable.ArrayBuffer import parsing.Tokens.Token +import printing.Printer import util.Stats object Trees { @@ -212,7 +213,7 @@ object Trees { def orElse(that: => Tree[T]): Tree[T] = if (this eq theEmptyTree) that else this - override def toText(implicit ctx: Context) = ctx.toText(this) + override def toText(printer: Printer) = printer.toText(this) override def hashCode(): Int = System.identityHashCode(this) override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] @@ -429,7 +430,7 @@ object Trees { } /** try block catch handler finally finalizer */ - case class Try[T >: Untyped](block: Tree[T], handler: Tree[T], finalizer: Tree[T]) + case class Try[T >: Untyped](expr: Tree[T], handler: Tree[T], finalizer: Tree[T]) extends TermTree[T] { type ThisTree[T >: Untyped] = Try[T] } @@ -531,11 +532,6 @@ object Trees { type ThisTree[T >: Untyped] = DefDef[T] } - class ImplicitDefDef[T >: Untyped](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T]) extends DefDef[T](mods, name, tparams, vparamss, tpt, rhs) { - override def copy[T >: Untyped](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T]) = - new ImplicitDefDef[T](mods, name, tparams, vparamss, tpt, rhs) - } - /** mods type name = rhs or * mods type name >: lo <: hi, if rhs = TypeBoundsTree(lo, hi) */ @@ -769,9 +765,9 @@ object Trees { case tree: Return[_] if (expr eq tree.expr) && (from eq tree.from) => tree case _ => Return(expr, from).copyAttr(tree) } - def derivedTry(block: Tree[T], handler: Tree[T], finalizer: Tree[T]): Try[T] = tree match { - case tree: Try[_] if (block eq tree.block) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree - case _ => Try(block, handler, finalizer).copyAttr(tree) + def derivedTry(expr: Tree[T], handler: Tree[T], finalizer: Tree[T]): Try[T] = tree match { + case tree: Try[_] if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree + case _ => Try(expr, handler, finalizer).copyAttr(tree) } def derivedThrow(expr: Tree[T]): Throw[T] = tree match { case tree: Throw[_] if (expr eq tree.expr) => tree diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 46b718968..ea3c9ca59 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -17,6 +17,7 @@ import Denotations._ import Periods._ import TypedTrees.tpd._, TypedTrees.TreeMapper, printing.Texts._ import transform.Erasure +import printing.Printer import scala.util.hashing.{ MurmurHash3 => hashing } import collection.mutable @@ -735,7 +736,7 @@ object Types { */ def signature(implicit ctx: Context): Signature = NotAMethod - def toText(implicit ctx: Context): Text = ctx.toText(this, printing.Printers.GlobalPrec) + def toText(printer: Printer): Text = printer.toText(this) // ----- hashing ------------------------------------------------------ diff --git a/src/dotty/tools/dotc/core/UntypedTrees.scala b/src/dotty/tools/dotc/core/UntypedTrees.scala index 6fa830a45..80aeba77e 100644 --- a/src/dotty/tools/dotc/core/UntypedTrees.scala +++ b/src/dotty/tools/dotc/core/UntypedTrees.scala @@ -23,7 +23,7 @@ object UntypedTrees { /** (vparams) => body */ case class SymbolLit(str: String) extends Tree - case class InterpolatedString(id: TermName, stringParts: List[Tree], elems: List[Tree]) extends Tree + case class InterpolatedString(id: TermName, strings: List[Literal], elems: List[Tree]) extends Tree case class Function(args: List[Tree], body: Tree) extends Tree case class InfixOp(left: Tree, op: Name, right: Tree) extends Tree case class PostfixOp(tree: Tree, op: Name) extends Tree diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 1c9ce4b8a..5083ccf52 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -11,6 +11,7 @@ import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, Name import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ import util.Positions._, TypedTrees.tpd._, TypedTrees.TreeOps import printing.Texts._ +import printing.Printer import io.AbstractFile import scala.reflect.internal.pickling.PickleFormat._ import Decorators._ @@ -24,8 +25,8 @@ object UnPickler { class BadSignature(msg: String) extends RuntimeException(msg) case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType { - override def toText(implicit ctx: Context): Text = - "[" ~ ctx.dclsText(tparams, ", ") ~ "]" ~ tpe.show + override def fallbackToText(printer: Printer): Text = + "[" ~ printer.dclsText(tparams, ", ") ~ "]" ~ printer.toText(tpe) } /** Temporary type for classinfos, will be decomposed on completion of the class */ diff --git a/src/dotty/tools/dotc/parsing/package.scala b/src/dotty/tools/dotc/parsing/package.scala index b829bd313..0f64f9e1f 100644 --- a/src/dotty/tools/dotc/parsing/package.scala +++ b/src/dotty/tools/dotc/parsing/package.scala @@ -27,6 +27,7 @@ package object parsing { } def minPrec = 0 - def maxPrec = 10 + def minInfixPrec = 1 + def maxPrec = 11 }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala new file mode 100644 index 000000000..274f78bee --- /dev/null +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -0,0 +1,356 @@ +package dotty.tools.dotc +package printing + +import core._ +import Texts._, Trees._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._ +import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation +import StdNames.nme +import UntypedTrees.untpd +import java.lang.Integer.toOctalString +import scala.annotation.switch + +class PlainPrinter(_ctx: Context) extends Printer { + protected[this] implicit val ctx = _ctx + + protected def maxToTextRecursions = 100 + + protected final def controlled(op: => Text): Text = + if (ctx.toTextRecursions < maxToTextRecursions) + try { + ctx.toTextRecursions += 1 + op + } finally { + ctx.toTextRecursions -= 1 + } + else { + recursionLimitExceeeded() + "..." + } + + protected def recursionLimitExceeeded() = { + ctx.warning("Exceeded recursion depth attempting to print type.") + (new Throwable).printStackTrace + } + + /** Render elements alternating with `sep` string */ + protected def toText(elems: Traversable[Showable], sep: String) = + Text(elems map (_ toText this), sep) + + /** Render element within highest precedence */ + protected def toTextLocal(elem: Showable): Text = + atPrec(DotPrec) { elem.toText(this) } + + /** Render element within lowest precedence */ + protected def toTextGlobal(elem: Showable): Text = + atPrec(GlobalPrec) { elem.toText(this) } + + protected def toTextLocal(elems: Traversable[Showable], sep: String) = + atPrec(DotPrec) { toText(elems, sep) } + + protected def toTextGlobal(elems: Traversable[Showable], sep: String) = + atPrec(GlobalPrec) { toText(elems, sep) } + + /** If the name of the symbol's owner should be used when you care about + * seeing an interesting name: in such cases this symbol is e.g. a method + * parameter with a synthetic name, a constructor named "this", an object + * "package", etc. The kind string, if non-empty, will be phrased relative + * to the name of the owner. + */ + protected def hasMeaninglessName(sym: Symbol) = ( + (sym is Param) && sym.owner.isSetter // x$1 + || sym.isClassConstructor // this + || (sym.name == nme.PACKAGE) // package + ) + + def nameString(name: Name): String = name.toString + { + if (ctx.settings.debugNames.value) + if (name.isLocalName) "/L" + else if (name.isTypeName) "/T" + else "/V" + else "" + } + + def toText(name: Name): Text = Str(nameString(name)) + + /** String representation of a name used in a refinement + * In refined printing this undoes type parameter expansion + */ + protected def refinementNameString(tp: RefinedType) = nameString(tp.refinedName) + + /** String representation of a refinement */ + protected def toTextRefinement(rt: RefinedType) = + (refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close + + /** The longest sequence of refinement types, starting at given type + * and following parents. + */ + private def refinementChain(tp: Type): List[Type] = + tp :: (tp match { + case RefinedType(parent, _) => refinementChain(parent) + case _ => Nil + }) + + def toText(tp: Type): Text = controlled { + tp match { + case tp: TypeType => + toTextRHS(tp) + case tp: SingletonType => + val pre = toTextPrefix(tp) + if (pre.lastLine.endsWith(".")) pre ~ "type" + else fullNameString(tp.typeSymbol.skipPackageObject) ~ ".type" + case TypeRef(pre, name) => + toTextPrefix(pre) ~ nameString(tp.typeSymbol) + case tp: RefinedType => + // return tp.toString // !!! DEBUG + val parent :: (refined: List[RefinedType]) = + refinementChain(tp).reverse + toTextLocal(parent) ~ "{" ~ Text(refined map toTextRefinement, "; ").close ~ "}" + case AndType(tp1, tp2) => + changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } + case OrType(tp1, tp2) => + changePrec(OrPrec) { toText(tp1) ~ " | " ~ toText(tp2) } + case ErrorType => + "<error>" + case WildcardType => + "?" + case NoType => + "<notype>" + case NoPrefix => + "<noprefix>" + case tp: MethodType => + def paramText(name: TermName, tp: Type) = toText(name) ~ ": " ~ toText(tp) + changePrec(GlobalPrec) { + (if (tp.isImplicit) "(implicit " else "(") ~ + Text((tp.paramNames, tp.paramTypes).zipped map paramText, ", ") ~ + ")" ~ toText(tp.resultType) + } + case tp: ExprType => + changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) } + case tp: PolyType => + def paramText(name: TypeName, bounds: TypeBounds) = toText(polyParamName(name)) ~ ": " ~ toText(bounds) + changePrec(GlobalPrec) { + "[" ~ + Text((tp.paramNames, tp.paramBounds).zipped map paramText, ", ") ~ + "]" ~ toText(tp.resultType) + } + case PolyParam(pt, n) => + toText(polyParamName(pt.paramNames(n))) + case AnnotatedType(annot, tpe) => + toTextLocal(tpe) ~ " " ~ toText(annot) + case _ => + tp.fallbackToText(this) + } + }.close + + protected def polyParamName(name: TypeName): TypeName = name + + /** The name of the symbol without a unique id. Under refined printing, + * the decoded original name. + */ + protected def simpleNameString(sym: Symbol): String = nameString(sym.name) + + /** The unique id of symbol, after a # */ + protected def idString(sym: Symbol): String = + if (ctx.settings.uniqid.value) "#" + sym.id else "" + + def nameString(sym: Symbol): String = simpleNameString(sym) + idString(sym) + + def fullNameString(sym: Symbol): String = + if (sym.isRoot || sym == NoSymbol || sym.owner.isEffectiveRoot) + nameString(sym) + else + fullNameString(sym.effectiveOwner.enclosingClass) + "." + nameString(sym) + + protected def objectPrefix = "object " + protected def packagePrefix = "package " + + protected def trimPrefix(text: Text) = + text.stripPrefix(objectPrefix).stripPrefix(packagePrefix) + + /** The string representation of this type used as a prefix */ + protected def toTextPrefix(tp: Type): Text = controlled { + tp match { + case tp @ TermRef(pre, name) => + toTextPrefix(pre) ~ nameString(tp.symbol) ~ "." + case ThisType(cls) => + nameString(cls) + ".this." + case SuperType(thistpe, _) => + toTextPrefix(thistpe).map(_.replaceAll("""\bthis\.$""", "super.")) + case tp @ ConstantType(value) => + toTextLocal(tp.underlying) ~ "(" ~ toText(value) ~ ")." + case MethodParam(mt, idx) => + nameString(mt.paramNames(idx)) + "." + case RefinedThis(_) => + "this." + case NoPrefix => + "" + case _ => + trimPrefix(toTextLocal(tp)) ~ "#" + } + } + + protected def isOmittablePrefix(sym: Symbol) = + (defn.UnqualifiedOwners contains sym) || isEmptyPrefix(sym) + + protected def isEmptyPrefix(sym: Symbol) = + sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName + + /** String representation of a definition's type following its name */ + protected def toTextRHS(tp: Type): Text = controlled { + tp match { + case TypeBounds(lo, hi) => + if (lo eq hi) + " = " ~ toText(lo) + else + (if (lo == defn.NothingType) Text() else " >: " ~ toText(lo)) ~ + (if (hi == defn.AnyType) Text() else " <: " ~ toText(hi)) + case ClassInfo(pre, cls, cparents, decls, optSelfType) => + val preText = toTextLocal(pre) + val (tparams, otherDecls) = decls.toList partition treatAsTypeParam + val tparamsText = + if (tparams.isEmpty) Text() else ("[" ~ dclsText(tparams) ~ "]").close + val selfText = + if (optSelfType.exists) + "this: " ~ atPrec(InfixPrec) { toText(optSelfType) } ~ " =>" + else Text() + val parentsText = Text(cparents.map(p => + toTextLocal(reconstituteParent(cls, p))), " with ") + val trueDecls = otherDecls.filterNot(treatAsTypeArg) + val declsText = if (trueDecls.isEmpty) Text() else dclsText(trueDecls) + tparamsText ~ " extends " ~ parentsText ~ "{" ~ selfText ~ declsText ~ + "} at " ~ preText + case _ => + ": " ~ toTextGlobal(tp) + } + } + + protected def treatAsTypeParam(sym: Symbol): Boolean = false + protected def treatAsTypeArg(sym: Symbol): Boolean = false + protected def reconstituteParent(cls: ClassSymbol, parent: Type): Type = parent + + /** String representation of symbol's kind. */ + def kindString(sym: Symbol): String = { + val flags = sym.flagsUNSAFE + if (flags is PackageClass) "package class" + else if (flags is PackageVal) "package" + else if (sym.isPackageObject) + if (sym.isClass) "package object class" + else "package object" + else if (sym.isAnonymousClass) "anonymous class" + else if (flags is ModuleClass) "module class" + else if (flags is ModuleVal) "module" + else if (flags is ImplClass) "implementation class" + else if (flags is Trait) "trait" + else if (sym.isClass) "class" + else if (sym.isType) "type" + else if (sym.isGetter) "getter" + else if (sym.isSetter) "setter" + else if (flags is Lazy) "lazy value" + else if (flags is Mutable) "variable" + else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" + else if (sym.isClassConstructor) "constructor" + else if (sym.isSourceMethod) "method" + else if (sym.isTerm) "value" + else "" + } + + /** String representation of symbol's definition key word */ + protected def keyString(sym: Symbol): String = { + val flags = sym.flagsUNSAFE + if (flags is JavaInterface) "interface" + else if ((flags is Trait) && !(flags is ImplClass)) "trait" + else if (sym.isClass) "class" + else if (sym.isType) "type" + else if (flags is Mutable) "var" + else if (flags is Package) "package" + else if (flags is Module) "object" + else if (sym.isSourceMethod) "def" + else if (sym.isTerm && (!(flags is Param))) "val" + else "" + } + + /** String representation of symbol's flags */ + protected def toTextFlags(sym: Symbol): Text = + Text(sym.flags.flagStrings map stringToText, " ") + + /** String representation of symbol's variance or "" if not applicable */ + protected def varianceString(sym: Symbol): String = sym.variance match { + case -1 => "-" + case 1 => "+" + case _ => "" + } + + def dclText(sym: Symbol): Text = + (toTextFlags(sym) ~~ keyString(sym) ~~ + (varianceString(sym) ~ nameString(sym)) ~ toTextRHS(sym.info)).close + + def toText(sym: Symbol): Text = + (kindString(sym) ~~ { + if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) + else nameString(sym) + }).close + + def locationText(sym: Symbol): Text = { + val owns = sym.effectiveOwner + if (owns.isClass && !isEmptyPrefix(owns)) " in " ~ toText(owns) else Text() + } + + def locatedText(sym: Symbol): Text = + (toText(sym) ~ locationText(sym)).close + + def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D" + + @switch private def escapedChar(ch: Char): String = ch match { + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '"' => "\\\"" + case '\'' => "\\\'" + case '\\' => "\\\\" + case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) + } + + def toText(const: Constant): Text = const.tag match { + case StringTag => "\"" + escapedString(const.value.toString) + "\"" + case ClazzTag => "classOf[" ~ toText(const.tpe) ~ "]" + case CharTag => s"'${escapedChar(const.charValue)}'" + case LongTag => const.longValue.toString + "L" + case EnumTag => const.symbolValue.name.toString + case _ => String.valueOf(const.value) + } + + def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now + + protected def escapedString(str: String): String = str flatMap escapedChar + + def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep) + + def toText(sc: Scope): Text = + ("Scope{" ~ dclsText(sc.toList) ~ "}").close + + def toText[T >: Untyped](tree: Tree[T]): Text = { + tree match { + case node: Positioned => + def toTextElem(elem: Any): Text = elem match { + case elem: Showable => elem.toText(this) + case elem: List[_] => "List(" ~ Text(elem map toTextElem, ",") ~ ")" + case elem => elem.toString + } + val nodeName = node.productPrefix + val elems = + Text(node.productIterator.map(toTextElem).toList, ", ") + val tpSuffix = + if (ctx.settings.printtypes.value && tree.hasType) + " | " ~ toText(tree.tpe.asInstanceOf[Type]) + else + Text() + + nodeName ~ "(" ~ elems ~ tpSuffix ~ ")" ~ node.pos.toString + case _ => + tree.fallbackToText(this) + } + }.close // todo: override in refined printer +} + diff --git a/src/dotty/tools/dotc/printing/Printer.scala b/src/dotty/tools/dotc/printing/Printer.scala new file mode 100644 index 000000000..9ba3141f8 --- /dev/null +++ b/src/dotty/tools/dotc/printing/Printer.scala @@ -0,0 +1,91 @@ +package dotty.tools.dotc +package printing + +import core._ +import Texts._, Trees._ +import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant, + Names.Name, Denotations.Denotation, Annotations.Annotation + +/** The base class of all printers + */ +abstract class Printer { + + private[this] var prec: Precedence = GlobalPrec + + /** The current precedence level */ + def currentPrecedence = prec + + /** Generate text using `op`, assuming a given precedence level `prec`. */ + def atPrec(prec: Precedence)(op: => Text): Text = { + val outerPrec = this.prec + this.prec = prec + try op + finally this.prec = outerPrec + } + + /** Generate text using `op`, assuming a given precedence level `prec`. + * If new level `prec` is lower than previous level, put text in parentheses. + */ + def changePrec(prec: Precedence)(op: => Text): Text = + if (prec < this.prec) atPrec(prec) ("(" ~ op ~ ")") else atPrec(prec)(op) + + /** The name, possibley with with namespace suffix if debugNames is set: + * /L for local names, /V for other term names, /T for type names + */ + def nameString(name: Name): String + + /** The name of the given symbol. + * If !settings.debug, the original name where + * expansions of operators are translated back to operator symbol. + * E.g. $eq => =. + * If settings.uniqid, adds id. + */ + def nameString(sym: Symbol): String + + /** The fully qualified name of the symbol */ + def fullNameString(sym: Symbol): String + + /** The kind of the symbol */ + def kindString(sym: Symbol): String + + /** The name as a text */ + def toText(name: Name): Text + + /** Textual representation, including symbol's kind e.g., "class Foo", "method Bar". + * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. + */ + def toText(sym: Symbol): Text + + /** Textual representation of symbol's declaration */ + def dclText(sym: Symbol): Text + + /** If symbol's owner is a printable class C, the text "in C", otherwise "" */ + def locationText(sym: Symbol): Text + + /** Textual representation of symbol and its location */ + def locatedText(sym: Symbol): Text + + /** Textual representation of denotation */ + def toText(denot: Denotation): Text + + /** Textual representation of constant */ + def toText(const: Constant): Text + + /** Textual representation of annotation */ + def toText(annot: Annotation): Text + + /** Textual representation of type */ + def toText(tp: Type): Text + + /** Textual representation of all symbols in given list, + * using `dclText` for displaying each. + */ + def dclsText(syms: List[Symbol], sep: String = "\n"): Text + + /** Textual representation of all definitions in a scope using `dclText` for each */ + def toText(sc: Scope): Text + + /** Textual representation of tree */ + def toText[T >: Untyped](tree: Tree[T]): Text +} + diff --git a/src/dotty/tools/dotc/printing/Printers.scala b/src/dotty/tools/dotc/printing/Printers.scala index 59e89ee9d..ba1cf83bb 100644 --- a/src/dotty/tools/dotc/printing/Printers.scala +++ b/src/dotty/tools/dotc/printing/Printers.scala @@ -1,545 +1,15 @@ package dotty.tools.dotc package printing -import core._ -import Types._, Symbols._, Contexts._, Scopes._, Names._, NameOps._, Flags._ -import Constants._, Annotations._, StdNames._, Denotations._, SymDenotations._, Trees._ -import Texts._ -import java.lang.Integer.toOctalString -import scala.annotation.switch -import parsing.{precedence, minPrec, maxPrec} +import core.Contexts.Context trait Printers { this: Context => - import Printers._ + /** A creation method for printers, depending on debug option */ + def printerFn = if (this.debug) plainPrinter else refinedPrinter - def printer = if (this.debug) plainPrinter else refinedPrinter + /** A function creatung a printer */ + def printer = printerFn(this) } -object Printers { - - class Precedence(val value: Int) extends AnyVal { - def parenthesize(nested: Precedence)(text: Text) = - if (nested.value < value) "(" ~ text ~ ")" else text - } - - val DotPrec = new Precedence(maxPrec) - val AndPrec = new Precedence(precedence(nme.raw.AMP)) - val OrPrec = new Precedence(precedence(nme.raw.BAR)) - val WithPrec = new Precedence(precedence(nme.WITHkw)) - val LeftArrowPrec = WithPrec - val GlobalPrec = new Precedence(minPrec) - - abstract class Printer { - - /** The name, possibley with with namespace suffix if debugNames is set: - * /L for local names, /V for other term names, /T for type names - */ - def nameString(name: Name): String - - /** The name of the given symbol. - * If !settings.debug, the original name where - * expansions of operators are translated back to operator symbol. - * E.g. $eq => =. - * If settings.uniqid, adds id. - */ - def nameString(sym: Symbol): String - - /** The fully qualified name of the symbol */ - def fullNameString(sym: Symbol): String - - /** The kind of the symbol */ - def kindString(sym: Symbol): String - - /** Textual representation, including symbol's kind e.g., "class Foo", "method Bar". - * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. - */ - def toText(sym: Symbol): Text - - /** Textual representation of symbol's declaration */ - def dclText(sym: Symbol): Text - - /** If symbol's owner is a printable class C, the text "in C", otherwise "" */ - def locationText(sym: Symbol): Text - - /** Textual representation of symbol and its location */ - def locatedText(sym: Symbol): Text - - /** Textual representation of denotation */ - def toText(denot: Denotation): Text - - /** Textual representation of constant */ - def toText(const: Constant): Text - - /** Textual representation of annotation */ - def toText(annot: Annotation): Text - - /** Textual representation of type in context with given precedence */ - def toText(tp: Type, precedence: Precedence): Text - - /** Textual representation of all symbols in given list, - * using `dclText` for displaying each. - */ - def dclsText(syms: List[Symbol], sep: String = "\n"): Text - - /** Textual representation of all definitions in a scope using `dclText` for each */ - def toText(sc: Scope): Text - - /** Textual representation of tree */ - def toText[T >: Untyped](tree: Tree[T]): Text - } - - class PlainPrinter(_ctx: Context) extends Printer { - protected[this] implicit val ctx = _ctx - - def controlled(op: => Text): Text = - if (ctx.toTextRecursions < maxToTextRecursions) - try { - ctx.toTextRecursions += 1 - op - } finally { - ctx.toTextRecursions -= 1 - } - else { - recursionLimitExceeeded() - "..." - } - - protected def recursionLimitExceeeded() = { - ctx.warning("Exceeded recursion depth attempting to print type.") - (new Throwable).printStackTrace - } - - /** If the name of the symbol's owner should be used when you care about - * seeing an interesting name: in such cases this symbol is e.g. a method - * parameter with a synthetic name, a constructor named "this", an object - * "package", etc. The kind string, if non-empty, will be phrased relative - * to the name of the owner. - */ - protected def hasMeaninglessName(sym: Symbol) = ( - (sym is Param) && sym.owner.isSetter // x$1 - || sym.isClassConstructor // this - || (sym.name == nme.PACKAGE) // package - ) - - def nameString(name: Name): String = name.toString + { - if (ctx.settings.debugNames.value) - if (name.isLocalName) "/L" - else if (name.isTypeName) "/T" - else "/V" - else "" - } - - /** String representation of a name used in a refinement - * In refined printing this undoes type parameter expansion - */ - protected def refinementNameString(tp: RefinedType) = nameString(tp.refinedName) - - /** String representation of a refinement */ - protected def toTextRefinement(rt: RefinedType) = - (refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close - - /** The longest sequence of refinement types, starting at given type - * and following parents. - */ - private def refinementChain(tp: Type): List[Type] = - tp :: (tp match { - case RefinedType(parent, _) => refinementChain(parent) - case _ => Nil - }) - - def toText(tp: Type, prec: Precedence): Text = controlled { - tp match { - case tp: TypeType => - toTextRHS(tp) - case tp: SingletonType => - val pre = toTextPrefix(tp) - if (pre.lastLine.endsWith(".")) pre ~ "type" - else fullNameString(tp.typeSymbol.skipPackageObject) ~ ".type" - case TypeRef(pre, name) => - toTextPrefix(pre) ~ nameString(tp.typeSymbol) - case tp: RefinedType => - // return tp.toString // !!! DEBUG - val parent :: (refined: List[RefinedType]) = - refinementChain(tp).reverse - toTextLocal(parent) ~ "{" ~ - Text(refined.map(toTextRefinement), "; ").close ~ "}" - case AndType(tp1, tp2) => - (prec parenthesize AndPrec) { - toText(tp1, AndPrec) ~ " & " ~ toText(tp2, AndPrec) - } - case OrType(tp1, tp2) => - (prec parenthesize OrPrec) { - toText(tp1, OrPrec) ~ " | " ~ toText(tp2, OrPrec) - } - case ErrorType => - "<error>" - case WildcardType => - "?" - case NoType => - "<notype>" - case NoPrefix => - "<noprefix>" - case tp: MethodType => - (prec parenthesize GlobalPrec) { - (if (tp.isImplicit) "(implicit " else "(") ~ - Text( - (tp.paramNames, tp.paramTypes).zipped - .map((name, tp) => nameString(name) ~ ": " ~ toTextGlobal(tp)), - ", ") ~ - ")" ~ toTextGlobal(tp.resultType) - } - case tp: ExprType => - (prec parenthesize GlobalPrec) { - "=> " ~ toTextGlobal(tp.resultType) - } - case tp: PolyType => - (prec parenthesize GlobalPrec) { - "[" ~ - Text( - (tp.paramNames, tp.paramBounds).zipped - .map((name, bounds) => - nameString(polyParamName(name)) ~ toTextGlobal(bounds)), - ", ") ~ - "]" ~ toTextGlobal(tp.resultType) - } - case PolyParam(pt, n) => - nameString(polyParamName(pt.paramNames(n))) - case AnnotatedType(annot, tpe) => - toTextLocal(tpe) ~ " " ~ toText(annot) - } - }.close - - protected def polyParamName(name: TypeName): TypeName = name - - /** Render type within highest precedence */ - protected def toTextLocal(tp: Type) = toText(tp, DotPrec) - - /** Render type within lowest precedence */ - protected def toTextGlobal(tp: Type) = toText(tp, GlobalPrec) - - /** The name of the symbol without a unique id. Under refined printing, - * the decoded original name. - */ - protected def simpleNameString(sym: Symbol): String = nameString(sym.name) - - /** The unique id of symbol, after a # */ - protected def idString(sym: Symbol): String = - if (ctx.settings.uniqid.value) "#" + sym.id else "" - - def nameString(sym: Symbol): String = simpleNameString(sym) + idString(sym) - - def fullNameString(sym: Symbol): String = - if (sym.isRoot || sym == NoSymbol || sym.owner.isEffectiveRoot) - nameString(sym) - else - fullNameString(sym.effectiveOwner.enclosingClass) + "." + nameString(sym) - - protected def objectPrefix = "object " - protected def packagePrefix = "package " - - protected def trimPrefix(text: Text) = - text.stripPrefix(objectPrefix).stripPrefix(packagePrefix) - - /** The string representation of this type used as a prefix */ - protected def toTextPrefix(tp: Type): Text = controlled { - tp match { - case tp @ TermRef(pre, name) => - toTextPrefix(pre) ~ nameString(tp.symbol) ~ "." - case ThisType(cls) => - nameString(cls) + ".this." - case SuperType(thistpe, _) => - toTextPrefix(thistpe).map(_.replaceAll("""\bthis\.$""", "super.")) - case tp @ ConstantType(value) => - toTextLocal(tp.underlying) ~ "(" ~ toText(value) ~ ")." - case MethodParam(mt, idx) => - nameString(mt.paramNames(idx)) + "." - case RefinedThis(_) => - "this." - case NoPrefix => - "" - case _ => - trimPrefix(toTextLocal(tp)) ~ "#" - } - } - - protected def isOmittablePrefix(sym: Symbol) = - (defn.UnqualifiedOwners contains sym) || isEmptyPrefix(sym) - - protected def isEmptyPrefix(sym: Symbol) = - sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName - - /** String representation of a definition's type following its name */ - protected def toTextRHS(tp: Type): Text = controlled { - tp match { - case TypeBounds(lo, hi) => - if (lo eq hi) - " = " ~ lo.toText - else - (if (lo == defn.NothingType) Text() else " >: " ~ lo.toText) ~ - (if (hi == defn.AnyType) Text() else " <: " ~ hi.toText) - case ClassInfo(pre, cls, cparents, decls, optSelfType) => - val preText = toTextLocal(pre) - val (tparams, otherDecls) = decls.toList partition treatAsTypeParam - val tparamsText = - if (tparams.isEmpty) Text() else ("[" ~ dclsText(tparams) ~ "]").close - val selfText = - if (optSelfType.exists) - "this: " ~ toText(optSelfType, LeftArrowPrec) ~ " =>" - else Text() - val parentsText = Text(cparents.map(p => - toText(reconstituteParent(cls, p), WithPrec)), " with ") - val trueDecls = otherDecls.filterNot(treatAsTypeArg) - val declsText = if (trueDecls.isEmpty) Text() else dclsText(trueDecls) - tparamsText ~ " extends " ~ parentsText ~ "{" ~ selfText ~ declsText ~ - "} at " ~ preText - case _ => - ": " ~ toTextGlobal(tp) - } - } - - protected def treatAsTypeParam(sym: Symbol): Boolean = false - protected def treatAsTypeArg(sym: Symbol): Boolean = false - protected def reconstituteParent(cls: ClassSymbol, parent: Type): Type = parent - - /** String representation of symbol's kind. */ - def kindString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (flags is PackageClass) "package class" - else if (flags is PackageVal) "package" - else if (sym.isPackageObject) - if (sym.isClass) "package object class" - else "package object" - else if (sym.isAnonymousClass) "anonymous class" - else if (flags is ModuleClass) "module class" - else if (flags is ModuleVal) "module" - else if (flags is ImplClass) "implementation class" - else if (flags is Trait) "trait" - else if (sym.isClass) "class" - else if (sym.isType) "type" - else if (sym.isGetter) "getter" - else if (sym.isSetter) "setter" - else if (flags is Lazy) "lazy value" - else if (flags is Mutable) "variable" - else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" - else if (sym.isClassConstructor) "constructor" - else if (sym.isSourceMethod) "method" - else if (sym.isTerm) "value" - else "" - } - - /** String representation of symbol's definition key word */ - protected def keyString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (flags is JavaInterface) "interface" - else if ((flags is Trait) && !(flags is ImplClass)) "trait" - else if (sym.isClass) "class" - else if (sym.isType) "type" - else if (flags is Mutable) "var" - else if (flags is Package) "package" - else if (flags is Module) "object" - else if (sym.isSourceMethod) "def" - else if (sym.isTerm && (!(flags is Param))) "val" - else "" - } - - /** String representation of symbol's flags */ - protected def toTextFlags(sym: Symbol): Text = - Text(sym.flags.flagStrings map stringToText, " ") - - /** String representation of symbol's variance or "" if not applicable */ - protected def varianceString(sym: Symbol): String = sym.variance match { - case -1 => "-" - case 1 => "+" - case _ => "" - } - - def dclText(sym: Symbol): Text = - (toTextFlags(sym) ~~ keyString(sym) ~~ - (varianceString(sym) ~ nameString(sym)) ~ toTextRHS(sym.info)).close - - def toText(sym: Symbol): Text = - (kindString(sym) ~~ { - if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) - else nameString(sym) - }).close - - def locationText(sym: Symbol): Text = { - val owns = sym.effectiveOwner - if (owns.isClass && !isEmptyPrefix(owns)) " in " ~ toText(owns) else Text() - } - - def locatedText(sym: Symbol): Text = - (toText(sym) ~ locationText(sym)).close - - def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D" - - @switch private def escapedChar(ch: Char): String = ch match { - case '\b' => "\\b" - case '\t' => "\\t" - case '\n' => "\\n" - case '\f' => "\\f" - case '\r' => "\\r" - case '"' => "\\\"" - case '\'' => "\\\'" - case '\\' => "\\\\" - case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) - } - - def toText(const: Constant): Text = const.tag match { - case StringTag => "\"" + (const.value.toString flatMap escapedChar) + "\"" - case ClazzTag => "classOf[" ~ const.tpe.toText ~ "]" - case CharTag => s"'${escapedChar(const.charValue)}'" - case LongTag => const.longValue.toString + "L" - case EnumTag => const.symbolValue.name.toString - case _ => String.valueOf(const.value) - } - - def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now - - def dclsText(syms: List[Symbol], sep: String): Text = - if (sep == "\n") Text.lines(syms map dclText) - else Text(syms map dclText, sep) - - def toText(sc: Scope): Text = - ("Scope{" ~ dclsText(sc.toList) ~ "}").close - - def toText[T >: Untyped](tree: Tree[T]): Text = { - tree match { - case node: Positioned => - def toTextElem(elem: Any): Text = elem match { - case elem: Showable => elem.toText - case elem: List[_] => "List(" ~ Text(elem map toTextElem, ",") ~ ")" - case elem => elem.toString - } - val nodeName = node.productPrefix - val elems = - Text(node.productIterator.map(toTextElem).toList, ", ") - val tpSuffix = - if (ctx.settings.printtypes.value && tree.hasType) - " | " ~ tree.tpe.asInstanceOf[Type].toText - else - Text() - - nodeName ~ "(" ~ elems ~ tpSuffix ~ ")" ~ node.pos.toString - case _ => - tree.toString: Text - } - }.close // todo: override in refined printer - } - - class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { - override protected def recursionLimitExceeeded() = {} - - override def nameString(name: Name): String = name.toString - - override protected def simpleNameString(sym: Symbol): String = { - var name = sym.originalName - if (sym is ModuleClass) name = name.stripModuleClassSuffix - name.decode.toString - } - - override def toTextPrefix(tp: Type): Text = controlled { - tp match { - case ThisType(cls) => - if (cls.isAnonymousClass) return "this." - if (isOmittablePrefix(cls)) return "" - if (cls is ModuleClass) return fullNameString(cls) + "." - case tp @ TermRef(pre, name) => - val sym = tp.symbol - if (sym.isPackageObject) return toTextPrefix(pre) - if (isOmittablePrefix(sym)) return "" - case _ => - } - super.toTextPrefix(tp) - } - - override protected def refinementNameString(tp: RefinedType): String = { - val tsym = tp.member(tp.refinedName).symbol - val name = tsym.originalName - nameString(if (tsym is ExpandedTypeParam) name.asTypeName.unexpandedName() else name) - } - - override def toText(tp: Type, prec: Precedence): Text = controlled { - def toTextFunction(args: List[Type]): Text = - (prec parenthesize GlobalPrec) { - val argStr: Text = - if (args.length == 2 && - !(defn.TupleClasses contains args.head.typeSymbol)) toText(args.head, LeftArrowPrec) - else - "(" ~ Text(args.init.map(toTextGlobal(_)), ", ") ~ ")" - argStr ~ " => " ~ toTextGlobal(args.last) - } - def toTextTuple(args: List[Type]): Text = - "(" ~ Text(args.map(toTextGlobal(_)), ", ") ~ ")" - tp match { - case tp: RefinedType => - val args = tp.typeArgs - if (args.nonEmpty) { - val tycon = tp.unrefine - val cls = tycon.typeSymbol - if (cls.typeParams.length == args.length) { - if (cls == defn.RepeatedParamClass) return toTextLocal(args.head) ~ "*" - if (cls == defn.ByNameParamClass) return "=> " ~ toTextGlobal(args.head) - if (defn.FunctionClasses contains cls) return toTextFunction(args) - if (defn.TupleClasses contains cls) return toTextTuple(args) - } - return (toTextLocal(tycon) ~ "[" ~ - Text(args.map(toTextGlobal(_)), ", ") ~ "]").close - } - case tp @ TypeRef(pre, name) => - if (tp.symbol is TypeParam) return nameString(tp.symbol) - case _ => - } - super.toText(tp, prec) - } - - override def toText[T >: Untyped](tree: Tree[T]): Text = toText(tree, GlobalPrec) - - def toText[T >: Untyped](tree: Tree[T], prec: Precedence): Text = ??? - - override protected def polyParamName(name: TypeName): TypeName = - name.unexpandedName() - - override protected def treatAsTypeParam(sym: Symbol): Boolean = sym is TypeParam - - override protected def treatAsTypeArg(sym: Symbol) = - sym.isType && (sym is ProtectedLocal) && - (sym.allOverriddenSymbols exists (_ is TypeParam)) - - override protected def reconstituteParent(cls: ClassSymbol, parent: Type): Type = - (parent /: parent.classSymbol.typeParams) { (parent, tparam) => - val targSym = cls.decls.lookup(tparam.name) - if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) - else parent - } - - override def kindString(sym: Symbol) = { - val flags = sym.flagsUNSAFE - if (flags is Package) "package" - else if (sym.isPackageObject) "package object" - else if (flags is Module) "object" - else if (flags is ImplClass) "class" - else if (sym.isClassConstructor) "constructor" - else super.kindString(sym) - } - - override protected def keyString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (sym.isType && (flags is ExpandedTypeParam)) "" - else super.keyString(sym) - } - - override def toTextFlags(sym: Symbol) = { - var flags = sym.flags - if (flags is TypeParam) flags = flags &~ Protected - Text(flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ") - } - - override def toText(denot: Denotation): Text = toText(denot.symbol) - } - - final val maxToTextRecursions = 100 - -} diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala new file mode 100644 index 000000000..88cc33c40 --- /dev/null +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -0,0 +1,372 @@ +package dotty.tools.dotc +package printing + +import core._ +import Texts._, Trees._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._ +import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation +import StdNames.nme +import UntypedTrees.untpd +import scala.annotation.switch + +class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { + override protected def recursionLimitExceeeded() = {} + + /** The closest enclosing DefDef, TypeDef, or ClassDef node */ + private var currentOwner: Tree[_ >: Untyped] = EmptyTree() + + def atOwner(owner: Tree[_ >: Untyped])(op: => Text): Text = { + val saved = owner + currentOwner = owner + try op + finally { currentOwner = saved } + } + + override def nameString(name: Name): String = name.toString + + override protected def simpleNameString(sym: Symbol): String = { + var name = sym.originalName + if (sym is ModuleClass) name = name.stripModuleClassSuffix + name.decode.toString + } + + override def toTextPrefix(tp: Type): Text = controlled { + tp match { + case ThisType(cls) => + if (cls.isAnonymousClass) return "this." + if (isOmittablePrefix(cls)) return "" + if (cls is ModuleClass) return fullNameString(cls) + "." + case tp @ TermRef(pre, name) => + val sym = tp.symbol + if (sym.isPackageObject) return toTextPrefix(pre) + if (isOmittablePrefix(sym)) return "" + case _ => + } + super.toTextPrefix(tp) + } + + override protected def refinementNameString(tp: RefinedType): String = { + val tsym = tp.member(tp.refinedName).symbol + val name = tsym.originalName + nameString(if (tsym is ExpandedTypeParam) name.asTypeName.unexpandedName() else name) + } + + override def toText(tp: Type): Text = controlled { + def toTextTuple(args: List[Type]): Text = + "(" ~ toTextGlobal(args, ", ") ~ ")" + def toTextFunction(args: List[Type]): Text = + changePrec(GlobalPrec) { + val argStr: Text = + if (args.length == 2 && !(defn.TupleClasses contains args.head.typeSymbol)) + atPrec(InfixPrec) { toText(args.head) } + else + toTextTuple(args.init) + argStr ~ " => " ~ toText(args.last) + } + tp match { + case tp: RefinedType => + val args = tp.typeArgs + if (args.nonEmpty) { + val tycon = tp.unrefine + val cls = tycon.typeSymbol + if (cls.typeParams.length == args.length) { + if (cls == defn.RepeatedParamClass) return toTextLocal(args.head) ~ "*" + if (cls == defn.ByNameParamClass) return "=> " ~ toText(args.head) + if (defn.FunctionClasses contains cls) return toTextFunction(args) + if (defn.TupleClasses contains cls) return toTextTuple(args) + } + return (toTextLocal(tycon) ~ "[" ~ toTextGlobal(args, ", ") ~ "]").close + } + case tp @ TypeRef(pre, name) => + if (tp.symbol is TypeParam) return nameString(tp.symbol) + case _ => + } + super.toText(tp) + } + + override def toText[T >: Untyped](tree: Tree[T]): Text = { + + def optDotPrefix(name: Name) = optText(name)(_ ~ ".") + + def tparamsText(params: List[Tree[T]]): Text = + "[" ~ toText(params, ", ") ~ "]" provided params.nonEmpty + + def addVparamssText[T >: Untyped](txt: Text, vparamss: List[List[ValDef[T]]]): Text = + (txt /: vparamss)((txt, vparams) => txt ~ "(" ~ toText(vparams, ", ") ~ ")") + + def blockText[T >: Untyped](trees: List[Tree[T]]): Text = + "{" ~ toText(trees, "\n") ~ "}" + + def caseBlockText[T >: Untyped](tree: Tree[T]): Text = tree match { + case Block(stats, expr) => toText(stats :+ expr, "\n") + case expr => toText(expr) + } + + def forText(enums: List[Tree[T]], expr: Tree[T], sep: String): Text = + changePrec(GlobalPrec) { "for " ~ toText(enums, "; ") ~ sep ~ toText(expr) } + + val txt: Text = tree match { + case id: BackquotedIdent[_] => + "`" ~ toText(id.name) ~ "`" + case Ident(name) => + toText(name) + case Select(qual, name) => + toTextLocal(qual) ~ "." ~ toText(name) + case This(name) => + optDotPrefix(name) ~ "this" + case Super(This(name), mix) => + optDotPrefix(name) ~ "super" ~ optText(mix)("[" ~ _ ~ "]") + case Apply(fun, args) => + toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + case TypeApply(fun, args) => + toTextLocal(fun) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" + case Literal(c) => + toText(c) + case New(tpt) => + "new " ~ toTextLocal(tpt) + case Pair(l, r) => + "(" ~ toTextGlobal(l) ~ ", " ~ toTextGlobal(r) ~ ")" + case Typed(l, tpt) => + changePrec(InfixPrec) { toText(l) ~ ": " ~ toText(tpt) } + case NamedArg(name, arg) => + toText(name) ~ " = " ~ toText(arg) + case Assign(lhs, rhs) => + changePrec(GlobalPrec) { toTextLocal(lhs) ~ " = " ~ toText(rhs) } + case Block(stats, expr) => + blockText(stats :+ expr) + case If(cond, thenp, elsep) => + changePrec(GlobalPrec) { + "if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[untpd.Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _) + } + case Match(sel, cases) => + if (sel.isEmpty) blockText(cases) + else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) } + case CaseDef(pat, guard, body) => + "case " ~ toText(pat) ~ optText(guard)("if " ~ _) ~ " => " ~ caseBlockText(body) + case Return(expr, from) => + changePrec(GlobalPrec) { "return " ~ toText(expr) } + case Try(expr, handler, finalizer) => + changePrec(GlobalPrec) { + "try " ~ toText(expr) ~ optText(handler)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _) + } + case Throw(expr) => + changePrec(GlobalPrec) { + "throw " ~ toText(expr) + } + case SeqLiteral(elempt, elems) => + "[" ~ toTextGlobal(elems, ",") ~ "]" + case TypeTree(orig) => + if (tree.hasType) toText(tree.typeOpt) else toText(orig) + case SingletonTypeTree(ref) => + toTextLocal(ref) ~ ".type" + case SelectFromTypeTree(qual, name) => + toTextLocal(qual) ~ "#" ~ toText(name) + case AndTypeTree(l, r) => + changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) } + case OrTypeTree(l, r) => + changePrec(OrPrec) { toText(l) ~ " | " ~ toText(r) } + case RefinedTypeTree(tpt, refines) => + toTextLocal(tpt) ~ " " ~ blockText(refines) + case AppliedTypeTree(tpt, args) => + toTextLocal(tpt) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + case TypeBoundsTree(lo, hi) => + optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _) + case Bind(name, body) => + changePrec(InfixPrec) { toText(name) ~ " @ " ~ toText(body) } + case Alternative(trees) => + changePrec(OrPrec) { toText(trees, " | ") } + case UnApply(fun, args) => + toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + case ValDef(mods, name, tpt, rhs) => + toText(mods, if (mods is Mutable) "var" else "val") ~~ toText(name) ~ + optText(tpt)(": " ~ _) ~ optText(rhs)(" = " ~ _) + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree) { + val first = toText(mods, "def") ~~ toText(name) ~ tparamsText(tparams) + addVparamssText(first, vparamss) ~ optText(tpt)(": " ~ _) ~ optText(rhs)(" = " ~ _) + } + case TypeDef(mods, name, tparams, rhs) => + atOwner(tree) { + val rhsText = rhs match { + case TypeBoundsTree(_, _) => toText(rhs) + case _ => optText(rhs)(" = " ~ _) + } + toText(mods, "type") ~~ toText(name) ~ tparamsText(tparams) ~ rhsText + } + case Template(DefDef(mods, _, _, vparamss, _, _), parents, self, stats) => + val prefix: Text = + if (vparamss.isEmpty) "" + else { + var modsText = toText(mods, "") + if (mods.hasAnnotations && !mods.hasFlags) modsText = modsText ~~ " this" + addVparamssText(modsText, vparamss) ~ " extends" + } + val parentsText = toTextLocal(parents, "with") + val selfText = optText(self)(" " ~ _ ~ " => ") + val bodyText: Text = stats match { + case Nil => + assert(self.isEmpty) + "" + case EmptyTree() :: Nil => + "{" ~ selfText ~ "}" + case stats => + "{" ~ selfText ~ toTextGlobal(stats, "\n") ~ "}" + } + prefix ~~ parentsText ~~ bodyText + case ClassDef(mods, name, tparams, impl) => + atOwner(tree) { + toText(mods, if (mods is Trait) "trait" else "class") ~~ + toText(name) ~ tparamsText(tparams) ~ toText(impl) + } + case Import(expr, selectors) => + def selectorText(sel: UntypedTree): Text = sel match { + case Pair(l, r) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) + case _ => toTextGlobal(sel) + } + val selectorsText: Text = selectors match { + case Ident(name) :: Nil => toText(name) + case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" + } + "import " ~ toTextLocal(expr) ~ "." ~ selectorsText + case PackageDef(pid, stats) => + val statsText = stats match { + case (pdef: PackageDef[_]) :: Nil => toText(pdef) + case _ => toTextGlobal(stats, "\n") + } + val bodyText = + if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}" + "package " ~ toTextLocal(pid) ~ bodyText + case Annotated(annot, arg) => + toTextLocal(arg) ~ " @" ~ toTextLocal(annot) + case EmptyTree() => + "<empty>" + case SharedTree(shared) => + toText(shared) + case untpd.TypedSplice(t) => + toText(t) + case untpd.ModuleDef(mods, name, impl) => + toText(mods, "object") ~~ toText(name) ~ " extends " ~ toText(impl) + case untpd.SymbolLit(str) => + "'" + str + case untpd.InterpolatedString(id, strings, elems) => + def interleave(strs: List[Text], elems: List[Text]): Text = ((strs, elems): @unchecked) match { + case (Nil, Nil) => "" + case (str :: Nil, Nil) => str + case (str :: strs1, elem :: elems1) => str ~ elem ~ interleave(strs1, elems1) + } + val strTexts = strings map (str => Str(escapedString(str.const.stringValue))) + val elemsTexts = elems map (elem => "{" ~ toTextGlobal(elem) ~ "}") + toText(id) ~ "\"" ~ interleave(strTexts, elemsTexts) ~ "\"" + case untpd.Function(args, body) => + var implicitSeen: Boolean = false + def argToText(arg: untpd.Tree) = arg match { + case ValDef(mods, name, tpt, _) => + val implicitText = + if ((mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " } + else "" + implicitText ~ toText(name) ~ optText(tpt)(": " ~ _) + case _ => + toText(arg) + } + val argsText = args match { + case (arg @ ValDef(_, _, tpt, _)) :: Nil if tpt.isEmpty => argToText(arg) + case _ => "(" ~ Text(args map argToText, ", ") ~ ")" + } + changePrec(GlobalPrec) { + argsText ~ " => " ~ toText(body) + } + case untpd.InfixOp(l, op, r) => + val opPrec = parsing.precedence(op) + changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) } + case untpd.PostfixOp(l, op) => + changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) } + case untpd.PrefixOp(op, r) => + changePrec(DotPrec) { toText(op) ~ " " ~ toText(r) } + case untpd.Parens(t) => + "(" ~ toTextGlobal(t) ~ ")" + case untpd.Tuple(ts) => + "(" ~ toTextGlobal(ts, ", ") ~ ")" + case untpd.WhileDo(cond, body) => + changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) } + case untpd.DoWhile(cond, body) => + changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) } + case untpd.ForYield(enums, expr) => + forText(enums, expr, " yield ") + case untpd.ForDo(enums, expr) => + forText(enums, expr, " do ") + case untpd.GenFrom(pat, expr) => + toText(pat) ~ " <- " ~ toText(expr) + case untpd.GenAlias(pat, expr) => + toText(pat) ~ " = " ~ toText(expr) + case untpd.ContextBounds(bounds, cxBounds) => + (toText(bounds) /: cxBounds) {(t, cxb) => + t ~ " : " ~ toText(cxb) + } + case untpd.PatDef(mods, pats, tpt, rhs) => + toText(mods, "val") ~~ toText(pats, ", ") ~ optText(tpt)(": " ~ _) ~ + optText(rhs)(" = " ~ _) + case _ => + tree.fallbackToText(this) + } + tree match { + case Block(_, _) | Template(_, _, _, _) => txt + case _ => txt.close + } + } + + def optText(name: Name)(encl: Text => Text): Text = + if (name.isEmpty) "" else encl(toText(name)) + + def optText[T >: Untyped](tree: Tree[T])(encl: Text => Text): Text = + if (tree.isEmpty) "" else encl(toText(tree)) + + def toText[T >: Untyped](mods: Modifiers[T], kw: String): Text = { + def annotText(annot: Tree[T]) = "@" ~ toTextLocal(annot) + def suppressKw = currentOwner match { + case _: ClassDef[_] => mods is ParamAndLocal + case _ => mods is Param + } + val flagMask = if (suppressKw) ModifierFlags &~ Private else ModifierFlags + val modsText: Text = (mods.flags & flagMask).toString + Text(mods.annotations.map(annotText), " ") ~~ modsText ~~ (kw provided !suppressKw) + } + + override protected def polyParamName(name: TypeName): TypeName = + name.unexpandedName() + + override protected def treatAsTypeParam(sym: Symbol): Boolean = sym is TypeParam + + override protected def treatAsTypeArg(sym: Symbol) = + sym.isType && (sym is ProtectedLocal) && + (sym.allOverriddenSymbols exists (_ is TypeParam)) + + override protected def reconstituteParent(cls: ClassSymbol, parent: Type): Type = + (parent /: parent.classSymbol.typeParams) { (parent, tparam) => + val targSym = cls.decls.lookup(tparam.name) + if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) + else parent + } + + override def kindString(sym: Symbol) = { + val flags = sym.flagsUNSAFE + if (flags is Package) "package" + else if (sym.isPackageObject) "package object" + else if (flags is Module) "object" + else if (flags is ImplClass) "class" + else if (sym.isClassConstructor) "constructor" + else super.kindString(sym) + } + + override protected def keyString(sym: Symbol): String = { + val flags = sym.flagsUNSAFE + if (sym.isType && (flags is ExpandedTypeParam)) "" + else super.keyString(sym) + } + + override def toTextFlags(sym: Symbol) = { + var flags = sym.flags + if (flags is TypeParam) flags = flags &~ Protected + Text(flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ") + } + + override def toText(denot: Denotation): Text = toText(denot.symbol) +} diff --git a/src/dotty/tools/dotc/printing/Showable.scala b/src/dotty/tools/dotc/printing/Showable.scala index 73d320019..cb65193e6 100644 --- a/src/dotty/tools/dotc/printing/Showable.scala +++ b/src/dotty/tools/dotc/printing/Showable.scala @@ -5,10 +5,19 @@ import core._ import Contexts._, Texts._, Decorators._ -trait Showable { +trait Showable extends Any { - def toText(implicit ctx: Context): Text + /** The text representation of this showable element. + * This normally dispatches to a pattern matching + * method in Printers. + */ + def toText(printer: Printer): Text - def show(implicit ctx: Context): String = toText.show + /** A fallback text representation, if the pattern matching + * in Printers does not have a case for this showable element + */ + def fallbackToText(printer: Printer): Text = toString + /** The string representation of this showable element. */ + def show(implicit ctx: Context): String = toText(ctx.printer).show }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/printing/Texts.scala b/src/dotty/tools/dotc/printing/Texts.scala index 2ff4edc9a..8c214f5f0 100644 --- a/src/dotty/tools/dotc/printing/Texts.scala +++ b/src/dotty/tools/dotc/printing/Texts.scala @@ -1,5 +1,6 @@ package dotty.tools.dotc package printing +import core.Contexts.Context object Texts { @@ -128,15 +129,28 @@ object Texts { def over (that: Text) = if (this.isVertical) Vertical(that :: this.relems) else Vertical(that :: this :: Nil) + + def provided(pred: => Boolean) = if (pred) this else Str("") } object Text { + + /** The empty text */ def apply(): Text = Str("") + + /** A concatenation of elements in `xs` and interspersed with + * separator strings `sep`. + */ def apply(xs: Traversable[Text], sep: String = " "): Text = { - val ys = xs filterNot (_.isEmpty) - if (ys.isEmpty) Str("") - else ys reduce (_ ~ sep ~ _) + if (sep == "\n") lines(xs) + else { + val ys = xs filterNot (_.isEmpty) + if (ys.isEmpty) Str("") + else ys reduce (_ ~ sep ~ _) + } } + + /** The given texts `xs`, each on a separate line */ def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse) } diff --git a/src/dotty/tools/dotc/printing/package.scala b/src/dotty/tools/dotc/printing/package.scala new file mode 100644 index 000000000..814eb2ad0 --- /dev/null +++ b/src/dotty/tools/dotc/printing/package.scala @@ -0,0 +1,17 @@ +package dotty.tools.dotc + +import core.StdNames.nme +import parsing.{precedence, minPrec, maxPrec, minInfixPrec} + +package object printing { + + type Precedence = Int + + val DotPrec = parsing.maxPrec + val AndPrec = parsing.precedence(nme.raw.AMP) + val OrPrec = parsing.precedence(nme.raw.BAR) + val InfixPrec = parsing.minInfixPrec + val GlobalPrec = parsing.minPrec + val TopLevelPrec = parsing.minPrec - 1 + +} |