Skip to content

Commit f916723

Browse files
committed
Warn about encoded pkg obj names
1 parent 8c3f1a6 commit f916723

File tree

6 files changed

+64
-21
lines changed

6 files changed

+64
-21
lines changed

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

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,25 +1270,29 @@ object desugar {
12701270
else tree
12711271
}
12721272

1273-
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1274-
1275-
def check(name: Name, errSpan: Span): Unit = name match
1276-
case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) =>
1277-
report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan))
1278-
case _ =>
1279-
1280-
def loop(part: RefTree): Unit = part match
1281-
case part @ Ident(name) => check(name, part.span)
1282-
case part @ Select(qual: RefTree, name) =>
1283-
check(name, part.nameSpan)
1284-
loop(qual)
1273+
def checkSimplePackageName(name: Name, errSpan: Span, source: SourceFile, isPackageObject: Boolean)(using Context) =
1274+
if !ctx.isAfterTyper then
1275+
name match
1276+
case name: SimpleName if (isPackageObject || !errSpan.isSynthetic) && name.exists(Chars.willBeEncoded) =>
1277+
report.warning(
1278+
em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.",
1279+
source.atSpan(errSpan))
12851280
case _ =>
12861281

1282+
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1283+
def check(name: Name, errSpan: Span) = checkSimplePackageName(name, errSpan, mdef.source, isPackageObject = false)
12871284
mdef match
1288-
case pdef: PackageDef => loop(pdef.pid)
1289-
case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan)
1290-
case _ =>
1291-
end checkPackageName
1285+
case pdef: PackageDef =>
1286+
def loop(part: RefTree): Unit = part match
1287+
case part @ Ident(name) => check(name, part.span)
1288+
case part @ Select(qual: RefTree, name) =>
1289+
check(name, part.nameSpan)
1290+
loop(qual)
1291+
case _ =>
1292+
loop(pdef.pid)
1293+
case mdef: ModuleDef if mdef.mods.is(Package) =>
1294+
check(mdef.name, mdef.nameSpan)
1295+
case _ =>
12921296

12931297
/** The normalized name of `mdef`. This means
12941298
* 1. Check that the name does not redefine a Scala core class.
@@ -1859,7 +1863,7 @@ object desugar {
18591863
/** Assuming `src` contains top-level definition, returns the name that should
18601864
* be using for the package object that will wrap them.
18611865
*/
1862-
def packageObjectName(src: SourceFile): TermName =
1866+
def packageObjectName(src: SourceFile, srcPos: SrcPos)(using Context): TermName =
18631867
val fileName = src.file.name
18641868
val sourceName = fileName.take(fileName.lastIndexOf('.'))
18651869
(sourceName ++ str.TOPLEVEL_SUFFIX).toTermName
@@ -1890,7 +1894,7 @@ object desugar {
18901894
val (nestedStats, topStats) = pdef.stats.partition(inPackageObject)
18911895
if (nestedStats.isEmpty) pdef
18921896
else {
1893-
val name = packageObjectName(ctx.source)
1897+
val name = packageObjectName(ctx.source, pdef.srcPos)
18941898
val grouped =
18951899
ModuleDef(name, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
18961900
.withMods(Modifiers(Synthetic))

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import cc.{CheckCaptures, isRetainsLike}
5050
import config.Config
5151
import config.MigrationVersion
5252
import transform.CheckUnused.OriginalName
53+
import dotty.tools.dotc.util.chaining.*
5354

5455
import scala.annotation.{unchecked as _, *}
5556
import dotty.tools.dotc.util.chaining.*
@@ -3460,12 +3461,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
34603461
inContext(ctx.packageContext(tree, pkg)) {
34613462
// If it exists, complete the class containing the top-level definitions
34623463
// before typing any statement in the package to avoid cycles as in i13669.scala
3463-
val topLevelClassName = desugar.packageObjectName(ctx.source).moduleClassName
3464-
pkg.moduleClass.info.decls.lookup(topLevelClassName).ensureCompleted()
3464+
val packageObjectName = desugar.packageObjectName(ctx.source, tree.srcPos)
3465+
val topLevelClassSymbol = pkg.moduleClass.info.decls.lookup(packageObjectName.moduleClassName)
3466+
topLevelClassSymbol.ensureCompleted()
34653467
var stats1 = typedStats(tree.stats, pkg.moduleClass)._1
34663468
if (!ctx.isAfterTyper)
34673469
stats1 = stats1 ++ typedBlockStats(MainProxies.proxies(stats1))._1
34683470
cpy.PackageDef(tree)(pid1, stats1).withType(pkg.termRef)
3471+
.tap: _ =>
3472+
if !ctx.isAfterTyper
3473+
&& pkg != defn.EmptyPackageVal
3474+
&& !topLevelClassSymbol.info.decls.filter(sym => !sym.isConstructor && !sym.is(Synthetic)).isEmpty
3475+
then
3476+
desugar.checkSimplePackageName(packageObjectName, tree.span, ctx.source, isPackageObject = true)
34693477
}
34703478
case _ =>
34713479
// Package will not exist if a duplicate type has already been entered, see `tests/neg/1708.scala`
@@ -3850,8 +3858,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38503858
def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(using Context): (List[Tree], Context) = {
38513859
val buf = new mutable.ListBuffer[Tree]
38523860
var enumContexts: SimpleIdentityMap[Symbol, Context] = SimpleIdentityMap.empty
3853-
val initialNotNullInfos = ctx.notNullInfos
38543861
// A map from `enum` symbols to the contexts enclosing their definitions
3862+
val initialNotNullInfos = ctx.notNullInfos
38553863
@tailrec def traverse(stats: List[untpd.Tree])(using Context): (List[Tree], Context) = stats match {
38563864
case (imp: untpd.Import) :: rest =>
38573865
val imp1 = typed(imp)

tests/neg/i22670.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/neg/i22670/i22670-macro.scala:4:8 --------------------------------------------------------------------
2+
4 |package xy // named package required for warning
3+
|^
4+
|The package name `i22670-macro$package` will be encoded on the classpath, and can lead to undefined behaviour.
5+
5 |import scala.quoted.*
6+
6 |transparent inline def foo =
7+
7 | ${ fooImpl }
8+
8 |def fooImpl(using Quotes): Expr[Any] =
9+
9 | Expr("hello")
10+
No warnings can be incurred under -Werror (or -Xfatal-warnings)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//> using options -Werror
2+
// nopos-error
3+
//package `X-Y` // explicit package name gets a diagnostic
4+
package xy // named package required for warning
5+
6+
import scala.quoted.*
7+
8+
transparent inline def foo =
9+
${ fooImpl }
10+
11+
def fooImpl(using Quotes): Expr[Any] =
12+
Expr("hello")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
//import `X-Y`.*
3+
import xy.foo
4+
5+
val x = foo // Cyclic macro dependencies (if compilation proceeds)

tests/warn/i22670-test.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//> using options -Werror
2+
3+
// don't warn about file package object with special char in name when in empty package
4+
def f = 42

0 commit comments

Comments
 (0)