Skip to content

Add an error message class for a case where a trait is expected #3465

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

Merged
merged 3 commits into from
Nov 14, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ public enum ErrorMessageID {
MissingEmptyArgumentListID,
DuplicateNamedTypeParameterID,
UndefinedNamedTypeParameterID,
IllegalStartOfStatementID
IllegalStartOfStatementID,
TraitIsExpectedID,
;

public int errorNumber() {
Expand Down
28 changes: 28 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1888,4 +1888,32 @@ object messages {
}
val explanation = "A statement is either an import, a definition or an expression."
}

case class TraitIsExpected(symbol: Symbol)(implicit ctx: Context) extends Message(TraitIsExpectedID) {
val kind = "Syntax"
val msg = hl"$symbol is not a trait"
val explanation = {
val errorCodeExample =
"""class A
|class B
|
|val a = new A with B // will fail with a compile error - class B is not a trait""".stripMargin
val codeExample =
"""class A
|trait B
|
|val a = new A with B // compiles normally""".stripMargin

hl"""Only traits can be mixed into classes using a ${"with"} keyword.
|Consider the following example:
|
|$errorCodeExample
|
|The example mentioned above would fail because B is not a trait.
|But if you make B a trait it will be compiled without any errors:
|
|$codeExample
|"""
}
}
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ trait Checking {
def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type =
tp.underlyingClassRef(refinementOK = false) match {
case tref: TypeRef =>
if (traitReq && !(tref.symbol is Trait)) ctx.error(ex"$tref is not a trait", pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(TraitIsExpected(tref.symbol), pos)
if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
tp
case _ =>
Expand Down
21 changes: 21 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1151,4 +1151,25 @@ class ErrorMessagesTests extends ErrorMessagesTest {
assertEquals(IllegalStartOfStatement(isModifier = false), err)
assertEquals(IllegalStartOfStatement(isModifier = true), errWithModifier)
}

@Test def traitIsExpected =
checkMessagesAfter("frontend") {
"""
|class A
|class B
|
|object Test {
| def main(args: Array[String]): Unit = {
| val a = new A with B
| }
|}
""".stripMargin
}
.expect { (ictx, messages) =>
implicit val ctx: Context = ictx

assertMessageCount(1, messages)
val TraitIsExpected(symbol) :: Nil = messages
assertEquals("class B", symbol.show)
}
}