Skip to content

Commit 9e82d56

Browse files
committed
Improve double definition error reporting
- Rename the error from DoubleDeclaration to DoubleDefinition so that we can also use it in post-erasure checks. - Display line numbers only when they make sense - Display the type signatures as written by the user
1 parent d8c1ff7 commit 9e82d56

File tree

6 files changed

+106
-14
lines changed

6 files changed

+106
-14
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public enum ErrorMessageID {
128128
PolymorphicMethodMissingTypeInParentID,
129129
ParamsNoInlineID,
130130
JavaSymbolIsNotAValueID,
131-
DoubleDeclarationID,
131+
DoubleDefinitionID,
132132
MatchCaseOnlyNullWarningID,
133133
ImportRenamedTwiceID,
134134
TypeTestAlwaysSucceedsID,

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,18 +2137,30 @@ object messages {
21372137
val explanation: String = ""
21382138
}
21392139

2140-
case class DoubleDeclaration(decl: Symbol, previousDecl: Symbol)(implicit ctx: Context) extends Message(DoubleDeclarationID) {
2140+
case class DoubleDefinition(decl: Symbol, previousDecl: Symbol)(implicit ctx: Context) extends Message(DoubleDefinitionID) {
21412141
val kind: String = "Duplicate Symbol"
21422142
val msg: String = {
21432143
val details = if (decl.isRealMethod && previousDecl.isRealMethod) {
21442144
// compare the signatures when both symbols represent methods
21452145
decl.signature.matchDegree(previousDecl.signature) match {
2146-
/* case Signature.NoMatch => // can't happen because decl.matches(previousDecl) is checked before reporting this error */
2147-
case Signature.ParamMatch => "\nOverloads with matching parameter types are not allowed."
2148-
case _ /* Signature.FullMatch */ => "\nThe definitions have matching type signatures after erasure."
2146+
case Signature.NoMatch =>
2147+
"" // shouldn't be reachable
2148+
case Signature.ParamMatch =>
2149+
"have matching parameter types."
2150+
case Signature.FullMatch =>
2151+
"have the same type after erasure."
21492152
}
21502153
} else ""
2151-
hl"${decl.showLocated} is already defined as ${previousDecl.showDcl} ${if (previousDecl.span.exists) s"at line ${previousDecl.sourcePos.line + 1}" else ""}." + details
2154+
def symLocation(sym: Symbol) = {
2155+
val lineDesc =
2156+
if (sym.span.exists && sym.span != sym.owner.span)
2157+
s" at line ${sym.sourcePos.line + 1}" else ""
2158+
i"in ${sym.owner}${lineDesc}"
2159+
}
2160+
em"""Double definition:
2161+
|${previousDecl.initial.showDcl} ${symLocation(previousDecl)} and
2162+
|${decl.initial.showDcl} ${symLocation(decl)}
2163+
|""" + details
21522164
}
21532165
val explanation: String = ""
21542166
}

compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import MegaPhase._
77
import Types._, Contexts._, Flags._, DenotTransformers._
88
import Symbols._, StdNames._, Trees._
99
import TypeErasure.ErasedValueType, ValueClasses._
10+
import reporting.diagnostic.messages.DoubleDefinition
1011

1112
object ElimErasedValueType {
1213
val name: String = "elimErasedValueType"
@@ -103,12 +104,7 @@ class ElimErasedValueType extends MiniPhase with InfoTransformer {
103104
//
104105
// The problem is that `map` was forwarded twice, with different instantiated types.
105106
// Maybe we should move mixin forwarding after erasure to avoid redundant forwarders like these.
106-
ctx.error(
107-
em"""double definition:
108-
|$sym1: $info1 in ${sym1.owner} and
109-
|$sym2: $info2 in ${sym2.owner}
110-
|have same type after erasure: $info""",
111-
root.sourcePos)
107+
ctx.error(DoubleDefinition(sym1, sym2), root.sourcePos)
112108
}
113109
val earlyCtx = ctx.withPhase(ctx.elimRepeatedPhase.next)
114110
while (opc.hasNext) {

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,7 @@ trait Checking {
755755
if (decl.matches(other) && !javaFieldMethodPair) {
756756
def doubleDefError(decl: Symbol, other: Symbol): Unit =
757757
if (!decl.info.isErroneous && !other.info.isErroneous)
758-
ctx.error(DoubleDeclaration(decl, other), decl.sourcePos)
758+
ctx.error(DoubleDefinition(decl, other), decl.sourcePos)
759759
if (decl is Synthetic) doubleDefError(other, decl)
760760
else doubleDefError(decl, other)
761761
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
15141514
}.expect { (ictx, messages) =>
15151515
implicit val ctx: Context = ictx
15161516
assertMessageCount(1, messages)
1517-
val DoubleDeclaration(symbol, previousSymbol) :: Nil = messages
1517+
val DoubleDefinition(symbol, previousSymbol) :: Nil = messages
15181518
assertEquals(symbol.name.mangledString, "a")
15191519
}
15201520

tests/neg/doubleDefinition.check

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<2016..2016> in doubleDefinition.scala
2+
Double definition:
3+
var foo: Int in class Test16 at line 115 and
4+
def foo: => Int in class Test16 at line 116
5+
<1949..1949> in doubleDefinition.scala
6+
Double definition:
7+
var foo: Int in class Test15 at line 111 and
8+
def foo: => String in class Test15 at line 112
9+
<1885..1885> in doubleDefinition.scala
10+
Double definition:
11+
var foo: Int in class Test14 at line 107 and
12+
def foo: => Int in class Test14 at line 108
13+
<1827..1827> in doubleDefinition.scala
14+
Double definition:
15+
var foo: Int in class Test13 at line 103 and
16+
def foo: => String in class Test13 at line 104
17+
<1772..1772> in doubleDefinition.scala
18+
Double definition:
19+
val foo: Int in class Test12 at line 99 and
20+
def foo: => Int in class Test12 at line 100
21+
<1705..1705> in doubleDefinition.scala
22+
Double definition:
23+
val foo: Int in class Test11 at line 95 and
24+
def foo: => String in class Test11 at line 96
25+
<1641..1641> in doubleDefinition.scala
26+
Double definition:
27+
val foo: Int in class Test10 at line 91 and
28+
def foo: => Int in class Test10 at line 92
29+
<1583..1583> in doubleDefinition.scala
30+
Double definition:
31+
val foo: Int in class Test9 at line 87 and
32+
def foo: => String in class Test9 at line 88
33+
<1481..1481> in doubleDefinition.scala
34+
Double definition:
35+
var foo: Int in class Test8d at line 81 and
36+
def foo: => Int in class Test8d at line 82
37+
<1428..1428> in doubleDefinition.scala
38+
Double definition:
39+
def foo: => Int in class Test8c at line 76 and
40+
var foo: Int in class Test8c at line 77
41+
<1375..1375> in doubleDefinition.scala
42+
Double definition:
43+
def foo: => Int in class Test8b at line 71 and
44+
val foo: Int in class Test8b at line 72
45+
<1322..1322> in doubleDefinition.scala
46+
Double definition:
47+
val foo: Int in class Test8 at line 66 and
48+
def foo: => Int in class Test8 at line 67
49+
<1236..1236> in doubleDefinition.scala
50+
Double definition:
51+
def foo(x: List[A]): A => A in trait Test7 at line 61 and
52+
def foo(x: List[A]): (B, B) => B in trait Test7 at line 62
53+
have matching parameter types.
54+
<984..984> in doubleDefinition.scala
55+
Double definition:
56+
def foo(x: List[A]): A => A in trait Test6 at line 54 and
57+
def foo(x: List[B]): B => B in trait Test6 at line 55
58+
have the same type after erasure.
59+
<739..739> in doubleDefinition.scala
60+
Double definition:
61+
var foo: Int in class Test4d at line 40 and
62+
def foo: => Int in class Test4d at line 41
63+
<686..686> in doubleDefinition.scala
64+
Double definition:
65+
def foo: => Int in class Test4c at line 35 and
66+
var foo: Int in class Test4c at line 36
67+
<633..633> in doubleDefinition.scala
68+
Double definition:
69+
def foo: => Int in class Test4b at line 30 and
70+
val foo: Int in class Test4b at line 31
71+
<580..580> in doubleDefinition.scala
72+
Double definition:
73+
val foo: Int in class Test4 at line 25 and
74+
def foo: => Int in class Test4 at line 26
75+
<494..494> in doubleDefinition.scala
76+
Double definition:
77+
def foo(x: List[A]): A => A in class Test3 at line 20 and
78+
def foo(x: List[A]): (B, B) => B in class Test3 at line 21
79+
have matching parameter types.
80+
<242..242> in doubleDefinition.scala
81+
Double definition:
82+
def foo(x: List[A]): A => A in class Test2 at line 13 and
83+
def foo(x: List[B]): B => B in class Test2 at line 14
84+
have the same type after erasure.

0 commit comments

Comments
 (0)