Skip to content

Commit d0cc4c1

Browse files
authored
Issue "positional after named argument" errors (#18363)
Issue "positional after named argument" errors if a positional argument follows a named argument and one of the following is true: - There is a formal argument before the argument position that has not yet been instantiated with a previous actual argument, (either named or positional), or - The formal parameter at the argument position is also mentioned in a subsequent named parameter. This brings the behavior largely in line with Scala 2. Fixes #18122
2 parents 3212890 + 91e56de commit d0cc4c1

File tree

4 files changed

+128
-10
lines changed

4 files changed

+128
-10
lines changed

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

+26-9
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,21 @@ trait Applications extends Compatibility {
503503
def infoStr = if methType.isErroneous then "" else i": $methType"
504504
i"${err.refStr(methRef)}$infoStr"
505505

506-
/** Re-order arguments to correctly align named arguments */
506+
/** Re-order arguments to correctly align named arguments
507+
* Issue errors in the following situations:
508+
*
509+
* - "positional after named argument" if a positional argument follows a named
510+
* argument and one of the following is true:
511+
*
512+
* - There is a formal argument before the argument position
513+
* that has not yet been instantiated with a previous actual argument,
514+
* (either named or positional), or
515+
* - The formal parameter at the argument position is also mentioned
516+
* in a subsequent named parameter.
517+
* - "parameter already instantiated" if a two named arguments have the same name.
518+
* - "does not have parameter" if a named parameter does not mention a formal
519+
* parameter name.
520+
*/
507521
def reorder[T <: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
508522

509523
/** @param pnames The list of parameter names that are missing arguments
@@ -517,40 +531,43 @@ trait Applications extends Compatibility {
517531
* 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
518532
*/
519533
def handleNamed(pnames: List[Name], args: List[Trees.Tree[T]],
520-
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match {
534+
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name],
535+
missingArgs: Boolean): List[Trees.Tree[T]] = pnames match {
521536
case pname :: pnames1 if nameToArg contains pname =>
522537
// there is a named argument for this parameter; pick it
523-
nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname)
538+
nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname, missingArgs)
524539
case _ =>
525540
def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
526541
args match {
527542
case (arg @ NamedArg(aname, _)) :: args1 =>
528543
if (toDrop contains aname) // argument is already passed
529-
handleNamed(pnames, args1, nameToArg, toDrop - aname)
544+
handleNamed(pnames, args1, nameToArg, toDrop - aname, missingArgs)
530545
else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree
531-
genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop)
546+
genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop, missingArgs = true)
532547
else { // name not (or no longer) available for named arg
533548
def msg =
534549
if (methodType.paramNames contains aname)
535550
em"parameter $aname of $methString is already instantiated"
536551
else
537552
em"$methString does not have a parameter $aname"
538553
fail(msg, arg.asInstanceOf[Arg])
539-
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop)
554+
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs)
540555
}
541556
case arg :: args1 =>
542-
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it
557+
if toDrop.nonEmpty || missingArgs then
558+
report.error(i"positional after named argument", arg.srcPos)
559+
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
543560
case Nil => // no more args, continue to pick up any preceding named args
544561
if (pnames.isEmpty) Nil
545-
else handleNamed(pnamesRest, args, nameToArg, toDrop)
562+
else handleNamed(pnamesRest, args, nameToArg, toDrop, missingArgs)
546563
}
547564
}
548565

549566
def handlePositional(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] =
550567
args match {
551568
case (arg: NamedArg @unchecked) :: _ =>
552569
val nameAssocs = for (case arg @ NamedArg(name, _) <- args) yield (name, arg)
553-
handleNamed(pnames, args, nameAssocs.toMap, Set())
570+
handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set(), missingArgs = false)
554571
case arg :: args1 =>
555572
arg :: handlePositional(if (pnames.isEmpty) Nil else pnames.tail, args1)
556573
case Nil => Nil

tests/neg/i18122.check

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
-- Error: tests/neg/i18122.scala:10:16 ---------------------------------------------------------------------------------
2+
10 | foo1(y = 1, 2, x = 3) // error: positional after named
3+
| ^
4+
| positional after named argument
5+
-- Error: tests/neg/i18122.scala:11:16 ---------------------------------------------------------------------------------
6+
11 | foo2(y = 1, 2, x = 3) // error: positional after named
7+
| ^
8+
| positional after named argument
9+
-- Error: tests/neg/i18122.scala:12:16 ---------------------------------------------------------------------------------
10+
12 | foo1(y = 1, 2, z = 3) // error: positional after named
11+
| ^
12+
| positional after named argument
13+
-- Error: tests/neg/i18122.scala:13:16 ---------------------------------------------------------------------------------
14+
13 | foo2(y = 1, 2, z = 3) // error: positional after named
15+
| ^
16+
| positional after named argument
17+
-- Error: tests/neg/i18122.scala:14:16 ---------------------------------------------------------------------------------
18+
14 | foo1(y = 1, 2) // error: positional after named
19+
| ^
20+
| positional after named argument
21+
-- Error: tests/neg/i18122.scala:15:16 ---------------------------------------------------------------------------------
22+
15 | foo2(y = 1, 2) // error: positional after named
23+
| ^
24+
| positional after named argument
25+
-- [E171] Type Error: tests/neg/i18122.scala:17:8 ----------------------------------------------------------------------
26+
17 | bar1() // error: missing arg
27+
| ^^^^^^
28+
| missing argument for parameter x of method bar1 in object Test: (x: Int, ys: Int*): Unit
29+
-- [E171] Type Error: tests/neg/i18122.scala:23:8 ----------------------------------------------------------------------
30+
23 | bar1(ys = 1) // error: missing arg
31+
| ^^^^^^^^^^^^
32+
| missing argument for parameter x of method bar1 in object Test: (x: Int, ys: Int*): Unit
33+
-- Error: tests/neg/i18122.scala:43:16 ---------------------------------------------------------------------------------
34+
43 | bar1(x = 1, 2, ys = 3) // error: positional after named
35+
| ^
36+
| positional after named argument
37+
-- Error: tests/neg/i18122.scala:44:18 ---------------------------------------------------------------------------------
38+
44 | bar1(1, 2, ys = 3) // error: parameter ys is already instantiated
39+
| ^^^^^^
40+
| parameter ys of method bar1 in object Test: (x: Int, ys: Int*): Unit is already instantiated
41+
-- Error: tests/neg/i18122.scala:45:16 ---------------------------------------------------------------------------------
42+
45 | bar2(x = 1, 2, ys = 3) // error: positional after named
43+
| ^
44+
| positional after named argument
45+
-- Error: tests/neg/i18122.scala:46:17 ---------------------------------------------------------------------------------
46+
46 | bar1(ys = 1, 2, x = 3) // error: positional after named
47+
| ^
48+
| positional after named argument
49+
-- Error: tests/neg/i18122.scala:47:17 ---------------------------------------------------------------------------------
50+
47 | bar2(ys = 1, 2, x = 3) // error: positional after named
51+
| ^
52+
| positional after named argument

tests/neg/i18122.scala

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
object Test {
2+
def foo1(x: Int, y: Int, z: Int) = println((x, y, z))
3+
def foo2(x: Int = 0, y: Int, z: Int) = println((x, y, z))
4+
def bar1(x: Int, ys: Int*) = println((x, ys))
5+
def bar2(x: Int = 0, ys: Int*) = println((x, ys))
6+
7+
def main(args: Array[String]) = {
8+
foo1(1, y = 2, 3)
9+
foo2(1, y = 2, 3)
10+
foo1(y = 1, 2, x = 3) // error: positional after named
11+
foo2(y = 1, 2, x = 3) // error: positional after named
12+
foo1(y = 1, 2, z = 3) // error: positional after named
13+
foo2(y = 1, 2, z = 3) // error: positional after named
14+
foo1(y = 1, 2) // error: positional after named
15+
foo2(y = 1, 2) // error: positional after named
16+
17+
bar1() // error: missing arg
18+
bar2()
19+
bar1(1)
20+
bar2(1)
21+
bar1(x = 1)
22+
bar2(x = 1)
23+
bar1(ys = 1) // error: missing arg
24+
bar2(ys = 1)
25+
bar1(1, 2)
26+
bar2(1, 2)
27+
bar1(1, ys = 2)
28+
bar2(1, ys = 2)
29+
bar1(x = 1, 2)
30+
bar2(x = 1, 2)
31+
bar1(x = 1, ys = 2)
32+
bar2(x = 1, ys = 2)
33+
bar1(ys = 1, x = 2)
34+
bar2(ys = 1, x = 2)
35+
bar1(1, 2, 3)
36+
bar2(1, 2, 3)
37+
bar1(1, ys = 2, 3)
38+
bar2(1, ys = 2, 3)
39+
bar1(x = 1, 2, 3)
40+
bar2(x = 1, 2, 3)
41+
bar1(x = 1, ys = 2, 3)
42+
bar2(x = 1, ys = 2, 3)
43+
bar1(x = 1, 2, ys = 3) // error: positional after named
44+
bar1(1, 2, ys = 3) // error: parameter ys is already instantiated
45+
bar2(x = 1, 2, ys = 3) // error: positional after named
46+
bar1(ys = 1, 2, x = 3) // error: positional after named
47+
bar2(ys = 1, 2, x = 3) // error: positional after named
48+
}
49+
}

tests/pos-with-compiler-cc/dotc/transform/PickleQuotes.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class PickleQuotes extends MacroTransform {
134134
contents += content
135135
val holeType =
136136
if isTerm then getTermHoleType(tree.tpe) else getTypeHoleType(tree.tpe)
137-
val hole = cpy.Hole(tree)(content = EmptyTree, TypeTree(holeType))
137+
val hole = cpy.Hole(tree)(content = EmptyTree, tpt = TypeTree(holeType))
138138
if isTerm then Inlined(EmptyTree, Nil, hole).withSpan(tree.span) else hole
139139
case tree: DefTree =>
140140
val newAnnotations = tree.symbol.annotations.mapconserve { annot =>

0 commit comments

Comments
 (0)