Skip to content

Commit ad002ff

Browse files
authored
Merge pull request #4064 from jendrikw/error-double-definition
Add error message about double definition (#1589)
2 parents 990469e + e4c64b7 commit ad002ff

File tree

7 files changed

+177
-18
lines changed

7 files changed

+177
-18
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public enum ErrorMessageID {
127127
PolymorphicMethodMissingTypeInParentID,
128128
ParamsNoInlineID,
129129
JavaSymbolIsNotAValueID,
130+
DoubleDeclarationID,
130131
;
131132

132133
public int errorNumber() {

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

+16
Original file line numberDiff line numberDiff line change
@@ -2079,4 +2079,20 @@ object messages {
20792079
}
20802080
val explanation = ""
20812081
}
2082+
2083+
case class DoubleDeclaration(decl: Symbol, previousDecl: Symbol)(implicit ctx: Context) extends Message(DoubleDeclarationID) {
2084+
val kind = "Duplicate Symbol"
2085+
val msg = {
2086+
val details = if (decl.isRealMethod && previousDecl.isRealMethod) {
2087+
// compare the signatures when both symbols represent methods
2088+
decl.signature.matchDegree(previousDecl.signature) match {
2089+
/* case Signature.NoMatch => // can't happen because decl.matches(previousDecl) is checked before reporting this error */
2090+
case Signature.ParamMatch => "\nOverloads with matching parameter types are not allowed."
2091+
case _ /* Signature.FullMatch */ => "\nThe definitions have matching type signatures after erasure."
2092+
}
2093+
} else ""
2094+
hl"${decl.showLocated} is already defined as ${previousDecl.showDcl} at line ${previousDecl.pos.line + 1}." + details
2095+
}
2096+
val explanation = ""
2097+
}
20822098
}

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

+5-9
Original file line numberDiff line numberDiff line change
@@ -613,23 +613,19 @@ trait Checking {
613613
}
614614
}
615615

616-
/** Check that class does not define same symbol twice */
617-
def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
616+
/** Check that class does not declare same symbol twice */
617+
def checkNoDoubleDeclaration(cls: Symbol)(implicit ctx: Context): Unit = {
618618
val seen = new mutable.HashMap[Name, List[Symbol]] {
619619
override def default(key: Name) = Nil
620620
}
621-
typr.println(i"check no double defs $cls")
621+
typr.println(i"check no double declarations $cls")
622622

623623
def checkDecl(decl: Symbol): Unit = {
624624
for (other <- seen(decl.name)) {
625625
typr.println(i"conflict? $decl $other")
626626
if (decl.matches(other)) {
627627
def doubleDefError(decl: Symbol, other: Symbol): Unit = {
628-
def ofType = if (decl.isType) "" else em": ${other.info}"
629-
def explanation =
630-
if (!decl.isRealMethod) ""
631-
else "\n(the definitions have matching type signatures)"
632-
ctx.error(em"$decl is already defined as $other$ofType$explanation", decl.pos)
628+
ctx.error(DoubleDeclaration(decl, other), decl.pos)
633629
}
634630
if (decl is Synthetic) doubleDefError(other, decl)
635631
else doubleDefError(decl, other)
@@ -872,7 +868,7 @@ trait NoChecking extends ReChecking {
872868
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
873869
override def checkFeasibleParent(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
874870
override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = ()
875-
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
871+
override def checkNoDoubleDeclaration(cls: Symbol)(implicit ctx: Context): Unit = ()
876872
override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
877873
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
878874
override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ class Typer extends Namer
15121512
// Expand comments and type usecases
15131513
cookComments(body1.map(_.symbol), self1.symbol)(ctx.localContext(cdef, cls).setNewScope)
15141514

1515-
checkNoDoubleDefs(cls)
1515+
checkNoDoubleDeclaration(cls)
15161516
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
15171517
.withType(dummy.termRef)
15181518
checkVariance(impl1)

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

+17-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ package dotty.tools
22
package dotc
33
package reporting
44

5-
import core.Contexts.Context
6-
import diagnostic.messages._
7-
import dotty.tools.dotc.core.Flags
8-
import dotty.tools.dotc.core.Flags.FlagSet
5+
import dotty.tools.dotc.core.Contexts.Context
96
import dotty.tools.dotc.core.Types.WildcardType
107
import dotty.tools.dotc.parsing.Tokens
8+
import dotty.tools.dotc.reporting.diagnostic.messages._
119
import org.junit.Assert._
1210
import org.junit.Test
1311

@@ -1293,4 +1291,19 @@ class ErrorMessagesTests extends ErrorMessagesTest {
12931291

12941292
assert(ctx.reporter.hasErrors)
12951293
}
1294+
1295+
@Test def typeDoubleDeclaration =
1296+
checkMessagesAfter("frontend") {
1297+
"""
1298+
|class Foo {
1299+
| val a = 1
1300+
| val a = 2
1301+
|}
1302+
""".stripMargin
1303+
}.expect { (ictx, messages) =>
1304+
implicit val ctx: Context = ictx
1305+
assertMessageCount(1, messages)
1306+
val DoubleDeclaration(symbol, previousSymbol) :: Nil = messages
1307+
assertEquals(symbol.name.mangledString, "a")
1308+
}
12961309
}

tests/neg/doubleDefinition.scala

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
trait A
2+
trait B
3+
4+
// test with classes
5+
6+
class Test1 {
7+
def foo(x: List[A]): Function1[A, A] = ???
8+
def foo(x: List[B]): Function2[B, B, B] = ???
9+
// ok, different jvm signature
10+
}
11+
12+
class Test2 {
13+
def foo(x: List[A]): Function1[A, A] = ???
14+
def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature
15+
// scalac calls this "have same type after erasure"
16+
}
17+
18+
class Test3 {
19+
// overload with same argument type, but different return types
20+
def foo(x: List[A]): Function1[A, A] = ???
21+
def foo(x: List[A]): Function2[B, B, B] = ??? // error
22+
}
23+
24+
class Test4 {
25+
val foo = 1
26+
def foo = 2 // error
27+
}
28+
29+
class Test4b {
30+
def foo = 2
31+
val foo = 1 // error
32+
}
33+
34+
class Test4c {
35+
def foo = 2
36+
var foo = 1 // error
37+
}
38+
39+
class Test4d {
40+
var foo = 1
41+
def foo = 2 // error
42+
}
43+
44+
45+
// test with traits
46+
47+
trait Test5 {
48+
def foo(x: List[A]): Function1[A, A] = ???
49+
def foo(x: List[B]): Function2[B, B, B] = ???
50+
// ok, different jvm signature
51+
}
52+
53+
trait Test6 {
54+
def foo(x: List[A]): Function1[A, A] = ???
55+
def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature
56+
// scalac calls this "have same type after erasure"
57+
}
58+
59+
trait Test7 {
60+
// overload with same argument type, but different return types
61+
def foo(x: List[A]): Function1[A, A] = ???
62+
def foo(x: List[A]): Function2[B, B, B] = ??? // error
63+
}
64+
65+
class Test8 {
66+
val foo = 1
67+
def foo = 2 // error
68+
}
69+
70+
class Test8b {
71+
def foo = 2
72+
val foo = 1 // error
73+
}
74+
75+
class Test8c {
76+
def foo = 2
77+
var foo = 1 // error
78+
}
79+
80+
class Test8d {
81+
var foo = 1
82+
def foo = 2 // error
83+
}
84+
85+
// test method and contructor argument clashing
86+
87+
class Test9(val foo: Int) {
88+
def foo: String // error
89+
}
90+
91+
class Test10(val foo: Int) {
92+
def foo: Int // error
93+
}
94+
95+
abstract class Test11(val foo: Int) {
96+
def foo: String // error
97+
}
98+
99+
abstract class Test12(val foo: Int) {
100+
def foo: Int // error
101+
}
102+
103+
class Test13(var foo: Int) {
104+
def foo: String // error
105+
}
106+
107+
class Test14(var foo: Int) {
108+
def foo: Int // error
109+
}
110+
111+
abstract class Test15(var foo: Int) {
112+
def foo: String // error
113+
}
114+
115+
abstract class Test16(var foo: Int) {
116+
def foo: Int // error
117+
}
118+
119+
// don't error when shadowing
120+
121+
class Test17 {
122+
val foo = 1
123+
def bar() = {
124+
val foo = ""
125+
}
126+
}
127+
128+
// no error when overloading
129+
130+
class Test18 {
131+
def foo(a: A) = 1
132+
def foo(b: B) = 1
133+
}

tests/neg/singletonOrs.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object Test {
2-
def foo: 1 | 2 = 1 // error // error
3-
def bar: 3 | 4 = foo // error // error
4-
def foo: 1 | 2 = 1 // error // error
5-
def bar: 1 = foo
2+
def a: 1 | 2 = 1 // error // error
3+
def b: 3 | 4 = a // error // error
4+
def c: 1 | 2 = 1 // error // error
5+
def d: 1 = a
66
}

0 commit comments

Comments
 (0)