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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
package dotty.tools
package dotc
package typer
import ast.{tpd, untpd}
import ast.Trees._
import core._
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 = tpd.Ident(refFn())
def imp = tpd.Import(expr, selectors)
new ImportInfo(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: => Symbol, val selectors: List[untpd.Tree],
symNameOpt: Option[TermName], val isRootImport: Boolean = false)(implicit ctx: Context) {
// Dotty deviation: we cannot use a lazy val here for the same reason
// that we cannot use one for `DottyPredefModuleRef`.
def sym = {
if (mySym == null) {
mySym = symf
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: 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
*/
lazy val unimported: Symbol = {
lazy val sym = site.termSymbol
def maybeShadowsRoot = symNameOpt match {
case Some(symName) => defn.ShadowableImportNames.contains(symName)
case None => false
}
if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym
else NoSymbol
}
override def toString = {
val siteStr = site.show
val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr
val selectorStr = selectors match {
case Ident(name) :: Nil => name.show
case _ => "{...}"
}
i"import $exprStr.$selectorStr"
}
}
|