From c2fd351441b37fb502cab8e6763e9f4e62b4121a Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Mon, 1 Jul 2024 11:01:12 +0200 Subject: [PATCH] Add an error message for local final defs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Eugene Flesselle Co-Authored-By: anna herlihy Co-Authored-By: Oliver Bračevac --- .../dotty/tools/dotc/parsing/Parsers.scala | 6 ++++ .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 6 ++++ tests/neg/17579.check | 30 +++++++++++++++++++ tests/neg/17579.scala | 22 ++++++++++++++ 5 files changed, 65 insertions(+) create mode 100644 tests/neg/17579.check create mode 100644 tests/neg/17579.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4c13934f3473..b84313cc972a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 273c3720bc1c..e3613e3f783a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index ecf542668bf5..d33b2c574318 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1850,6 +1850,12 @@ class ExpectedStartOfTopLevelDefinition()(using Context) 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) extends SyntaxMsg(NoReturnFromInlineableID) { def msg(using Context) = i"No explicit ${hl("return")} allowed from inlineable $owner" diff --git a/tests/neg/17579.check b/tests/neg/17579.check new file mode 100644 index 000000000000..f0daf7907e26 --- /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:18:4 ------------------------------------------------------------------- +18 | 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..3e6b6d47afa4 --- /dev/null +++ b/tests/neg/17579.scala @@ -0,0 +1,22 @@ +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 {}