Skip to content

Commit

Permalink
Add a specific error message for local final defs (#20557)
Browse files Browse the repository at this point in the history
Fixes #17579.
  • Loading branch information
odersky authored Jul 2, 2024
2 parents d68b645 + cfbace4 commit b481932
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 2 deletions.
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4560,6 +4560,12 @@ object Parsers {
for (imod <- implicitMods.mods) mods = addMod(mods, imod)
if (mods.is(Final))
// A final modifier means the local definition is "class-like". // FIXME: Deal with modifiers separately

// See test 17579. We allow `final` on `given` because these can be
// translated to class definitions, for which `final` is allowed but
// redundant--there is a seperate warning for this.
if isDclIntro && in.token != GIVEN then syntaxError(FinalLocalDef())

tmplDef(start, mods)
else
defOrDcl(start, mods)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case InlinedAnonClassWarningID // errorNumber: 197
case UnusedSymbolID // errorNumber: 198
case TailrecNestedCallID //errorNumber: 199
case FinalLocalDefID // errorNumber: 200

def errorNumber = ordinal - 1

Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@ class WrongNumberOfParameters(tree: untpd.Tree, foundCount: Int, pt: Type, expec

class DuplicatePrivateProtectedQualifier()(using Context)
extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) {
def msg(using Context) = "Duplicate private/protected qualifier"
def msg(using Context) = "Duplicate private/protected modifier"
def explain(using Context) =
i"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes"
}
Expand All @@ -1847,7 +1847,13 @@ class ExpectedStartOfTopLevelDefinition()(using Context)
extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) {
def msg(using Context) = "Expected start of definition"
def explain(using Context) =
i"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers"
i"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after modifiers"
}

class FinalLocalDef()(using Context)
extends SyntaxMsg(FinalLocalDefID) {
def msg(using Context) = i"The ${hl("final")} modifier is not allowed on local definitions"
def explain(using Context) = ""
}

class NoReturnFromInlineable(owner: Symbol)(using Context)
Expand Down
30 changes: 30 additions & 0 deletions tests/neg/17579.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- [E200] Syntax Error: tests/neg/17579.scala:5:10 ---------------------------------------------------------------------
5 | final val v1 = 42 // error: final modifier is not allowed on local definitions
| ^^^
| The final modifier is not allowed on local definitions
-- [E200] Syntax Error: tests/neg/17579.scala:6:15 ---------------------------------------------------------------------
6 | final lazy val v2 = 42 // error: final modifier is not allowed on local definitions
| ^^^
| The final modifier is not allowed on local definitions
-- [E200] Syntax Error: tests/neg/17579.scala:7:10 ---------------------------------------------------------------------
7 | final def v4 = 42 // error: final modifier is not allowed on local definitions
| ^^^
| The final modifier is not allowed on local definitions
-- [E200] Syntax Error: tests/neg/17579.scala:8:10 ---------------------------------------------------------------------
8 | final var v5 = 42 // error: final modifier is not allowed on local definitions
| ^^^
| The final modifier is not allowed on local definitions
-- [E200] Syntax Error: tests/neg/17579.scala:9:10 ---------------------------------------------------------------------
9 | final type Foo = String // error: final modifier is not allowed on local definitions
| ^^^^
| The final modifier is not allowed on local definitions
-- [E088] Syntax Error: tests/neg/17579.scala:14:10 --------------------------------------------------------------------
14 | final private val v3 = 42 // error: expected start of definition
| ^^^^^^^
| Expected start of definition
|
| longer explanation available when compiling with `-explain`
-- [E147] Syntax Warning: tests/neg/17579.scala:19:6 -------------------------------------------------------------------
19 | final given Object with {} // warning: modifier `final` is redundant for this definition
| ^^^^^
| Modifier final is redundant for this definition
26 changes: 26 additions & 0 deletions tests/neg/17579.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class C:
final var v = 42 // ok

def f =
final val v1 = 42 // error: final modifier is not allowed on local definitions
final lazy val v2 = 42 // error: final modifier is not allowed on local definitions
final def v4 = 42 // error: final modifier is not allowed on local definitions
final var v5 = 42 // error: final modifier is not allowed on local definitions
final type Foo = String // error: final modifier is not allowed on local definitions

// We get a different error message here because `private` is also not a
// local modifier token. In the future, we could always parse all tokens and
// then flag those that are not legal at this point.
final private val v3 = 42 // error: expected start of definition

{
// No error in this case, because the `given` is translated to a class
// definition, for which `final` is redundant but not illegal.
final given Object with {} // warning: modifier `final` is redundant for this definition
}

{
// Also no error in this case, because we can't easily distinguish it from
// the previous case.
final given Object = new Object {}
}

0 comments on commit b481932

Please sign in to comment.