aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/printing/Disambiguation.scala
blob: baacee42fea4ed2c5834d55b34e9c4bb85485724 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package dotty.tools.dotc
package printing

import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Contexts._
import collection.mutable
import scala.annotation.switch

object Disambiguation {

  private class State {
    var hasConflicts = false
    val symString = new mutable.HashMap[Symbol, String]
    val variants = new mutable.HashMap[String, mutable.ListBuffer[Symbol]]
  }

  def newPrinter: Context => Printer = {
    val state = new State
    new Printer(state)(_)
  }

  class Printer(state: State)(_ctx: Context) extends RefinedPrinter(_ctx) {
    import state._

    override def simpleNameString(sym: Symbol): String = {
      if ((sym is ModuleClass) && sym.sourceModule.exists) simpleNameString(sym.sourceModule)
      else symString.getOrElse(sym, recordedNameString(sym))
    }

    private def rawNameString(sym: Symbol) = super.simpleNameString(sym)

    private def recordedNameString(sym: Symbol): String = {
      val str = rawNameString(sym)
      val existing = variants.getOrElse(str, new mutable.ListBuffer[Symbol])
        // Dotty deviation: without a type parameter on ListBuffer, inference
        // will compute ListBuffer[Symbol] | ListBuffer[Nothing] as the type of "existing"
        // and then the assignment to variants below will fail.
        // We need to find a way to avoid such useless inferred types.
      if (!(existing contains sym)) {
        hasConflicts |= existing.nonEmpty
        variants(str) = (existing += sym)
      }
      str
    }

    def disambiguated(): Boolean = {
      val res = hasConflicts
      while (hasConflicts) disambiguate()
      res
    }

    private def qualifiers: Stream[String] =
      Stream("", "(some other)", "(some 3rd)") ++ (Stream.from(4) map (n => s"(some ${n}th)"))

    private def disambiguate(): Unit = {
      def update(sym: Symbol, str: String) = if (!(symString contains sym)) symString(sym) = str
      def disambiguated(sym: Symbol, owner: Symbol) = s"${rawNameString(sym)}(in ${simpleNameString(owner)})"
      hasConflicts = false
      for ((name, vs) <- variants.toList)
        if (vs.tail.nonEmpty) {
          for ((owner, syms) <- vs.groupBy(_.effectiveOwner)) {
            if (syms.tail.isEmpty) update(syms.head, disambiguated(syms.head, owner))
            else
              for {
                (kind, syms1) <- syms.groupBy(kindString)
                (sym, qual) <- syms1 zip qualifiers
              } {
                update(sym, s"$qual$kind ${disambiguated(sym, owner)}")
              }
          }
        }
    }
  }

  def disambiguated(op: Context => String)(implicit ctx: Context): String = {
    val dctx = ctx.printer match {
      case dp: Printer => ctx
      case _ => ctx.fresh.setPrinterFn(newPrinter)
    }
    val res = op(dctx)
    dctx.printer match {
      case dp: Printer if dp.disambiguated() => op(dctx)
      case _ => res
    }
  }
}