aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/printing/Disambiguation.scala
blob: aa3fae2dee883724248d77d0902c84480fc637fb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                           
                                               



                         
                                                                                   










                                                                                             




                                                                                          






































                                                                                                             
                                                  






                                                        
 
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 => RefinedPrinter = {
    val state = new State
    new Printer(state)(_)
  }

  private 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
    }
  }
}