From 2a9127460b1f87f93be8d2dcc2e0cb2e592191c5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 May 2019 15:10:42 +0200 Subject: [PATCH 1/4] Don't drop module class suffix in RefinedPrinter Don't drop the module class suffix `$` in RefinedPrinter.simpleNameString. We do this in ExplainingPrinter instead, where we also take compensations to explain that this is an object. --- 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 eeb3b7961b0f..c8ba4f862774 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -78,7 +78,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (ctx.settings.YdebugNames.value) name.debugString else NameTransformer.decodeIllegalChars(name.toString) override protected def simpleNameString(sym: Symbol): String = - nameString(if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name) + nameString(if (ctx.property(XprintMode).isEmpty) sym.initial.name else sym.name) override def fullNameString(sym: Symbol): String = if (isEmptyPrefix(sym.maybeOwner)) nameString(sym) From 11b8ee8c304375405125d98853335e0fd56e590c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 May 2019 15:29:55 +0200 Subject: [PATCH 2/4] Print type of module class C as "object C" --- .../src/dotty/tools/dotc/printing/Formatting.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 6b6a6845565a..9a513e0fa704 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -128,8 +128,13 @@ object Formatting { } private class ExplainingPrinter(seen: Seen)(_ctx: Context) extends RefinedPrinter(_ctx) { + + /** True if printer should a source module instead of its module class */ + private def useSourceModule(sym: Symbol): Boolean = + sym.is(ModuleClass, butNot = Package) && sym.sourceModule.exists && !_ctx.settings.YdebugNames.value + override def simpleNameString(sym: Symbol): String = - if ((sym is ModuleClass) && sym.sourceModule.exists) simpleNameString(sym.sourceModule) + if (useSourceModule(sym)) simpleNameString(sym.sourceModule) else seen.record(super.simpleNameString(sym), sym) override def ParamRefNameString(param: ParamRef): String = @@ -139,6 +144,11 @@ object Formatting { case tp: SkolemType => seen.record(tp.repr.toString, tp) case _ => super.toTextRef(tp) } + + override def toText(tp: Type): Text = tp match { + case tp: TypeRef if useSourceModule(tp.symbol) => Str("object ") ~ super.toText(tp) + case _ => super.toText(tp) + } } /** Create explanation for single `Recorded` type or symbol */ From 01260f497dae3c3055266a0f39836d049a404f15 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 May 2019 15:32:06 +0200 Subject: [PATCH 3/4] Don't disambiguate between terms and types An ExplainingTypePrinter would disambiguate between a class and its companion object, like this: {message involving C and C'} where C is a class ... C' is an object I believe this is not necessessary, as types and terms should be distinguishable from the context in which they appear. --- .../tools/dotc/printing/Formatting.scala | 39 ++++++++++++------- .../tools/dotc/printing/PlainPrinter.scala | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 9a513e0fa704..1a3e6b43f7d5 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -106,22 +106,32 @@ object Formatting { private type Recorded = AnyRef /*Symbol | ParamRef | SkolemType */ - private class Seen extends mutable.HashMap[String, List[Recorded]] { + private case class SeenKey(str: String, isType: Boolean) + private class Seen extends mutable.HashMap[SeenKey, List[Recorded]] { - override def default(key: String) = Nil + override def default(key: SeenKey) = Nil - def record(str: String, entry: Recorded)(implicit ctx: Context): String = { + def record(str: String, isType: Boolean, entry: Recorded)(implicit ctx: Context): String = { + + /** If `e1` is an alias of another class of the same name, return the other + * class symbol instead. This normalization avoids recording e.g. scala.List + * and scala.collection.immutable.List as two different types + */ def followAlias(e1: Recorded): Recorded = e1 match { case e1: Symbol if e1.isAliasType => val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol if (underlying.name == e1.name) underlying else e1 case _ => e1 } + val key = SeenKey(str, isType) + val existing = apply(key) lazy val dealiased = followAlias(entry) - var alts = apply(str).dropWhile(alt => dealiased ne followAlias(alt)) + + // alts: The alternatives in `existing` that are equal, or follow (an alias of) `entry` + var alts = existing.dropWhile(alt => dealiased ne followAlias(alt)) if (alts.isEmpty) { - alts = entry :: apply(str) - update(str, alts) + alts = entry :: existing + update(key, alts) } str + "'" * (alts.length - 1) } @@ -135,13 +145,13 @@ object Formatting { override def simpleNameString(sym: Symbol): String = if (useSourceModule(sym)) simpleNameString(sym.sourceModule) - else seen.record(super.simpleNameString(sym), sym) + else seen.record(super.simpleNameString(sym), sym.isType, sym) override def ParamRefNameString(param: ParamRef): String = - seen.record(super.ParamRefNameString(param), param) + seen.record(super.ParamRefNameString(param), param.isInstanceOf[TypeParamRef], param) override def toTextRef(tp: SingletonType): Text = tp match { - case tp: SkolemType => seen.record(tp.repr.toString, tp) + case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp) case _ => super.toTextRef(tp) } @@ -207,10 +217,13 @@ object Formatting { } val toExplain: List[(String, Recorded)] = seen.toList.flatMap { - case (str, entry :: Nil) => - if (needsExplanation(entry)) (str, entry) :: Nil else Nil - case (str, entries) => - entries.map(alt => (seen.record(str, alt), alt)) + case (key, entry :: Nil) => + if (needsExplanation(entry)) (key.str, entry) :: Nil else Nil + case (key, entries) => + for (alt <- entries) yield { + val tickedString = seen.record(key.str, key.isType, alt) + (tickedString, alt) + } }.sortBy(_._1) def columnar(parts: List[(String, String)]): List[String] = { diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 9efd80c1424c..aaf84dde745a 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -298,7 +298,7 @@ class PlainPrinter(_ctx: Context) extends Printer { } } - /** The string representation of this type used as a prefix */ + /** The string representation of this type used as a prefix, including separator */ protected def toTextPrefix(tp: Type): Text = controlled { homogenize(tp) match { case NoPrefix => "" From efe720ab4e186c39f6623bed88293afc142e0c9a Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Wed, 8 May 2019 16:42:21 +0200 Subject: [PATCH 4/4] CompletionTest tests adapted to the new printer format --- .../test/dotty/tools/languageserver/CompletionTest.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 39b37ddfeb19..e16df58a7527 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -79,7 +79,7 @@ class CompletionTest { withSources( code"""object O { object MyObject }""", code"""import O.My${m1}""" - ).completion(m1, Set(("MyObject", Module, "O.MyObject"))) + ).completion(m1, Set(("MyObject", Module, "O.MyObject$"))) } @Test def importCompleteWithClassAndCompanion: Unit = { @@ -114,7 +114,7 @@ class CompletionTest { ).completion(m1, Set(("myVal", Field, "Int"), ("myDef", Method, "=> Int"), ("myVar", Variable, "Int"), - ("myObject", Module, "MyObject.myObject"), + ("myObject", Module, "MyObject.myObject$"), ("myClass", Class, "MyObject.myClass"), ("myTrait", Class, "MyObject.myTrait"))) } @@ -138,7 +138,7 @@ class CompletionTest { code"""object O { val out = java.io.FileDesc${m1} }""".withSource - .completion(m1, Set(("FileDescriptor", Module, "java.io.FileDescriptor"))) + .completion(m1, Set(("FileDescriptor", Module, "java.io.FileDescriptor$"))) } @Test def importRename: Unit = { @@ -197,7 +197,7 @@ class CompletionTest { | object bat | val bizz: ba${m1} |}""".withSource - .completion(m1, Set(("bar", Field, "Bar"), ("bat", Module, "Foo.bat"))) + .completion(m1, Set(("bar", Field, "Bar"), ("bat", Module, "Foo.bat$"))) } @Test def completionOnRenamedImport: Unit = {