aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Decorators.scala
blob: c65dc5b974dddb5feaa089c2c3d9e4bf9530528e (plain) (tree)
1
2
3
4
5
6
7
8
9


                        

                         
                                                                                           

                                                   
                                                  
                
                                         
                            
 
                                                                                  

                   
                                                                    
                                                                            

                                          
                                               

   









                                                                                          


                                                                      









                                                                                   
                                      
 
                                                        
                                                                   
     
                                                                   
 
                                                    

























                                                                                      



                                                                              



                                                            
                                           








                                              






                                                                      
                                       






                                                                        
 




                                                                   





                                                            





                                                                       
   
 

                                                                                
                                                                                          

   



                                                                                 


                                                                           
     
                                                                             
                                                            
                                                                           








                                                                             
     
   
 
                                                                                  

                                                                                             
                                                                                  


                             
                                 
   
 
                                                                            
 


                                                      
 




                                                                                         
 

                                                                              
       
                                                       
                                             


                                                                             
                                                        
   

 
package dotty.tools.dotc
package core

import annotation.tailrec
import Symbols._
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printing.Showable
import util.Positions.Position, util.SourcePosition
import collection.mutable.ListBuffer
import dotty.tools.dotc.transform.TreeTransforms._
import ast.tpd._
import scala.language.implicitConversions
import printing.Formatting._

/** This object provides useful implicit decorators for types defined elsewhere */
object Decorators {

  /** Turns Strings into PreNames, adding toType/TermName methods */
  implicit class PreNamedString(val s: String) extends AnyVal with PreName {
    def toTypeName: TypeName = typeName(s)
    def toTermName: TermName = termName(s)
    def toText(printer: Printer): Text = Str(s)
  }

  implicit class StringDecorator(val s: String) extends AnyVal {
    def splitWhere(f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] = {
      def splitAt(idx: Int, doDropIndex: Boolean): Option[(String, String)] =
        if (idx == -1) None
        else Some((s.take(idx), s.drop(if (doDropIndex) idx + 1 else idx)))

      splitAt(s.indexWhere(f), doDropIndex)
    }
  }

  /** Implements a findSymbol method on iterators of Symbols that
   *  works like find but avoids Option, replacing None with NoSymbol.
   */
  implicit class SymbolIteratorDecorator(val it: Iterator[Symbol]) extends AnyVal {
    final def findSymbol(p: Symbol => Boolean): Symbol = {
      while (it.hasNext) {
        val sym = it.next
        if (p(sym)) return sym
      }
      NoSymbol
    }
  }

  final val MaxFilterRecursions = 1000

  /** Implements filterConserve, zipWithConserve methods
   *  on lists that avoid duplication of list nodes where feasible.
   */
  implicit class ListDecorator[T](val xs: List[T]) extends AnyVal {

    final def mapconserve[U](f: T => U): List[U] = {
      @tailrec
      def loop(mapped: ListBuffer[U], unchanged: List[U], pending: List[T]): List[U] =
        if (pending.isEmpty) {
          if (mapped eq null) unchanged
          else mapped.prependToList(unchanged)
        } else {
          val head0 = pending.head
          val head1 = f(head0)

          if (head1.asInstanceOf[AnyRef] eq head0.asInstanceOf[AnyRef])
            loop(mapped, unchanged, pending.tail)
          else {
            val b = if (mapped eq null) new ListBuffer[U] else mapped
            var xc = unchanged
            while (xc ne pending) {
              b += xc.head
              xc = xc.tail
            }
            b += head1
            val tail0 = pending.tail
            loop(b, tail0.asInstanceOf[List[U]], tail0)
          }
        }
      loop(null, xs.asInstanceOf[List[U]], xs)
    }

    /** Like `xs filter p` but returns list `xs` itself  - instead of a copy -
     *  if `p` is true for all elements and `xs` is not longer
     *  than `MaxFilterRecursions`.
     */
    def filterConserve(p: T => Boolean): List[T] = {
      def loop(xs: List[T], nrec: Int): List[T] = xs match {
        case Nil => xs
        case x :: xs1 =>
          if (nrec < MaxFilterRecursions) {
            val ys1 = loop(xs1, nrec + 1)
            if (p(x))
              if (ys1 eq xs1) xs else x :: ys1
            else
              ys1
          } else xs filter p
      }
      loop(xs, 0)
    }

    /** Like `(xs, ys).zipped.map(f)`, but returns list `xs` itself
     *  - instead of a copy - if function `f` maps all elements of
     *  `xs` to themselves. Also, it is required that `ys` is at least
     *  as long as `xs`.
     */
    def zipWithConserve[U](ys: List[U])(f: (T, U) => T): List[T] =
      if (xs.isEmpty || ys.isEmpty) Nil
      else {
        val x1 = f(xs.head, ys.head)
        val xs1 = xs.tail.zipWithConserve(ys.tail)(f)
        if ((x1.asInstanceOf[AnyRef] eq xs.head.asInstanceOf[AnyRef]) &&
            (xs1 eq xs.tail)) xs
        else x1 :: xs1
      }

    def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match {
      case Nil => z
      case x :: xs1 => op(x, xs1.foldRightBN(z)(op))
    }

    final def hasSameLengthAs[U](ys: List[U]): Boolean = {
      @tailrec def loop(xs: List[T], ys: List[U]): Boolean =
        if (xs.isEmpty) ys.isEmpty
        else ys.nonEmpty && loop(xs.tail, ys.tail)
      loop(xs, ys)
    }

    /** Union on lists seen as sets */
    def | (ys: List[T]): List[T] = xs ++ (ys filterNot (xs contains _))

    /** Intersection on lists seen as sets */
    def & (ys: List[T]): List[T] = xs filter (ys contains _)
  }

  implicit class ListOfListDecorator[T](val xss: List[List[T]]) extends AnyVal {
    def nestedMap[U](f: T => U): List[List[U]] = xss map (_ map f)
    def nestedMapconserve[U](f: T => U): List[List[U]] = xss mapconserve (_ mapconserve f)
  }

  implicit class TextToString(val text: Text) extends AnyVal {
    def show(implicit ctx: Context) = text.mkString(ctx.settings.pageWidth.value)
  }

  /** Test whether a list of strings representing phases contains
   *  a given phase. See [[config.CompilerCommand#explainAdvanced]] for the
   *  exact meaning of "contains" here.
   */
  implicit class PhaseListDecorator(val names: List[String]) extends AnyVal {
    def containsPhase(phase: Phase): Boolean = phase match {
      case phase: TreeTransformer => phase.miniPhases.exists(containsPhase)
      case _ =>
        names exists { name =>
          name == "all" || {
            val strippedName = name.stripSuffix("+")
            val logNextPhase = name ne strippedName
            phase.phaseName.startsWith(strippedName) ||
              (logNextPhase && phase.prev.phaseName.startsWith(strippedName))
          }
        }
    }
  }

  implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition = {
    def recur(inlinedCalls: List[Tree], pos: Position): SourcePosition = inlinedCalls match {
      case inlinedCall :: rest =>
        sourceFile(inlinedCall).atPos(pos).withOuter(recur(rest, inlinedCall.pos))
      case empty =>
        ctx.source.atPos(pos)
    }
    recur(enclosingInlineds, pos)
  }

  implicit class StringInterpolators(val sc: StringContext) extends AnyVal {

    /** General purpose string formatting */
    def i(args: Any*)(implicit ctx: Context): String =
      new StringFormatter(sc).assemble(args)

    /** Formatting for error messages: Like `i` but suppress follow-on
     *  error messages after the first one if some of their arguments are "non-sensical".
     */
    def em(args: Any*)(implicit ctx: Context): String =
      new ErrorMessageFormatter(sc).assemble(args)

    /** Formatting with added explanations: Like `em`, but add explanations to
     *  give more info about type variables and to disambiguate where needed.
     */
    def ex(args: Any*)(implicit ctx: Context): String =
      explained(implicit ctx => em(args: _*))

    /** Formatter that adds syntax highlighting to all interpolated values */
    def hl(args: Any*)(implicit ctx: Context): String =
      new SyntaxFormatter(sc).assemble(args).stripMargin
  }
}