Skip to content

Commit 5a695a5

Browse files
authored
Merge pull request #7678 from dotty-staging/sourcepath-toplevel
Improve sourcepath handling
2 parents 3a7dfd2 + 1907b49 commit 5a695a5

File tree

20 files changed

+126
-85
lines changed

20 files changed

+126
-85
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,29 @@ object desugar {
12201220
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts)
12211221
}
12221222

1223+
private def isTopLevelDef(stat: Tree)(given Context): Boolean = stat match
1224+
case _: ValDef | _: PatDef | _: DefDef | _: Export => true
1225+
case stat: ModuleDef =>
1226+
stat.mods.isOneOf(GivenOrImplicit)
1227+
case stat: TypeDef =>
1228+
!stat.isClassDef || stat.mods.isOneOf(GivenOrImplicit)
1229+
case _ =>
1230+
false
1231+
1232+
/** Does this package contains at least one top-level definition
1233+
* that will require a wrapping object ?
1234+
*/
1235+
def hasTopLevelDef(pdef: PackageDef)(given Context): Boolean =
1236+
pdef.stats.exists(isTopLevelDef)
1237+
1238+
/** Assuming `src` contains top-level definition, returns the name that should
1239+
* be using for the package object that will wrap them.
1240+
*/
1241+
def packageObjectName(src: SourceFile): TermName =
1242+
val fileName = src.file.name
1243+
val sourceName = fileName.take(fileName.lastIndexOf('.'))
1244+
(sourceName ++ str.TOPLEVEL_SUFFIX).toTermName
1245+
12231246
/** Group all definitions that can't be at the toplevel in
12241247
* an object named `<source>$package` where `<source>` is the name of the source file.
12251248
* Definitions that can't be at the toplevel are:
@@ -1231,26 +1254,22 @@ object desugar {
12311254
* (i.e. objects having the same name as a wrapped type)
12321255
*/
12331256
def packageDef(pdef: PackageDef)(implicit ctx: Context): PackageDef = {
1234-
def isWrappedType(stat: TypeDef): Boolean =
1235-
!stat.isClassDef || stat.mods.isOneOf(GivenOrImplicit)
12361257
val wrappedTypeNames = pdef.stats.collect {
1237-
case stat: TypeDef if isWrappedType(stat) => stat.name
1238-
}
1239-
def needsObject(stat: Tree) = stat match {
1240-
case _: ValDef | _: PatDef | _: DefDef | _: Export => true
1241-
case stat: ModuleDef =>
1242-
stat.mods.isOneOf(GivenOrImplicit) ||
1243-
wrappedTypeNames.contains(stat.name.stripModuleClassSuffix.toTypeName)
1244-
case stat: TypeDef => isWrappedType(stat)
1245-
case _ => false
1258+
case stat: TypeDef if isTopLevelDef(stat) => stat.name
12461259
}
1247-
val (nestedStats, topStats) = pdef.stats.partition(needsObject)
1260+
def inPackageObject(stat: Tree) =
1261+
isTopLevelDef(stat) || {
1262+
stat match
1263+
case stat: ModuleDef =>
1264+
wrappedTypeNames.contains(stat.name.stripModuleClassSuffix.toTypeName)
1265+
case _ =>
1266+
false
1267+
}
1268+
val (nestedStats, topStats) = pdef.stats.partition(inPackageObject)
12481269
if (nestedStats.isEmpty) pdef
12491270
else {
1250-
var fileName = ctx.source.file.name
1251-
val sourceName = fileName.take(fileName.lastIndexOf('.'))
1252-
val groupName = (sourceName ++ str.TOPLEVEL_SUFFIX).toTermName.asSimpleName
1253-
val grouped = ModuleDef(groupName, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
1271+
val name = packageObjectName(ctx.source)
1272+
val grouped = ModuleDef(name, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
12541273
cpy.PackageDef(pdef)(pdef.pid, topStats :+ grouped)
12551274
}
12561275
}

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class ScalaSettings extends Settings.SettingGroup {
1717
val javabootclasspath: Setting[String] = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath) withAbbreviation "--java-boot-class-path"
1818
val javaextdirs: Setting[String] = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs) withAbbreviation "--java-extension-directories"
1919
val sourcepath: Setting[String] = PathSetting("-sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath) withAbbreviation "--source-path"
20-
val scansource: Setting[Boolean] = BooleanSetting("-scansource", "Scan source files to locate classes for which class-name != file-name") withAbbreviation "--scan-source"
2120

2221
val classpath: Setting[String] = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp" withAbbreviation "--class-path"
2322
val outputDir: Setting[AbstractFile] = OutputSetting("-d", "directory|jar", "destination for generated classfiles.",

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

Lines changed: 46 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import ast.Trees._
1818
import parsing.JavaParsers.OutlineJavaParser
1919
import parsing.Parsers.OutlineParser
2020
import reporting.trace
21+
import ast.desugar.{ packageObjectName, hasTopLevelDef }
2122

2223
object SymbolLoaders {
2324
import ast.untpd._
@@ -31,7 +32,7 @@ object SymbolLoaders {
3132
owner: Symbol, member: Symbol,
3233
completer: SymbolLoader, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = {
3334
val comesFromScan =
34-
completer.isInstanceOf[SourcefileLoader] && ctx.settings.scansource.value
35+
completer.isInstanceOf[SourcefileLoader]
3536
assert(comesFromScan || scope.lookup(member.name) == NoSymbol,
3637
s"${owner.fullName}.${member.name} already has a symbol")
3738
owner.asClass.enter(member, scope)
@@ -103,69 +104,62 @@ object SymbolLoaders {
103104
scope = scope)
104105
}
105106

106-
/** If setting -scansource is set:
107-
* 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.
110-
* If -scansource is not set:
111-
* Enter class and module with given `name` into scope of `owner`.
107+
/** 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.
112110
*
113111
* All entered symbols are given a source completer of `src` as info.
114112
*/
115113
def enterToplevelsFromSource(
116114
owner: Symbol, name: PreName, src: AbstractFile,
117-
scope: Scope = EmptyScope)(implicit ctx: Context): Unit = {
115+
scope: Scope = EmptyScope)(implicit ctx: Context): Unit =
116+
if src.exists && !src.isDirectory
117+
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+
}
118125

119-
val completer = new SourcefileLoader(src)
120-
if (ctx.settings.scansource.value && ctx.run != null) {
121-
if (src.exists && !src.isDirectory) {
122-
val filePath = owner.ownersIterator.takeWhile(!_.isRoot).map(_.name.toTermName).toList
126+
def enterScanned(unit: CompilationUnit)(implicit ctx: Context) = {
123127

124-
def addPrefix(pid: RefTree, path: List[TermName]): List[TermName] = pid match {
125-
case Ident(name: TermName) => name :: path
126-
case Select(qual: RefTree, name: TermName) => name :: addPrefix(qual, path)
127-
case _ => path
128+
def checkPathMatches(path: List[TermName], what: String, tree: NameTree): Boolean = {
129+
val ok = filePath == path
130+
if (!ok)
131+
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)}""",
134+
tree.sourcePos.focus)
135+
ok
128136
}
129137

130-
def enterScanned(unit: CompilationUnit)(implicit ctx: Context) = {
131-
132-
def checkPathMatches(path: List[TermName], what: String, tree: MemberDef): Boolean = {
133-
val ok = filePath == path
134-
if (!ok)
135-
ctx.warning(i"""$what ${tree.name} is in the wrong directory.
136-
|It was declared to be in package ${path.reverse.mkString(".")}
137-
|But it is found in directory ${filePath.reverse.mkString(File.separator)}""",
138-
tree.sourcePos)
139-
ok
140-
}
141-
142-
def traverse(tree: Tree, path: List[TermName]): Unit = tree match {
143-
case PackageDef(pid, body) =>
144-
val path1 = addPrefix(pid, path)
145-
for (stat <- body) traverse(stat, path1)
146-
case tree: TypeDef if tree.isClassDef =>
147-
if (checkPathMatches(path, "class", tree))
148-
enterClassAndModule(owner, tree.name, completer, scope = scope)
149-
// It might be a case class or implicit class,
150-
// so enter class and module to be on the safe side
151-
case tree: ModuleDef =>
152-
if (checkPathMatches(path, "object", tree))
153-
enterModule(owner, tree.name, completer, scope = scope)
154-
case _ =>
155-
}
156-
157-
traverse(
158-
if (unit.isJava) new OutlineJavaParser(unit.source).parse()
159-
else new OutlineParser(unit.source).parse(),
160-
Nil)
138+
def traverse(tree: Tree, path: List[TermName]): Unit = tree match {
139+
case tree @ PackageDef(pid, body) =>
140+
val path1 = addPrefix(pid, path)
141+
if hasTopLevelDef(tree) && checkPathMatches(path1, "package", pid)
142+
enterModule(owner, packageObjectName(unit.source), completer, scope = scope)
143+
for (stat <- body) traverse(stat, path1)
144+
case tree: TypeDef if tree.isClassDef =>
145+
if (checkPathMatches(path, "class", tree))
146+
// It might be a case class or implicit class,
147+
// so enter class and module to be on the safe side
148+
enterClassAndModule(owner, tree.name, completer, scope = scope)
149+
case tree: ModuleDef =>
150+
if (checkPathMatches(path, "object", tree))
151+
enterModule(owner, tree.name, completer, scope = scope)
152+
case _ =>
161153
}
162154

163-
val unit = CompilationUnit(ctx.getSource(src.path))
164-
enterScanned(unit)(ctx.run.runContext.fresh.setCompilationUnit(unit))
155+
traverse(
156+
if (unit.isJava) new OutlineJavaParser(unit.source).parse()
157+
else new OutlineParser(unit.source).parse(),
158+
Nil)
165159
}
166-
}
167-
else enterClassAndModule(owner, name, completer, scope = scope)
168-
}
160+
161+
val unit = CompilationUnit(ctx.getSource(src.path))
162+
enterScanned(unit)(ctx.fresh.setCompilationUnit(unit))
169163

170164
/** The package objects of scala and scala.reflect should always
171165
* be loaded in binary if classfiles are available, even if sourcefiles

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ class CompilationTests extends ParallelTesting {
3939
compileFile("tests/pos-scala2/rewrites.scala", scala2CompatMode.and("-rewrite")).copyToTarget(),
4040
compileFile("tests/pos-special/utf8encoded.scala", explicitUTF8),
4141
compileFile("tests/pos-special/utf16encoded.scala", explicitUTF16),
42-
compileFile("tests/pos-special/completeFromSource/Test.scala", defaultOptions.and("-sourcepath", "tests/pos-special")),
43-
compileFile("tests/pos-special/completeFromSource/Test2.scala", defaultOptions.and("-sourcepath", "tests/pos-special")),
44-
compileFile("tests/pos-special/completeFromSource/Test3.scala", defaultOptions.and("-sourcepath", "tests/pos-special", "-scansource")),
45-
compileFile("tests/pos-special/completeFromSource/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special", "-scansource")),
42+
compileFile("tests/pos-special/sourcepath/outer/Test.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
43+
compileFile("tests/pos-special/sourcepath/outer/Test2.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
44+
compileFile("tests/pos-special/sourcepath/outer/Test3.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
45+
compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
4646
compileFilesInDir("tests/pos-special/fatal-warnings", defaultOptions.and("-Xfatal-warnings", "-feature")),
4747
compileFilesInDir("tests/pos-special/spec-t5545", defaultOptions),
4848
compileFilesInDir("tests/pos-special/strawman-collections", defaultOptions),
@@ -135,7 +135,8 @@ class CompilationTests extends ParallelTesting {
135135
compileFilesInDir("tests/neg-custom-args/isInstanceOf", allowDeepSubtypes and "-Xfatal-warnings"),
136136
compileFile("tests/neg-custom-args/i3627.scala", allowDeepSubtypes),
137137
compileFile("tests/neg-custom-args/matchtype-loop.scala", allowDeepSubtypes),
138-
compileFile("tests/neg-custom-args/completeFromSource/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args", "-scansource")),
138+
compileFile("tests/neg-custom-args/sourcepath/outer/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath")),
139+
compileDir("tests/neg-custom-args/sourcepath2/hi", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath2", "-Xfatal-warnings")),
139140
compileList("duplicate source", List(
140141
"tests/neg-custom-args/toplevel-samesource/S.scala",
141142
"tests/neg-custom-args/toplevel-samesource/nested/S.scala"),

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ class DottyLanguageServer extends LanguageServer
8787
config.compilerArguments.toList
8888
.update("-d", config.classDirectory.getAbsolutePath)
8989
.update("-classpath", (config.classDirectory +: config.dependencyClasspath).mkString(File.pathSeparator))
90-
.update("-sourcepath", config.sourceDirectories.mkString(File.pathSeparator)) :+
91-
"-scansource"
90+
.update("-sourcepath", config.sourceDirectories.mkString(File.pathSeparator))
9291
myDrivers(config) = new InteractiveDriver(settings)
9392
}
9493
myDrivers

tests/neg-custom-args/completeFromSource/nested/D.java renamed to tests/neg-custom-args/sourcepath/outer/nested/D.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested;
1+
package outer.nested;
22

33
public class D {
44
public D(int i) {

tests/neg-custom-args/completeFromSource/nested/Test1.scala renamed to tests/neg-custom-args/sourcepath/outer/nested/Test1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested
1+
package outer.nested
22

33
class Test4 {
44

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Error: tests/neg-custom-args/sourcepath2/hi/A.scala:3:6 -------------------------------------------------------------
2+
3 |class Hello { // error
3+
| ^
4+
| class Hello is in the wrong directory.
5+
| It was declared to be in package <empty>
6+
| But it is found in directory hi
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Missing `package hi`
2+
3+
class Hello { // error
4+
val x: Int = 1
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package hi
2+
class B

tests/pos-special/completeFromSource/Test.scala renamed to tests/pos-special/sourcepath/outer/Test.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource
1+
package outer
22

33
class Test extends nested.A(22) {
44

tests/pos-special/completeFromSource/Test2.scala renamed to tests/pos-special/sourcepath/outer/Test2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource
1+
package outer
22
import nested._
33

44
class Test2 extends A(22) {

tests/pos-special/completeFromSource/Test3.scala renamed to tests/pos-special/sourcepath/outer/Test3.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource
1+
package outer
22
import nested._
33

44
class Test3 {

tests/pos-special/completeFromSource/nested/A.scala renamed to tests/pos-special/sourcepath/outer/nested/A.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested
1+
package outer.nested
22

33
class A(y: Int) {
44

tests/pos-special/completeFromSource/nested/BC.scala renamed to tests/pos-special/sourcepath/outer/nested/BC.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested
1+
package outer.nested
22

33

44
case class B(x: Int) extends A(x)

tests/pos-special/completeFromSource/nested/D.java renamed to tests/pos-special/sourcepath/outer/nested/D.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested;
1+
package outer.nested;
22

33
public class D {
44
public D(int i) {

tests/pos-special/completeFromSource/nested/Test4.scala renamed to tests/pos-special/sourcepath/outer/nested/Test4.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package completeFromSource.nested
1+
package outer.nested
22

33
class Test4 {
44

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package outer
2+
package nested
3+
4+
val one: Int = 1
5+
6+
type Hi = Int
7+
object Hi {
8+
def hi: Hi = 2
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package outer
2+
import nested._
3+
4+
object toplevel2 {
5+
val a: Int = one
6+
val b: Hi = Hi.hi
7+
}

0 commit comments

Comments
 (0)