Skip to content

check if package names will be encoded in desugar #14597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Symbols._, StdNames._, Trees._, ContextOps._
import Decorators._, transform.SymUtils._
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
import typer.{Namer, Checking}
import util.{Property, SourceFile, SourcePosition}
import util.{Property, SourceFile, SourcePosition, Chars}
import config.Feature.{sourceVersion, migrateTo3, enabled}
import config.SourceVersion._
import collection.mutable.ListBuffer
Expand Down Expand Up @@ -521,9 +521,9 @@ object desugar {
val enumCompanionRef = TermRefTree()
val enumImport =
Import(enumCompanionRef, enumCases.flatMap(caseIds).map(
enumCase =>
enumCase =>
ImportSelector(enumCase.withSpan(enumCase.span.startPos))
)
)
)
(enumImport :: enumStats, enumCases, enumCompanionRef)
}
Expand Down Expand Up @@ -834,7 +834,8 @@ object desugar {
val impl = mdef.impl
val mods = mdef.mods
val moduleName = normalizeName(mdef, impl).asTermName
if (mods.is(Package))
if mods.is(Package) then
checkPackageName(mdef)
PackageDef(Ident(moduleName),
cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil)
else
Expand Down Expand Up @@ -950,6 +951,26 @@ object desugar {
else tree
}

def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =

def check(name: Name, errSpan: Span): Unit = name match
case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) =>
report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan))
case _ =>

def loop(part: RefTree): Unit = part match
case part @ Ident(name) => check(name, part.span)
case part @ Select(qual: RefTree, name) =>
check(name, part.nameSpan)
loop(qual)
case _ =>

mdef match
case pdef: PackageDef => loop(pdef.pid)
case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan)
case _ =>
end checkPackageName

/** The normalized name of `mdef`. This means
* 1. Check that the name does not redefine a Scala core class.
* If it does redefine, issue an error and return a mangled name instead
Expand Down Expand Up @@ -1134,7 +1155,7 @@ object desugar {
val matchExpr =
if (tupleOptimizable) rhs
else
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil)
vars match {
case Nil if !mods.is(Lazy) =>
Expand All @@ -1155,11 +1176,11 @@ object desugar {
val restDefs =
for (((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD)
yield
if mods.is(Lazy) then
if mods.is(Lazy) then
DefDef(named.name.asTermName, Nil, tpt, selector(n))
.withMods(mods &~ Lazy)
.withSpan(named.span)
else
else
valDef(
ValDef(named.name.asTermName, tpt, selector(n))
.withMods(mods)
Expand Down Expand Up @@ -1321,6 +1342,7 @@ object desugar {
* (i.e. objects having the same name as a wrapped type)
*/
def packageDef(pdef: PackageDef)(using Context): PackageDef = {
checkPackageName(pdef)
val wrappedTypeNames = pdef.stats.collect {
case stat: TypeDef if isTopLevelDef(stat) => stat.name
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/util/Chars.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@ object Chars {
'|' | '/' | '\\' => true
case c => isSpecial(c)
}

/** Would the character be encoded by `NameTransformer.encode`? */
def willBeEncoded(c : Char) : Boolean = !JCharacter.isJavaIdentifierPart(c)
}
16 changes: 16 additions & 0 deletions tests/neg-custom-args/fatal-warnings/symbolic-packages.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:1:8 ---------------------------------------------
1 |package `with spaces` { // error
| ^^^^^^^^^^^^^
| The package name `with spaces` will be encoded on the classpath, and can lead to undefined behaviour.
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:5:10 --------------------------------------------
5 |package +.* { // error // error
| ^
| The package name `*` will be encoded on the classpath, and can lead to undefined behaviour.
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:5:8 ---------------------------------------------
5 |package +.* { // error // error
| ^
| The package name `+` will be encoded on the classpath, and can lead to undefined behaviour.
-- Error: tests/neg-custom-args/fatal-warnings/symbolic-packages.scala:9:16 --------------------------------------------
9 |package object `mixed_*` { // error
| ^^^^^^^
| The package name `mixed_*` will be encoded on the classpath, and can lead to undefined behaviour.
11 changes: 11 additions & 0 deletions tests/neg-custom-args/fatal-warnings/symbolic-packages.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package `with spaces` { // error
class Foo
}

package +.* { // error // error
class Bar
}

package object `mixed_*` { // error
class Baz
}
4 changes: 4 additions & 0 deletions tests/pos-special/fatal-warnings/stats-in-empty-pkg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def foo = 23
val bar = foo
var baz = bar
type Qux = Int