/
BrowsingLoaders.scala
139 lines (127 loc) · 4.82 KB
/
BrowsingLoaders.scala
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
138
139
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.nsc
package symtab
import scala.tools.nsc.io.AbstractFile
/** A subclass of SymbolLoaders that implements browsing behavior.
* This class should be used whenever file dependencies and recompile sets
* are managed automatically.
*/
abstract class BrowsingLoaders extends GlobalSymbolLoaders {
val global: Global
import global._
import syntaxAnalyzer.{OutlineParser, MalformedInput}
/** In browse mode, it can happen that an encountered symbol is already
* present. For instance, if the source file has a name different from
* the classes and objects it contains, the symbol loader will always
* reparse the source file. The symbols it encounters might already be loaded
* as class files. In this case we return the one which has a sourcefile
* (and the other has not), and issue an error if both have sourcefiles.
*/
override protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = {
completer.sourcefile match {
case Some(src) =>
(if (member.isModule) member.moduleClass else member).associatedFile = src
case _ =>
}
val decls = owner.info.decls
val existing = decls.lookup(member.name)
if (existing == NoSymbol) {
decls enter member
member
} else {
val existingSourceFile = existing.sourceFile
if (existingSourceFile == null) {
decls unlink existing
decls enter member
member
} else {
val memberSourceFile = member.sourceFile
if (memberSourceFile != null) {
if (existingSourceFile != memberSourceFile)
error(""+member+"is defined twice,"+
"\n in "+existingSourceFile+
"\n and also in "+memberSourceFile)
}
existing
}
}
}
/** Browse the top-level of given abstract file `src` and enter
* any encountered top-level classes and modules in `root`
*/
def browseTopLevel(root: Symbol, src: AbstractFile): Unit = {
class BrowserTraverser extends Traverser {
var packagePrefix = ""
var entered = 0
def addPackagePrefix(pkg: Tree): Unit = pkg match {
case Select(pre, name) =>
addPackagePrefix(pre)
packagePrefix += ("." + name)
case Ident(name) =>
if (name != nme.EMPTY_PACKAGE_NAME) { // mirrors logic in Namers, see createPackageSymbol
if (packagePrefix.length != 0) packagePrefix += "."
packagePrefix += name
}
case _ =>
throw new MalformedInput(pkg.pos.point, "illegal tree node in package prefix: "+pkg)
}
private def inPackagePrefix(pkg: Tree)(op: => Unit): Unit = {
val oldPrefix = packagePrefix
addPackagePrefix(pkg)
op
packagePrefix = oldPrefix
}
override def traverse(tree: Tree): Unit = tree match {
case PackageDef(pkg, body) =>
inPackagePrefix(pkg) { body foreach traverse }
case ClassDef(_, name, _, _) =>
if (packagePrefix == root.fullName) {
enterClass(root, name.toString, new SourcefileLoader(src))
entered += 1
} else log("prefixes differ: "+packagePrefix+","+root.fullName)
case ModuleDef(_, name, _) =>
if (packagePrefix == root.fullName) {
val module = enterModule(root, name.toString, new SourcefileLoader(src))
entered += 1
if (name == nme.PACKAGEkw) {
log("open package module: "+module)
openPackageModule(module, root)
}
} else log("prefixes differ: "+packagePrefix+","+root.fullName)
case _ =>
}
}
// System.out.println("Browsing "+src)
val source = getSourceFile(src) // this uses the current encoding
val body = new OutlineParser(source).parse()
// System.out.println(body)
val browser = new BrowserTraverser
browser.traverse(body)
if (browser.entered == 0)
warning("No classes or objects found in "+source+" that go in "+root)
}
/** Enter top-level symbols from a source file
*/
override def enterToplevelsFromSource(root: Symbol, name: String, src: AbstractFile): Unit = {
try {
if (root.isEffectiveRoot || !src.name.endsWith(".scala")) // RootClass or EmptyPackageClass
super.enterToplevelsFromSource(root, name, src)
else
browseTopLevel(root, src)
} catch {
case ex: syntaxAnalyzer.MalformedInput =>
log(s"[$src] caught malformed input exception at offset ${ex.offset}: ${ex.msg}")
super.enterToplevelsFromSource(root, name, src)
}
}
}