From f18961558e06289eb33ee0632b6ce61ec4efb619 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 3 Oct 2022 23:10:38 -0700 Subject: [PATCH 1/2] Clarify ambiguous reference error message --- .../dotty/tools/dotc/reporting/messages.scala | 30 ++++++++----- tests/neg/ambiref.check | 16 +++---- tests/neg/i12682.check | 45 +++++++++++++++++++ tests/neg/i12682.scala | 13 ++++++ tests/neg/i13558.check | 8 ++-- tests/neg/i9803.check | 4 +- 6 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 tests/neg/i12682.check create mode 100644 tests/neg/i12682.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index d7920e1f1b36..92ee1b44def0 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1328,21 +1328,29 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec } def msg(using Context) = - i"""|Reference to $name is ambiguous, - |it is both ${bindingString(newPrec, ctx)} + i"""|Reference to $name is ambiguous. + |It is both ${bindingString(newPrec, ctx)} |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" def explain(using Context) = - i"""|The compiler can't decide which of the possible choices you - |are referencing with $name: A definition of lower precedence - |in an inner scope, or a definition with higher precedence in - |an outer scope. - |Note: - | - Definitions in an enclosing scope take precedence over inherited definitions - | - Definitions take precedence over imports + val precedent = + if newPrec == prevPrec then """two bindings of equal precedence + |were introduced in the same scope.""".stripMargin + else """a binding of lower precedence + |in an inner scope cannot shadow a binding with higher precedence in + |an outer scope.""".stripMargin + + i"""|The identifier $name is ambiguous because $precedent + | + |The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports | - Named imports take precedence over wildcard imports - | - You may replace a name when imported using - | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } + | - Definitions from packages in other files + |Note: + | - When importing, you can avoid naming conflicts by renaming: + | ${hl("import")} scala.{$name => ${name.show}Tick} |""" } diff --git a/tests/neg/ambiref.check b/tests/neg/ambiref.check index 95b542c7aae3..5d701b3b3b71 100644 --- a/tests/neg/ambiref.check +++ b/tests/neg/ambiref.check @@ -1,32 +1,32 @@ -- [E049] Reference Error: tests/neg/ambiref.scala:8:14 ---------------------------------------------------------------- 8 | println(x) // error | ^ - | Reference to x is ambiguous, - | it is both defined in object Test + | Reference to x is ambiguous. + | It is both defined in object Test | and inherited subsequently in class D | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:10:14 --------------------------------------------------------------- 10 | println(x) // error | ^ - | Reference to x is ambiguous, - | it is both defined in object Test + | Reference to x is ambiguous. + | It is both defined in object Test | and inherited subsequently in anonymous class test1.C {...} | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:17:14 --------------------------------------------------------------- 17 | println(y) // error | ^ - | Reference to y is ambiguous, - | it is both defined in method c + | Reference to y is ambiguous. + | It is both defined in method c | and inherited subsequently in anonymous class D {...} | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:25:16 --------------------------------------------------------------- 25 | println(y) // error | ^ - | Reference to y is ambiguous, - | it is both defined in method c + | Reference to y is ambiguous. + | It is both defined in method c | and inherited subsequently in class E | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12682.check b/tests/neg/i12682.check new file mode 100644 index 000000000000..3b421b44ebe5 --- /dev/null +++ b/tests/neg/i12682.check @@ -0,0 +1,45 @@ +-- [E049] Reference Error: tests/neg/i12682.scala:6:12 ----------------------------------------------------------------- +6 | val x = m(1) // error + | ^ + | Reference to m is ambiguous. + | It is both defined in object C + | and inherited subsequently in object T + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The identifier m is ambiguous because a binding of lower precedence + | in an inner scope cannot shadow a binding with higher precedence in + | an outer scope. + | + | The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports + | - Named imports take precedence over wildcard imports + | - Definitions from packages in other files + | Note: + | - When importing, you can avoid naming conflicts by renaming: + | import scala.{m => mTick} + --------------------------------------------------------------------------------------------------------------------- +-- [E049] Reference Error: tests/neg/i12682.scala:13:10 ---------------------------------------------------------------- +13 | def d = m(42) // error + | ^ + | Reference to m is ambiguous. + | It is both imported by import X._ + | and imported subsequently by import Y._ + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The identifier m is ambiguous because two bindings of equal precedence + | were introduced in the same scope. + | + | The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports + | - Named imports take precedence over wildcard imports + | - Definitions from packages in other files + | Note: + | - When importing, you can avoid naming conflicts by renaming: + | import scala.{m => mTick} + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i12682.scala b/tests/neg/i12682.scala new file mode 100644 index 000000000000..0b37816ef0df --- /dev/null +++ b/tests/neg/i12682.scala @@ -0,0 +1,13 @@ +// scalac: -explain + +object C: + def m(x: Int) = 1 + object T extends K: + val x = m(1) // error +class K: + def m(i: Int) = 2 +object X extends K +object Y extends K +object D: + import X.*, Y.* + def d = m(42) // error diff --git a/tests/neg/i13558.check b/tests/neg/i13558.check index ab10a42cdd32..7b4b5215a0a3 100644 --- a/tests/neg/i13558.check +++ b/tests/neg/i13558.check @@ -8,8 +8,8 @@ | | failed with: | - | Reference to id is ambiguous, - | it is both imported by import testcode.ExtensionB._ + | Reference to id is ambiguous. + | It is both imported by import testcode.ExtensionB._ | and imported subsequently by import testcode.ExtensionA._ -- [E008] Not Found Error: tests/neg/i13558.scala:29:14 ---------------------------------------------------------------- 29 | println(a.id) // error @@ -21,6 +21,6 @@ | | failed with: | - | Reference to id is ambiguous, - | it is both imported by import testcode.ExtensionA._ + | Reference to id is ambiguous. + | It is both imported by import testcode.ExtensionA._ | and imported subsequently by import testcode.ExtensionB._ diff --git a/tests/neg/i9803.check b/tests/neg/i9803.check index cc7d56d585b0..20225f1f5bc5 100644 --- a/tests/neg/i9803.check +++ b/tests/neg/i9803.check @@ -1,8 +1,8 @@ -- [E049] Reference Error: tests/neg/i9803.scala:15:10 ----------------------------------------------------------------- 15 | println(f421()) // error | ^^^^ - | Reference to f421 is ambiguous, - | it is both imported by name by import bugs.shadowing.x.f421 + | Reference to f421 is ambiguous. + | It is both imported by name by import bugs.shadowing.x.f421 | and imported by name subsequently by import bugs.shadowing.y.f421 | | longer explanation available when compiling with `-explain` From 363996c3cafab13f7ef3988aee0a6be69a1a9f19 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 18 Feb 2023 10:07:07 -0800 Subject: [PATCH 2/2] Retain and expand new rule help, add more words --- .../dotty/tools/dotc/reporting/messages.scala | 13 ++++++----- tests/neg/i12682.check | 22 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 92ee1b44def0..a82e49a048ab 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1334,21 +1334,24 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec def explain(using Context) = val precedent = - if newPrec == prevPrec then """two bindings of equal precedence + if newPrec == prevPrec then """two name bindings of equal precedence |were introduced in the same scope.""".stripMargin - else """a binding of lower precedence + else """a name binding of lower precedence |in an inner scope cannot shadow a binding with higher precedence in |an outer scope.""".stripMargin i"""|The identifier $name is ambiguous because $precedent | - |The precedence of the different kinds of bindings, from highest to lowest, is: + |The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files |Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | ${hl("import")} scala.{$name => ${name.show}Tick} |""" diff --git a/tests/neg/i12682.check b/tests/neg/i12682.check index 3b421b44ebe5..605414938529 100644 --- a/tests/neg/i12682.check +++ b/tests/neg/i12682.check @@ -7,17 +7,20 @@ |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | The identifier m is ambiguous because a binding of lower precedence + | The identifier m is ambiguous because a name binding of lower precedence | in an inner scope cannot shadow a binding with higher precedence in | an outer scope. | - | The precedence of the different kinds of bindings, from highest to lowest, is: + | The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files | Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | import scala.{m => mTick} --------------------------------------------------------------------------------------------------------------------- @@ -30,16 +33,19 @@ |-------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | The identifier m is ambiguous because two bindings of equal precedence + | The identifier m is ambiguous because two name bindings of equal precedence | were introduced in the same scope. | - | The precedence of the different kinds of bindings, from highest to lowest, is: + | The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files | Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | import scala.{m => mTick} --------------------------------------------------------------------------------------------------------------------