aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
blob: 3bee0bbe84ee91fc359f292e91393992d26a71a7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                   
                       
                  
             
                                   

                                                                                  
                                     
 

                                                                  
                                                                 
                                                    


                                                                                    


   
                                     





                                                                                                  
   
                                                                          
                                                                                                    
 

                                                                       
                                    
                        
                       




                                     
 




                                                                 










                                                                                       
                                                                  













                                                                   
                                                                              
                              
                                                                              
                                                 
                              













                                                               
                                                                 
                  
                           




                                                                        

                                                                            
                                                                           
   
 


                                                                                            


                                                                              
                                                                                       


                                                                                                
     










                                                                                 
     
                
   
                                            
 
                                                     
 
package dotty.tools
package dotc
package typer

import ast.{tpd, untpd}
import ast.Trees._
import core._
import printing.{Printer, Showable}
import util.SimpleMap
import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._
import Decorators.StringInterpolators

object ImportInfo {
  /** The import info for a root import from given symbol `sym` */
  def rootImport(refFn: () => TermRef)(implicit ctx: Context) = {
    val selectors = untpd.Ident(nme.WILDCARD) :: Nil
    def expr(implicit ctx: Context) = tpd.Ident(refFn())
    def imp(implicit ctx: Context) = tpd.Import(expr, selectors)
    new ImportInfo(implicit ctx => imp.symbol, selectors, None, isRootImport = true)
  }
}

/** Info relating to an import clause
 *  @param   sym          The import symbol defined by the clause
 *  @param   selectors    The selector clauses
 *  @param   symNameOpt   Optionally, the name of the import symbol. None for root imports.
 *                        Defined for all explicit imports from ident or select nodes.
 *  @param   isRootImport true if this is one of the implicit imports of scala, java.lang,
 *                        scala.Predef or dotty.DottyPredef in the start context, false otherwise.
 */
class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
                 symNameOpt: Option[TermName], val isRootImport: Boolean = false) extends Showable {

  // Dotty deviation: we cannot use a lazy val here for the same reason
  // that we cannot use one for `DottyPredefModuleRef`.
  def sym(implicit ctx: Context) = {
    if (mySym == null) {
      mySym = symf(ctx)
      assert(mySym != null)
    }
    mySym
  }
  private[this] var mySym: Symbol = _

  /** The (TermRef) type of the qualifier of the import clause */
  def site(implicit ctx: Context): Type = {
    val ImportType(expr) = sym.info
    expr.tpe
  }

  /** The names that are excluded from any wildcard import */
  def excluded: Set[TermName] = { ensureInitialized(); myExcluded }

  /** A mapping from renamed to original names */
  def reverseMapping: SimpleMap[TermName, TermName] = { ensureInitialized(); myMapped }

  /** The original names imported by-name before renaming */
  def originals: Set[TermName] = { ensureInitialized(); myOriginals }

  /** Does the import clause end with wildcard? */
  def isWildcardImport = { ensureInitialized(); myWildcardImport }

  private var myExcluded: Set[TermName] = null
  private var myMapped: SimpleMap[TermName, TermName] = null
  private var myOriginals: Set[TermName] = null
  private var myWildcardImport: Boolean = false

  /** Compute info relating to the selector list */
  private def ensureInitialized(): Unit = if (myExcluded == null) {
    myExcluded = Set()
    myMapped = SimpleMap.Empty
    myOriginals = Set()
    def recur(sels: List[untpd.Tree]): Unit = sels match {
      case sel :: sels1 =>
        sel match {
          case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) =>
            myExcluded += name
          case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) =>
            myMapped = myMapped.updated(to, from)
            myExcluded += from
            myOriginals += from
          case Ident(nme.WILDCARD) =>
            myWildcardImport = true
          case Ident(name: TermName) =>
            myMapped = myMapped.updated(name, name)
            myOriginals += name
        }
        recur(sels1)
      case nil =>
    }
    recur(selectors)
  }

  /** The implicit references imported by this import clause */
  def importedImplicits(implicit ctx: Context): List[TermRef] = {
    val pre = site
    if (isWildcardImport) {
      val refs = pre.implicitMembers
      if (excluded.isEmpty) refs
      else refs filterNot (ref => excluded contains ref.name.toTermName)
    } else
      for {
        renamed <- reverseMapping.keys
        denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit)
      } yield TermRef.withSigAndDenot(pre, renamed, denot.signature, denot)
  }

  /** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden.
   *  Note: this computation needs to work even for un-initialized import infos, and
   *  is not allowed to force initialization.
   *
   *  TODO: Once we have fully bootstrapped, I would prefer if we expressed
   *  unimport with an `override` modifier, and generalized it to all imports.
   *  I believe this would be more transparent than the current set of conditions. E.g.
   *
   *      override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String +
   *      override import java.lang.{}                                   // disables all imports
   */
  def unimported(implicit ctx: Context): Symbol = {
    if (myUnimported == null) {
      lazy val sym = site.termSymbol
      def maybeShadowsRoot = symNameOpt match {
        case Some(symName) => defn.ShadowableImportNames.contains(symName)
        case None => false
      }
      myUnimported =
        if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym
        else NoSymbol
      assert(myUnimported != null)
    }
    myUnimported
  }
  private[this] var myUnimported: Symbol = _

  def toText(printer: Printer) = printer.toText(this)
}