Skip to content

Commit 8be509f

Browse files
committed
Disallow naming the root package, except for selections
1 parent ed319e8 commit 8be509f

File tree

3 files changed

+75
-9
lines changed

3 files changed

+75
-9
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,9 +1072,16 @@ object Parsers {
10721072
}
10731073

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

1078+
/** Call `rawTermIdent`, and check it isn't a root package name. */
1079+
def termIdent(): Ident =
1080+
val ident = rawTermIdent()
1081+
if ident.name == nme.ROOTPKG then
1082+
syntaxError(em"Illegal use of root package name.")
1083+
ident
1084+
10781085
/** Accept identifier and return Ident with its name as a type name. */
10791086
def typeIdent(): Ident =
10801087
makeIdent(in.token, in.offset, ident().toTypeName)
@@ -1112,7 +1119,7 @@ object Parsers {
11121119
* | [id ‘.’] ‘this’
11131120
* | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id
11141121
*/
1115-
def simpleRef(): Tree =
1122+
def simpleRef(allowRoot: Boolean = true): Tree =
11161123
val start = in.offset
11171124

11181125
def handleThis(qual: Ident) =
@@ -1129,7 +1136,7 @@ object Parsers {
11291136
if in.token == THIS then handleThis(EmptyTypeIdent)
11301137
else if in.token == SUPER then handleSuper(EmptyTypeIdent)
11311138
else
1132-
val t = termIdent()
1139+
val t = if allowRoot then rawTermIdent() else termIdent()
11331140
if in.token == DOT then
11341141
def qual = cpy.Ident(t)(t.name.toTypeName)
11351142
in.lookahead.token match
@@ -2965,7 +2972,7 @@ object Parsers {
29652972
*/
29662973
def simplePattern(): Tree = in.token match {
29672974
case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER =>
2968-
simpleRef() match
2975+
simpleRef(allowRoot = false) match
29692976
case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id))
29702977
case t => simplePatternRest(t)
29712978
case USCORE =>

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
569569
// optimization, it also avoids forcing imports thus potentially avoiding
570570
// cyclic references.
571571
if (name == nme.ROOTPKG)
572-
return tree.withType(defn.RootPackage.termRef)
572+
val tree2 = tree.withType(defn.RootPackage.termRef)
573+
checkLegalValue(tree2, pt)
574+
return tree2
573575

574576
val rawType =
575577
val saved1 = unimported
@@ -581,13 +583,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
581583
if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then
582584
report.migrationWarning(
583585
em"""Name resolution will change.
584-
| currently selected : $foundUnderScala2
585-
| in the future, without -source 3.0-migration: $found""", tree.srcPos)
586+
| currently selected : $foundUnderScala2
587+
| in the future, without -source 3.0-migration: $found""", tree.srcPos)
586588
foundUnderScala2
587589
else found
588590
finally
589-
unimported = saved1
590-
foundUnderScala2 = saved2
591+
unimported = saved1
592+
foundUnderScala2 = saved2
591593

592594
/** Normally, returns `ownType` except if `ownType` is a constructor proxy,
593595
* and there is another shadowed type accessible with the same name that is not:

tests/neg/i18020.scala

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import _root_.scala.StringContext // ok
2+
3+
class Test :
4+
val Foo = 1
5+
def foo0: Unit =
6+
val x = new _root_.scala.StringContext() // ok
7+
val y: Option[_root_.scala.Serializable] = None // ok
8+
val z: _root_.scala.None.type = None
9+
val w = _root_.scala.None
10+
val (_root_, other) = (1, 2) // error
11+
val (Test.this.Foo, 1) = ???
12+
??? match
13+
case (Test.this.Foo, 1) => ()
14+
15+
def foo3 =
16+
val _root_ = "abc" // error
17+
18+
def foo1: Unit =
19+
val _root_: String = "abc" // error // error
20+
// _root_: is, technically, a legal name
21+
// so then it tries to construct the infix op pattern
22+
// "_root_ String .." and then throws in a null when it fails
23+
// to find an argument
24+
// then Typer rejects "String" as an infix extractor (like ::)
25+
// which is the second error
26+
27+
def foo2: Unit = // error
28+
val _root_ : String = "abc" // error
29+
30+
// i17757
31+
def fooVal: Unit =
32+
val _root_ = "abc" // error
33+
println(_root_.length) // error
34+
println(_root_) // error
35+
36+
def barVal: Unit =
37+
_root_ // error
38+
_root_.scala // error
39+
println(_root_) // error
40+
println(_root_.scala) // error
41+
42+
// i18050
43+
package p {
44+
package _root_ { // error
45+
object X // error
46+
}
47+
}
48+
49+
// i12508
50+
package _root_ { // error
51+
class C {
52+
val _root_ = 42 // error
53+
}
54+
}
55+
package _root_.p { // error
56+
class C
57+
}

0 commit comments

Comments
 (0)