Skip to content

Commit

Permalink
check if package names will be encoded in desugar
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bishabosha committed Mar 2, 2022
1 parent d57a4a4 commit d7e4cfa
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
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 name != nme.EMPTY_PACKAGE && 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: SimpleName) => check(name, part.span)
case part @ Select(qual: RefTree, name: SimpleName) =>
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

0 comments on commit d7e4cfa

Please sign in to comment.