Skip to content

Commit c7d9464

Browse files
committed
Fix #7783: Loosen check for classes in wrong directory
The previous check demanded that the package prefix of a file corresponds one-to-one with the directory name. This makes it illegal to have classes in nested package clauses inside a file, which seems unreasonable. We now only require that the file path is a prefix of the package path.
1 parent e45f03e commit c7d9464

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import parsing.JavaParsers.OutlineJavaParser
1919
import parsing.Parsers.OutlineParser
2020
import reporting.trace
2121
import ast.desugar
22+
import collection.mutable.ListBuffer
2223

2324
object SymbolLoaders {
2425
import ast.untpd._
@@ -105,8 +106,8 @@ object SymbolLoaders {
105106
}
106107

107108
/** Enter all toplevel classes and objects in file `src` into package `owner`, provided
108-
* they are in the right package. Issue a warning if a class or object is in the wrong
109-
* package, i.e. if the file path differs from the declared package clause.
109+
* they are in the right directory. Issue a warning if a class or object is in the wrong
110+
* directory, i.e. if the file path is not a prefix of the declared package clause.
110111
*
111112
* All entered symbols are given a source completer of `src` as info.
112113
*/
@@ -115,22 +116,25 @@ object SymbolLoaders {
115116
scope: Scope = EmptyScope)(implicit ctx: Context): Unit =
116117
if src.exists && !src.isDirectory
117118
val completer = new SourcefileLoader(src)
118-
val filePath = owner.ownersIterator.takeWhile(!_.isRoot).map(_.name.toTermName).toList
119-
120-
def addPrefix(pid: RefTree, path: List[TermName]): List[TermName] = pid match {
121-
case Ident(name: TermName) => name :: path
122-
case Select(qual: RefTree, name: TermName) => name :: addPrefix(qual, path)
123-
case _ => path
124-
}
119+
val filePath = owner.ownersIterator.takeWhile(!_.isRoot).map(_.name.toTermName).toList.reverse
125120

126121
def enterScanned(unit: CompilationUnit)(implicit ctx: Context) = {
122+
val path = ListBuffer[TermName]()
123+
124+
def addSuffix(pid: RefTree): Unit = pid match
125+
case Ident(name: TermName) =>
126+
path += name
127+
case Select(qual: RefTree, name: TermName) =>
128+
addSuffix(qual)
129+
path += name
130+
case _ =>
127131

128-
def checkPathMatches(path: List[TermName], what: String, tree: NameTree): Boolean = {
129-
val ok = filePath == path
132+
def checkPathMatches(what: String, tree: NameTree): Boolean = {
133+
val ok = path.startsWith(filePath)
130134
if (!ok)
131135
ctx.warning(i"""$what ${tree.name} is in the wrong directory.
132-
|It was declared to be in package ${path.reverse.mkString(".")}
133-
|But it is found in directory ${filePath.reverse.mkString(File.separator)}""",
136+
|It was declared to be in package ${path.mkString(".")}
137+
|But it is found in directory ${filePath.mkString(File.separator)}""",
134138
tree.sourcePos.focus)
135139
ok
136140
}
@@ -144,25 +148,26 @@ object SymbolLoaders {
144148
case _ =>
145149
tree
146150

147-
def traverse(tree: Tree, path: List[TermName]): Unit = simpleDesugar(tree) match {
151+
def traverse(tree: Tree): Unit = simpleDesugar(tree) match {
148152
case tree @ PackageDef(pid, body) =>
149-
val path1 = addPrefix(pid, path)
150-
for (stat <- body) traverse(stat, path1)
153+
val size = path.size
154+
addSuffix(pid)
155+
for stat <- body do traverse(stat)
156+
path.takeInPlace(size)
151157
case tree: TypeDef if tree.isClassDef =>
152-
if (checkPathMatches(path, "class", tree))
158+
if (checkPathMatches("class", tree))
153159
// It might be a case class or implicit class,
154160
// so enter class and module to be on the safe side
155161
enterClassAndModule(owner, tree.name, completer, scope = scope)
156162
case tree: ModuleDef =>
157-
if (checkPathMatches(path, "object", tree))
163+
if (checkPathMatches("object", tree))
158164
enterModule(owner, tree.name, completer, scope = scope)
159165
case _ =>
160166
}
161167

162168
traverse(
163169
if (unit.isJava) new OutlineJavaParser(unit.source).parse()
164-
else new OutlineParser(unit.source).parse(),
165-
Nil)
170+
else new OutlineParser(unit.source).parse())
166171
}
167172

168173
val unit = CompilationUnit(ctx.getSource(src.path))

0 commit comments

Comments
 (0)