Skip to content

Commit 7322ab1

Browse files
committed
Disallow ? as a type name
Now that we use `?` for wildcards, we should treat it more like a keyword for types and not allow it as a type name to avoid confusion.
1 parent 9a4feff commit 7322ab1

File tree

4 files changed

+113
-1
lines changed

4 files changed

+113
-1
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ object Parsers {
359359
offset
360360
}
361361

362+
/** Do not accept `?` as an identifier here. */
363+
def refuseQuestionMark(category: String, addendum: String = ""): Unit =
364+
if isIdent(nme.?) then
365+
ctx.errorOrMigrationWarning(s"`?` is not a valid $category name$addendum", in.sourcePos())
366+
362367
def reportMissing(expected: Token): Unit =
363368
syntaxError(ExpectedTokenButFound(expected, in.token))
364369

@@ -2845,6 +2850,9 @@ object Parsers {
28452850
else if isIdent(nme.raw.MINUS) then variance(Contravariant)
28462851
else EmptyFlags)
28472852
atSpan(start, nameStart) {
2853+
refuseQuestionMark("type parameter",
2854+
addendum = if ownerKind == ParamOwner.Type
2855+
then ", use `_` to denote a higher-kinded type parameter" else "")
28482856
val name =
28492857
if (isAbstractOwner && in.token == USCORE) {
28502858
in.nextToken()
@@ -3337,6 +3345,7 @@ object Parsers {
33373345
def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = {
33383346
newLinesOpt()
33393347
atSpan(start, nameStart) {
3348+
refuseQuestionMark("type")
33403349
val nameIdent = typeIdent()
33413350
val tparams = typeParamClauseOpt(ParamOwner.Type)
33423351
def makeTypeDef(rhs: Tree): Tree = {
@@ -3414,6 +3423,7 @@ object Parsers {
34143423
/** ClassDef ::= id ClassConstr TemplateOpt
34153424
*/
34163425
def classDef(start: Offset, mods: Modifiers): TypeDef = atSpan(start, nameStart) {
3426+
refuseQuestionMark("class or trait")
34173427
classDefRest(start, mods, ident().toTypeName)
34183428
}
34193429

@@ -3458,6 +3468,7 @@ object Parsers {
34583468
*/
34593469
def enumDef(start: Offset, mods: Modifiers): TypeDef = atSpan(start, nameStart) {
34603470
val mods1 = checkAccessOnly(mods, "definitions")
3471+
refuseQuestionMark("enum")
34613472
val modulName = ident()
34623473
in.endMarkerScope(modulName) {
34633474
val clsName = modulName.toTypeName
@@ -3474,6 +3485,7 @@ object Parsers {
34743485
accept(CASE)
34753486

34763487
atSpan(start, nameStart) {
3488+
refuseQuestionMark("enum case")
34773489
val id = termIdent()
34783490
if (in.token == COMMA) {
34793491
in.nextToken()
@@ -3530,6 +3542,7 @@ object Parsers {
35303542
var mods1 = addMod(mods, givenMod)
35313543
val hasGivenSig = followingIsGivenSig()
35323544
val nameStart = in.offset
3545+
refuseQuestionMark("given")
35333546
val name = if isIdent && hasGivenSig then ident() else EmptyTermName
35343547

35353548
val gdef = in.endMarkerScope(if name.isEmpty then GIVEN else name) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
scala> case class Foo[M[?]](x: M[Int])
1+
scala> case class Foo[M[_]](x: M[Int])
22
// defined case class Foo
33
scala> Foo(Option(1))
44
val res0: Foo[Option] = Foo(Some(1))

tests/neg/type-qmark.check

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
-- Error: tests/neg/type-qmark.scala:1:10 ------------------------------------------------------------------------------
2+
1 |class Foo[?] // error
3+
| ^
4+
| `?` is not a valid type parameter name
5+
-- Error: tests/neg/type-qmark.scala:2:12 ------------------------------------------------------------------------------
6+
2 |class Foo[M[?]] // error
7+
| ^
8+
| `?` is not a valid type parameter name, use `_` to denote a higher-kinded type parameter
9+
-- Error: tests/neg/type-qmark.scala:3:8 -------------------------------------------------------------------------------
10+
3 |def foo[?] = {} // error
11+
| ^
12+
| `?` is not a valid type parameter name
13+
-- Error: tests/neg/type-qmark.scala:4:10 ------------------------------------------------------------------------------
14+
4 |def bar[M[?]] = {} // error
15+
| ^
16+
| `?` is not a valid type parameter name, use `_` to denote a higher-kinded type parameter
17+
-- Error: tests/neg/type-qmark.scala:6:7 -------------------------------------------------------------------------------
18+
6 |type A[?] = Int // error
19+
| ^
20+
| `?` is not a valid type parameter name, use `_` to denote a higher-kinded type parameter
21+
-- Error: tests/neg/type-qmark.scala:7:10 ------------------------------------------------------------------------------
22+
7 |type B = [?] =>> Int // error
23+
| ^
24+
| `?` is not a valid type parameter name
25+
-- Error: tests/neg/type-qmark.scala:8:10 ------------------------------------------------------------------------------
26+
8 |type C = [?] => Int => Int // error
27+
| ^
28+
| `?` is not a valid type parameter name
29+
-- Error: tests/neg/type-qmark.scala:9:12 ------------------------------------------------------------------------------
30+
9 |type D = [X[?]] =>> Int // error
31+
| ^
32+
| `?` is not a valid type parameter name, use `_` to denote a higher-kinded type parameter
33+
-- Error: tests/neg/type-qmark.scala:10:12 -----------------------------------------------------------------------------
34+
10 |type E = [X[?]] => Int => Int // error
35+
| ^
36+
| `?` is not a valid type parameter name, use `_` to denote a higher-kinded type parameter
37+
-- Error: tests/neg/type-qmark.scala:13:7 ------------------------------------------------------------------------------
38+
13 | case ?() // error
39+
| ^
40+
| `?` is not a valid enum case name
41+
-- Error: tests/neg/type-qmark.scala:16:8 ------------------------------------------------------------------------------
42+
16 | class ? // error
43+
| ^
44+
| `?` is not a valid class or trait name
45+
-- Error: tests/neg/type-qmark.scala:19:8 ------------------------------------------------------------------------------
46+
19 | trait ? // error
47+
| ^
48+
| `?` is not a valid class or trait name
49+
-- Error: tests/neg/type-qmark.scala:22:7 ------------------------------------------------------------------------------
50+
22 | type ? = Int // error
51+
| ^
52+
| `?` is not a valid type name
53+
-- Error: tests/neg/type-qmark.scala:25:7 ------------------------------------------------------------------------------
54+
25 | enum ? { // error
55+
| ^
56+
| `?` is not a valid enum name
57+
-- Error: tests/neg/type-qmark.scala:30:8 ------------------------------------------------------------------------------
58+
30 | given ? as Int = 1 // error
59+
| ^
60+
| `?` is not a valid given name
61+
-- Error: tests/neg/type-qmark.scala:34:8 ------------------------------------------------------------------------------
62+
34 | given ? as Foo {} // error
63+
| ^
64+
| `?` is not a valid given name

tests/neg/type-qmark.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class Foo[?] // error
2+
class Foo[M[?]] // error
3+
def foo[?] = {} // error
4+
def bar[M[?]] = {} // error
5+
6+
type A[?] = Int // error
7+
type B = [?] =>> Int // error
8+
type C = [?] => Int => Int // error
9+
type D = [X[?]] =>> Int // error
10+
type E = [X[?]] => Int => Int // error
11+
12+
enum E {
13+
case ?() // error
14+
}
15+
object F {
16+
class ? // error
17+
}
18+
object G {
19+
trait ? // error
20+
}
21+
object H {
22+
type ? = Int // error
23+
}
24+
object I {
25+
enum ? { // error
26+
case X
27+
}
28+
}
29+
object J {
30+
given ? as Int = 1 // error
31+
}
32+
object K {
33+
class Foo
34+
given ? as Foo {} // error
35+
}

0 commit comments

Comments
 (0)