
scala.tools.nsc.interpreter.Imports.scala Maven / Gradle / Ivy
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package interpreter
import scala.collection.mutable
trait Imports {
self: IMain =>
import global._
import definitions.{ ScalaPackage, JavaLangPackage, PredefModule }
import memberHandlers._
/** Synthetic import handlers for the language defined imports. */
private def makeWildcardImportHandler(sym: Symbol): ImportHandler = {
val hd :: tl = sym.fullName.split('.').toList map newTermName
val tree = Import(
tl.foldLeft(Ident(hd): Tree)((x, y) => Select(x, y)),
ImportSelector.wildList
)
tree setSymbol sym
new ImportHandler(tree)
}
/** Symbols whose contents are language-defined to be imported. */
def languageWildcardSyms: List[Symbol] = List(JavaLangPackage, ScalaPackage, PredefModule)
def languageWildcardHandlers = languageWildcardSyms map makeWildcardImportHandler
def allImportedNames = importHandlers flatMap (_.importedNames)
/** Types which have been wildcard imported, such as:
* val x = "abc" ; import x._ // type java.lang.String
* import java.lang.String._ // object java.lang.String
*
* Used by tab completion.
*
* XXX right now this gets import x._ and import java.lang.String._,
* but doesn't figure out import String._. There's a lot of ad hoc
* scope twiddling which should be swept away in favor of digging
* into the compiler scopes.
*/
def sessionWildcards: List[Type] = {
importHandlers filter (_.importsWildcard) map (_.targetType) distinct
}
def languageSymbols = languageWildcardSyms flatMap membersAtPickler
def sessionImportedSymbols = importHandlers flatMap (_.importedSymbols)
def importedSymbols = languageSymbols ++ sessionImportedSymbols
def importedTermSymbols = importedSymbols collect { case x: TermSymbol => x }
/** Tuples of (source, imported symbols) in the order they were imported.
*/
def importedSymbolsBySource: List[(Symbol, List[Symbol])] = {
val lang = languageWildcardSyms map (sym => (sym, membersAtPickler(sym)))
val session = importHandlers filter (_.targetType != NoType) map { mh =>
(mh.targetType.typeSymbol, mh.importedSymbols)
}
lang ++ session
}
def implicitSymbolsBySource: List[(Symbol, List[Symbol])] = {
importedSymbolsBySource map {
case (k, vs) => (k, vs filter (_.isImplicit))
} filterNot (_._2.isEmpty)
}
/** Compute imports that allow definitions from previous
* requests to be visible in a new request. Returns
* three or four pieces of related code:
*
* 0. Header code fragment that should go at the beginning
* of the compilation unit, specifically, import Predef.
*
* 1. An initial code fragment that should go before
* the code of the new request.
*
* 2. A code fragment that should go after the code
* of the new request.
*
* 3. An access path which can be traversed to access
* any bindings inside code wrapped by #1 and #2 .
*
* The argument is a set of Names that need to be imported.
*
* Limitations: This method is not as precise as it could be.
* (1) It does not process wildcard imports to see what exactly
* they import.
* (2) If it imports any names from a request, it imports all
* of them, which is not really necessary.
* (3) It imports multiple same-named implicits, but only the
* last one imported is actually usable.
*/
case class ComputedImports(header: String, prepend: String, append: String, access: String)
protected def importsCode(wanted: Set[Name], wrapper: Request#Wrapper, definesClass: Boolean, generousImports: Boolean): ComputedImports = {
val header, code, trailingBraces, accessPath = new StringBuilder
val currentImps = mutable.HashSet[Name]()
var predefEscapes = false // only emit predef import header if name not resolved in history, loosely
/** Narrow down the list of requests from which imports
* should be taken. Removes requests which cannot contribute
* useful imports for the specified set of wanted names.
*/
case class ReqAndHandler(req: Request, handler: MemberHandler)
def reqsToUse: List[ReqAndHandler] = {
/** Loop through a list of MemberHandlers and select which ones to keep.
* 'wanted' is the set of names that need to be imported.
*/
def select(reqs: List[ReqAndHandler], wanted: Set[Name]): List[ReqAndHandler] = {
// Single symbol imports might be implicits! See bug #1752. Rather than
// try to finesse this, we will mimic all imports for now.
def keepHandler(handler: MemberHandler) = handler match {
// While defining classes in class based mode - implicits are not needed.
case h: ImportHandler if isClassBased && definesClass => h.importedNames.exists(x => wanted.contains(x))
case _: ImportHandler => true
case x if generousImports => x.definesImplicit || (x.definedNames exists (d => wanted.exists(w => d.startsWith(w))))
case x => x.definesImplicit || (x.definedNames exists wanted)
}
reqs match {
case Nil => predefEscapes = wanted contains PredefModule.name ; Nil
case rh :: rest if !keepHandler(rh.handler) => select(rest, wanted)
case rh :: rest =>
import rh.handler._
val augment = rh match {
case ReqAndHandler(_, _: ImportHandler) => referencedNames // for "import a.b", add "a" to names to be resolved
case _ => Nil
}
val newWanted = wanted ++ augment -- definedNames -- importedNames
rh :: select(rest, newWanted)
}
}
/** Flatten the handlers out and pair each with the original request */
select(allReqAndHandlers reverseMap { case (r, h) => ReqAndHandler(r, h) }, wanted).reverse
}
// add code for a new object to hold some imports
def addWrapper() {
import nme.{ INTERPRETER_IMPORT_WRAPPER => iw }
code append (wrapper.prewrap format iw)
trailingBraces append wrapper.postwrap
accessPath append s".$iw"
currentImps.clear()
}
def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper()
def wrapBeforeAndAfter[T](op: => T): T = {
addWrapper()
try op finally addWrapper()
}
// imports from Predef are relocated to the template header to allow hiding.
def checkHeader(h: ImportHandler) = h.referencedNames contains PredefModule.name
// loop through previous requests, adding imports for each one
wrapBeforeAndAfter {
// Reusing a single temporary value when import from a line with multiple definitions.
val tempValLines = mutable.Set[Int]()
for (ReqAndHandler(req, handler) <- reqsToUse) {
val objName = req.lineRep.readPathInstance
if (isReplTrace)
code.append(ss"// $objName definedNames ${handler.definedNames}, curImps $currentImps\n")
handler match {
case h: ImportHandler if checkHeader(h) =>
header.clear()
header append f"${h.member}%n"
// If the user entered an import, then just use it; add an import wrapping
// level if the import might conflict with some other import
case x: ImportHandler if x.importsWildcard =>
wrapBeforeAndAfter(code append (x.member + "\n"))
case x: ImportHandler =>
maybeWrap(x.importedNames: _*)
code append (x.member + "\n")
currentImps ++= x.importedNames
case x if isClassBased =>
for (sym <- x.definedSymbols) {
maybeWrap(sym.name)
x match {
case _: ClassHandler =>
code.append(s"import ${objName}${req.accessPath}.`${sym.name}`\n")
case _ =>
val valName = s"${req.lineRep.packageName}${req.lineRep.readName}"
if (!tempValLines.contains(req.lineRep.lineId)) {
code.append(s"val $valName: ${objName}.type = $objName\n")
tempValLines += req.lineRep.lineId
}
code.append(s"import ${valName}${req.accessPath}.`${sym.name}`\n")
}
currentImps += sym.name
}
// For other requests, import each defined name.
// import them explicitly instead of with _, so that
// ambiguity errors will not be generated. Also, quote
// the name of the variable, so that we don't need to
// handle quoting keywords separately.
case x =>
for (sym <- x.definedSymbols) {
maybeWrap(sym.name)
code append s"import ${x.path}\n"
currentImps += sym.name
}
}
}
}
val computedHeader = if (predefEscapes) header.toString else ""
ComputedImports(computedHeader, code.toString, trailingBraces.toString, accessPath.toString)
}
private def allReqAndHandlers =
prevRequestList flatMap (req => req.handlers map (req -> _))
private def membersAtPickler(sym: Symbol): List[Symbol] =
enteringPickler(sym.info.nonPrivateMembers.toList)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy