Skip to content

Commit 5c2a2d5

Browse files
committed
sourcepath: support top-level definitions
If a top-level definition is found in a file that is part of the sourcepath, create a symbol for the package object that will wrap the top-level definitions.
1 parent 35c0804 commit 5c2a2d5

File tree

4 files changed

+56
-18
lines changed

4 files changed

+56
-18
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/core/SymbolLoaders.scala

Lines changed: 5 additions & 2 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._
@@ -124,7 +125,7 @@ object SymbolLoaders {
124125

125126
def enterScanned(unit: CompilationUnit)(implicit ctx: Context) = {
126127

127-
def checkPathMatches(path: List[TermName], what: String, tree: MemberDef): Boolean = {
128+
def checkPathMatches(path: List[TermName], what: String, tree: NameTree): Boolean = {
128129
val ok = filePath == path
129130
if (!ok)
130131
ctx.warning(i"""$what ${tree.name} is in the wrong directory.
@@ -135,8 +136,10 @@ object SymbolLoaders {
135136
}
136137

137138
def traverse(tree: Tree, path: List[TermName]): Unit = tree match {
138-
case PackageDef(pid, body) =>
139+
case tree @ PackageDef(pid, body) =>
139140
val path1 = addPrefix(pid, path)
141+
if hasTopLevelDef(tree) && checkPathMatches(path1, "package", pid)
142+
enterModule(owner, packageObjectName(unit.source), completer, scope = scope)
140143
for (stat <- body) traverse(stat, path1)
141144
case tree: TypeDef if tree.isClassDef =>
142145
if (checkPathMatches(path, "class", tree))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package completeFromSource
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 completeFromSource
2+
import nested._
3+
4+
object toplevel2 {
5+
val a: Int = one
6+
val b: Hi = Hi.hi
7+
}

0 commit comments

Comments
 (0)