@@ -2,7 +2,7 @@ package dotty.tools
22package dotc
33package core
44
5- import java .io .IOException
5+ import java .io .{ IOException , File }
66import scala .compat .Platform .currentTime
77import dotty .tools .io .{ ClassPath , ClassRepresentation , AbstractFile }
88import classpath ._
@@ -11,8 +11,10 @@ import StdNames._, NameOps._
1111import Decorators .{PreNamedString , StringInterpolators }
1212import classfile .ClassfileParser
1313import util .Stats
14+ import Decorators ._
1415import scala .util .control .NonFatal
1516import ast .Trees ._
17+ import parsing .Parsers .OutlineParser
1618import reporting .trace
1719
1820object SymbolLoaders {
@@ -24,11 +26,15 @@ object SymbolLoaders {
2426
2527/** A base class for Symbol loaders with some overridable behavior */
2628class SymbolLoaders {
29+ import ast .untpd ._
2730
2831 protected def enterNew (
2932 owner : Symbol , member : Symbol ,
3033 completer : SymbolLoader , scope : Scope = EmptyScope )(implicit ctx : Context ): Symbol = {
31- assert(scope.lookup(member.name) == NoSymbol , s " ${owner.fullName}. ${member.name} already has a symbol " )
34+ val comesFromScan =
35+ completer.isInstanceOf [SourcefileLoader ] && ctx.settings.scansource.value
36+ assert(comesFromScan || scope.lookup(member.name) == NoSymbol ,
37+ s " ${owner.fullName}. ${member.name} already has a symbol " )
3238 owner.asClass.enter(member, scope)
3339 member
3440 }
@@ -98,16 +104,65 @@ class SymbolLoaders {
98104 scope = scope)
99105 }
100106
101- /** In batch mode: Enter class and module with given `name` into scope of `owner`
102- * and give them a source completer for given `src` as type.
103- * In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `owner`
104- * with source completer for given `src` as type.
105- * (overridden in interactive.Global).
107+ /** If setting -scansource is set:
108+ * Enter all toplevel classes and objects in file `src` into package `owner`, provided
109+ * they are in the right package. Issue a warning if a class or object is in the wrong
110+ * package, i.e. if the file path differs from the declared package clause.
111+ * If -scansource is not set:
112+ * Enter class and module with given `name` into scope of `owner`.
113+ *
114+ * All entered symbols are given a source completer of `src` as info.
106115 */
107116 def enterToplevelsFromSource (
108117 owner : Symbol , name : PreName , src : AbstractFile ,
109118 scope : Scope = EmptyScope )(implicit ctx : Context ): Unit = {
110- enterClassAndModule(owner, name, new SourcefileLoader (src), scope = scope)
119+
120+ val completer = new SourcefileLoader (src)
121+ if (ctx.settings.scansource.value) {
122+ if (src.exists && ! src.isDirectory) {
123+ val filePath = owner.ownersIterator.takeWhile(! _.isRoot).map(_.name.toTermName).toList
124+
125+ def addPrefix (pid : RefTree , path : List [TermName ]): List [TermName ] = pid match {
126+ case Ident (name : TermName ) => name :: path
127+ case Select (qual : RefTree , name : TermName ) => name :: addPrefix(qual, path)
128+ case _ => path
129+ }
130+
131+ def enterScanned (unit : CompilationUnit )(implicit ctx : Context ) = {
132+
133+ def checkPathMatches (path : List [TermName ], what : String , tree : MemberDef ): Boolean = {
134+ val ok = filePath == path
135+ if (! ok)
136+ ctx.warning(i """ $what ${tree.name} is in wrong directory.
137+ |It was declared to be in package ${path.reverse.mkString(" ." )}
138+ |But it is found in directory ${filePath.reverse.mkString(File .separator)}""" ,
139+ tree.pos)
140+ ok
141+ }
142+
143+ def traverse (tree : Tree , path : List [TermName ]): Unit = tree match {
144+ case PackageDef (pid, body) =>
145+ val path1 = addPrefix(pid, path)
146+ for (stat <- body) traverse(stat, path1)
147+ case tree : TypeDef if tree.isClassDef =>
148+ if (checkPathMatches(path, " class" , tree))
149+ enterClassAndModule(owner, tree.name, completer, scope = scope)
150+ // It might be a case class or implicit class,
151+ // so enter class and module to be on the safe side
152+ case tree : ModuleDef =>
153+ if (checkPathMatches(path, " object" , tree))
154+ enterModule(owner, tree.name, completer, scope = scope)
155+ case _ =>
156+ }
157+
158+ traverse(new OutlineParser (unit.source).parse(), Nil )
159+ }
160+
161+ val unit = new CompilationUnit (ctx.run.getSource(src.path))
162+ enterScanned(unit)(ctx.run.runContext.fresh.setCompilationUnit(unit))
163+ }
164+ }
165+ else enterClassAndModule(owner, name, completer, scope = scope)
111166 }
112167
113168 /** The package objects of scala and scala.reflect should always
0 commit comments