Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a specific error message for local final defs #20557

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {}
}
Loading