Skip to content

Commit

Permalink
Disallow naming the root package, except for selections
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jul 11, 2023
1 parent ed319e8 commit 8be509f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 9 deletions.
15 changes: 11 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1072,9 +1072,16 @@ object Parsers {
}

/** Accept identifier and return Ident with its name as a term name. */
def termIdent(): Ident =
def rawTermIdent(): Ident =
makeIdent(in.token, in.offset, ident())

/** Call `rawTermIdent`, and check it isn't a root package name. */
def termIdent(): Ident =
val ident = rawTermIdent()
if ident.name == nme.ROOTPKG then
syntaxError(em"Illegal use of root package name.")
ident

/** Accept identifier and return Ident with its name as a type name. */
def typeIdent(): Ident =
makeIdent(in.token, in.offset, ident().toTypeName)
Expand Down Expand Up @@ -1112,7 +1119,7 @@ object Parsers {
* | [id ‘.’] ‘this’
* | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id
*/
def simpleRef(): Tree =
def simpleRef(allowRoot: Boolean = true): Tree =
val start = in.offset

def handleThis(qual: Ident) =
Expand All @@ -1129,7 +1136,7 @@ object Parsers {
if in.token == THIS then handleThis(EmptyTypeIdent)
else if in.token == SUPER then handleSuper(EmptyTypeIdent)
else
val t = termIdent()
val t = if allowRoot then rawTermIdent() else termIdent()
if in.token == DOT then
def qual = cpy.Ident(t)(t.name.toTypeName)
in.lookahead.token match
Expand Down Expand Up @@ -2965,7 +2972,7 @@ object Parsers {
*/
def simplePattern(): Tree = in.token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER =>
simpleRef() match
simpleRef(allowRoot = false) match
case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id))
case t => simplePatternRest(t)
case USCORE =>
Expand Down
12 changes: 7 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// optimization, it also avoids forcing imports thus potentially avoiding
// cyclic references.
if (name == nme.ROOTPKG)
return tree.withType(defn.RootPackage.termRef)
val tree2 = tree.withType(defn.RootPackage.termRef)
checkLegalValue(tree2, pt)
return tree2

val rawType =
val saved1 = unimported
Expand All @@ -581,13 +583,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then
report.migrationWarning(
em"""Name resolution will change.
| currently selected : $foundUnderScala2
| in the future, without -source 3.0-migration: $found""", tree.srcPos)
| currently selected : $foundUnderScala2
| in the future, without -source 3.0-migration: $found""", tree.srcPos)
foundUnderScala2
else found
finally
unimported = saved1
foundUnderScala2 = saved2
unimported = saved1
foundUnderScala2 = saved2

/** Normally, returns `ownType` except if `ownType` is a constructor proxy,
* and there is another shadowed type accessible with the same name that is not:
Expand Down
57 changes: 57 additions & 0 deletions tests/neg/i18020.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import _root_.scala.StringContext // ok

class Test :
val Foo = 1
def foo0: Unit =
val x = new _root_.scala.StringContext() // ok
val y: Option[_root_.scala.Serializable] = None // ok
val z: _root_.scala.None.type = None
val w = _root_.scala.None
val (_root_, other) = (1, 2) // error
val (Test.this.Foo, 1) = ???
??? match
case (Test.this.Foo, 1) => ()

def foo3 =
val _root_ = "abc" // error

def foo1: Unit =
val _root_: String = "abc" // error // error
// _root_: is, technically, a legal name
// so then it tries to construct the infix op pattern
// "_root_ String .." and then throws in a null when it fails
// to find an argument
// then Typer rejects "String" as an infix extractor (like ::)
// which is the second error

def foo2: Unit = // error
val _root_ : String = "abc" // error

// i17757
def fooVal: Unit =
val _root_ = "abc" // error
println(_root_.length) // error
println(_root_) // error

def barVal: Unit =
_root_ // error
_root_.scala // error
println(_root_) // error
println(_root_.scala) // error

// i18050
package p {
package _root_ { // error
object X // error
}
}

// i12508
package _root_ { // error
class C {
val _root_ = 42 // error
}
}
package _root_.p { // error
class C
}

0 comments on commit 8be509f

Please sign in to comment.