Skip to content

Commit 30a88be

Browse files
committed
check if package names will be encoded in desugar
produce a warning if they are - this is because encoding a package name is mostly undefined behaviour that the scala 3 compiler magically recovers from - but could be problematic.
1 parent d57a4a4 commit 30a88be

File tree

5 files changed

+63
-7
lines changed

5 files changed

+63
-7
lines changed

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Symbols._, StdNames._, Trees._, ContextOps._
88
import Decorators._, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
1010
import typer.{Namer, Checking}
11-
import util.{Property, SourceFile, SourcePosition}
11+
import util.{Property, SourceFile, SourcePosition, Chars}
1212
import config.Feature.{sourceVersion, migrateTo3, enabled}
1313
import config.SourceVersion._
1414
import collection.mutable.ListBuffer
@@ -521,9 +521,9 @@ object desugar {
521521
val enumCompanionRef = TermRefTree()
522522
val enumImport =
523523
Import(enumCompanionRef, enumCases.flatMap(caseIds).map(
524-
enumCase =>
524+
enumCase =>
525525
ImportSelector(enumCase.withSpan(enumCase.span.startPos))
526-
)
526+
)
527527
)
528528
(enumImport :: enumStats, enumCases, enumCompanionRef)
529529
}
@@ -834,7 +834,8 @@ object desugar {
834834
val impl = mdef.impl
835835
val mods = mdef.mods
836836
val moduleName = normalizeName(mdef, impl).asTermName
837-
if (mods.is(Package))
837+
if mods.is(Package) then
838+
checkPackageName(mdef)
838839
PackageDef(Ident(moduleName),
839840
cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil)
840841
else
@@ -950,6 +951,26 @@ object desugar {
950951
else tree
951952
}
952953

954+
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
955+
956+
def check(name: Name, errSpan: Span): Unit = name match
957+
case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) =>
958+
report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan))
959+
case _ =>
960+
961+
def loop(part: RefTree): Unit = part match
962+
case part @ Ident(name) => check(name, part.span)
963+
case part @ Select(qual: RefTree, name) =>
964+
check(name, part.nameSpan)
965+
loop(qual)
966+
case _ =>
967+
968+
mdef match
969+
case pdef: PackageDef => loop(pdef.pid)
970+
case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan)
971+
case _ =>
972+
end checkPackageName
973+
953974
/** The normalized name of `mdef`. This means
954975
* 1. Check that the name does not redefine a Scala core class.
955976
* If it does redefine, issue an error and return a mangled name instead
@@ -1134,7 +1155,7 @@ object desugar {
11341155
val matchExpr =
11351156
if (tupleOptimizable) rhs
11361157
else
1137-
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
1158+
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
11381159
Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil)
11391160
vars match {
11401161
case Nil if !mods.is(Lazy) =>
@@ -1155,11 +1176,11 @@ object desugar {
11551176
val restDefs =
11561177
for (((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD)
11571178
yield
1158-
if mods.is(Lazy) then
1179+
if mods.is(Lazy) then
11591180
DefDef(named.name.asTermName, Nil, tpt, selector(n))
11601181
.withMods(mods &~ Lazy)
11611182
.withSpan(named.span)
1162-
else
1183+
else
11631184
valDef(
11641185
ValDef(named.name.asTermName, tpt, selector(n))
11651186
.withMods(mods)
@@ -1321,6 +1342,7 @@ object desugar {
13211342
* (i.e. objects having the same name as a wrapped type)
13221343
*/
13231344
def packageDef(pdef: PackageDef)(using Context): PackageDef = {
1345+
checkPackageName(pdef)
13241346
val wrappedTypeNames = pdef.stats.collect {
13251347
case stat: TypeDef if isTopLevelDef(stat) => stat.name
13261348
}

compiler/src/dotty/tools/dotc/util/Chars.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,7 @@ object Chars {
9393
'|' | '/' | '\\' => true
9494
case c => isSpecial(c)
9595
}
96+
97+
/** Would the character be encoded by `NameTransformer.encode`? */
98+
def willBeEncoded(c : Char) : Boolean = !JCharacter.isJavaIdentifierPart(c)
9699
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:1:8 ---------------------------------------------
2+
1 |package `with spaces` { // error
3+
| ^^^^^^^^^^^^^
4+
| The package name `with spaces` will be encoded on the classpath, and can lead to undefined behaviour.
5+
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:5:10 --------------------------------------------
6+
5 |package +.* { // error // error
7+
| ^
8+
| The package name `*` will be encoded on the classpath, and can lead to undefined behaviour.
9+
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:5:8 ---------------------------------------------
10+
5 |package +.* { // error // error
11+
| ^
12+
| The package name `+` will be encoded on the classpath, and can lead to undefined behaviour.
13+
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:9:16 --------------------------------------------
14+
9 |package object `mixed_*` { // error
15+
| ^^^^^^^
16+
| The package name `mixed_*` will be encoded on the classpath, and can lead to undefined behaviour.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package `with spaces` { // error
2+
class Foo
3+
}
4+
5+
package +.* { // error // error
6+
class Bar
7+
}
8+
9+
package object `mixed_*` { // error
10+
class Baz
11+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def foo = 23
2+
val bar = foo
3+
var baz = bar
4+
type Qux = Int

0 commit comments

Comments
 (0)