From a6d5eb507bbeac2055a224a15fd76e7f9425520b Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 4 May 2016 15:52:01 -0700 Subject: SI-8667 Improve too-many-args message Use removeNames to help diagnose the application. Supplement the error message with how many extra args and any other residual assignments that the user might have thought was a properly named arg. The error message is gradual: succinct for short arg lists, more verbose for longer applications. Very long arg lists are probably generated, so that message is the least colloquial. --- .../tools/nsc/typechecker/ContextErrors.scala | 31 +++++++++++++++++++--- .../scala/tools/nsc/typechecker/Typers.scala | 10 +++++-- 2 files changed, 36 insertions(+), 5 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index e190b57017..e1055144f8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -538,8 +538,33 @@ trait ContextErrors { def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = NormalTypeError(tree, "macro applications do not support named and/or default arguments") - def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = - NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) + def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree, expected: Int, supplied: Int, unknowns: List[Name]) = { + val msg = { + val badappl = { + val excess = supplied - expected + val target = treeSymTypeMsg(fun) + + if (expected == 0) s"no arguments allowed for nullary $target" + else if (excess < 3 && expected <= 5) s"too many arguments ($supplied) for $target" + else if (expected > 10) s"$supplied arguments but expected $expected for $target" + else { + val oneOf = + if (excess == 1) "one more argument" + else if (excess > 0) s"$excess more arguments" + else "too many arguments" + s"$oneOf than can be applied to $target" + } + } + val suppl = + unknowns.size match { + case 0 => "" + case 1 => s"\nNote that '${unknowns.head}' is not a parameter name of the invoked method." + case _ => unknowns.mkString("\nNote that '", "', '", "' are not parameter names of the invoked method.") + } + s"${badappl}${suppl}" + } + NormalTypeError(tree, msg) + } // can it still happen? see test case neg/overloaded-unapply.scala def OverloadedUnapplyError(tree: Tree) = @@ -551,7 +576,7 @@ trait ContextErrors { def MultipleVarargError(tree: Tree) = NormalTypeError(tree, "when using named arguments, the vararg parameter has to be specified exactly once") - def ModuleUsingCompanionClassDefaultArgsErrror(tree: Tree) = + def ModuleUsingCompanionClassDefaultArgsError(tree: Tree) = NormalTypeError(tree, "module extending its companion class cannot use default constructor arguments") def NotEnoughArgsError(tree: Tree, fun: Tree, missing: List[Symbol]) = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 329ce8c23b..1d24d8c232 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3330,7 +3330,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // #2064 duplErrorTree(WrongNumberOfArgsError(tree, fun)) } else if (lencmp > 0) { - tryTupleApply orElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun)) + tryTupleApply orElse duplErrorTree { + val (namelessArgs, _) = removeNames(Typer.this)(args, params) + val wrongs = (namelessArgs zip args) collect { + case (_: Assign, AssignOrNamedArg(Ident(name), _)) => name + } + TooManyArgsNamesDefaultsError(tree, fun, expected = formals.size, supplied = args.size, wrongs) + } } else if (lencmp == 0) { // we don't need defaults. names were used, so this application is transformed // into a block (@see transformNamedApplication in NamesDefaults) @@ -3394,7 +3400,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val lencmp2 = compareLengths(allArgs, formals) if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) { - duplErrorTree(ModuleUsingCompanionClassDefaultArgsErrror(tree)) + duplErrorTree(ModuleUsingCompanionClassDefaultArgsError(tree)) } else if (lencmp2 > 0) { removeNames(Typer.this)(allArgs, params) // #3818 duplErrTree -- cgit v1.2.3