diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 262f0d5f35c0..6fe02fae9b2f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -17,6 +17,7 @@ import util.Property import collection.mutable import ast.tpd._ import reporting.trace +import reporting.diagnostic.Message trait TypeOps { this: Context => // TODO: Make standalone object. @@ -315,7 +316,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def dynamicsEnabled = featureEnabled(defn.LanguageModuleClass, nme.dynamics) - def testScala2Mode(msg: => String, pos: Position, rewrite: => Unit = ()) = { + def testScala2Mode(msg: => Message, pos: Position, rewrite: => Unit = ()) = { if (scala2Mode) { migrationWarning(msg, pos) rewrite diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 618e1857902c..09ca64e7fa8e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -106,7 +106,8 @@ public enum ErrorMessageID { ClassAndCompanionNameClashID, TailrecNotApplicableID, FailureToEliminateExistentialID, - OnlyFunctionsCanBeFollowedByUnderscoreID + OnlyFunctionsCanBeFollowedByUnderscoreID, + MissingEmptyArgumentListID ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index aec64be3468a..74ace0a629f2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1847,4 +1847,22 @@ object messages { hl"""The syntax ${"x _"} is no longer supported if ${"x"} is not a function. |To convert to a function value, you need to explicitly write ${"() => x"}""" } + + case class MissingEmptyArgumentList(method: Symbol)(implicit ctx: Context) + extends Message(MissingEmptyArgumentListID) { + val kind = "Syntax" + val msg = hl"$method must be called with ${"()"} argument" + val explanation = { + val codeExample = + """def next(): T = ... + |next // is expanded to next()""" + + hl"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. + | + |$codeExample + | + |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. + |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" + } + } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7c6250689d23..b75a8cc75949 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1908,7 +1908,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def methodStr = err.refStr(methPart(tree).tpe) def missingArgs(mt: MethodType) = { - ctx.error(em"missing arguments for $methodStr", tree.pos) + ctx.error(MissingEmptyArgumentList(methPart(tree).symbol), tree.pos) tree.withType(mt.resultType) } @@ -2076,7 +2076,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def isAutoApplied(sym: Symbol): Boolean = { sym.isConstructor || sym.matchNullaryLoosely || - ctx.testScala2Mode(em"${sym.showLocated} requires () argument", tree.pos, + ctx.testScala2Mode(MissingEmptyArgumentList(sym), tree.pos, patch(tree.pos.endPos, "()")) } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index e261c7e7c339..ca3bc6b06585 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -1069,4 +1069,23 @@ class ErrorMessagesTests extends ErrorMessagesTest { val OnlyFunctionsCanBeFollowedByUnderscore(pt) :: Nil = messages assertEquals("String(n)", pt.show) } + + @Test def missingEmptyArgumentList = + checkMessagesAfter("frontend") { + """ + |class Test { + | def greet(): String = "Hello" + | def main(args: Array[String]): Unit = { + | greet + | } + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + + assertMessageCount(1, messages) + val MissingEmptyArgumentList(method) :: Nil = messages + assertEquals("method greet", method.show) + } }