diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3bd458979754..a7f4cbd0952b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -4350,6 +4350,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) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index b832832590d9..bfeb426d6f9a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -212,6 +212,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case ContextBoundCompanionNotValueID // errorNumber: 196 - unused in LTS case InlinedAnonClassWarningID // errorNumber: 197 case UnusedSymbolID // errorNumber: 198 + case TailrecNestedCallID //errorNumber: 199 - unused in LTS + case FinalLocalDefID // errorNumber: 200 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 38c79f90a733..393816714931 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1819,7 +1819,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" } @@ -1828,7 +1828,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) diff --git a/tests/neg/17579.check b/tests/neg/17579.check new file mode 100644 index 000000000000..1149f9c0faa4 --- /dev/null +++ b/tests/neg/17579.check @@ -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 diff --git a/tests/neg/17579.scala b/tests/neg/17579.scala new file mode 100644 index 000000000000..268199e9006f --- /dev/null +++ b/tests/neg/17579.scala @@ -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 {} + }