8000 Merge pull request #6089 from lrytz/syntacticNamedArgs · scala/scala@a74f0c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit a74f0c7

Browse files
authored
Merge pull request #6089 from lrytz/syntacticNamedArgs
Deprecate assignments in argument position
2 parents 25e294d + ef0daee commit a74f0c7

10 files changed

+125
-12
lines changed

src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,8 +1337,12 @@ trait ContextErrors {
13371337
context.warning(arg.pos, note)
13381338
}
13391339

1340-
def UnknownParameterNameNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = {
1341-
issueNormalTypeError(arg, "unknown parameter name: " + name)
1340+
def UnknownParameterNameNamesDefaultError(arg: Tree, name: Name, isVariableInScope: Boolean)(implicit context: Context) = {
1341+
val suffix =
1342+
if (isVariableInScope)
1343+
s"\nNote that assignments in argument position are no longer allowed since Scala 2.13.\nTo express the assignment expression, wrap it in brackets, e.g., `{ $name = ... }`."
1344+
else ""
1345+
issueNormalTypeError(arg, s"unknown parameter name: $name$suffix")
13421346
setError(arg)
13431347
}
13441348

src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,10 @@ trait NamesDefaults { self: Analyzer =>
486486
} else NoSymbol
487487
}
488488

489+
def isVariableInScope(context: Context, name: Name): Boolean = {
490+
context.lookupSymbol(name, _.isVariable).isSuccess
491+
}
492+
489493
/** A full type check is very expensive; let's make sure there's a name
490494
* somewhere which could potentially be ambiguous before we go that route.
491495
*/
@@ -593,19 +597,27 @@ trait NamesDefaults { self: Analyzer =>
593597
def stripNamedArg(arg: AssignOrNamedArg, argIndex: Int): Tree = {
594598
val AssignOrNamedArg(Ident(name), rhs) = arg
595599
params indexWhere (p => matchesName(p, name, argIndex)) match {
596-
case -1 if positionalAllowed =>
600+
case -1 if positionalAllowed && !settings.isScala213 =>
601+
if (isVariableInScope(context0, name)) {
602+
// only issue the deprecation warning if `name` is in scope, this avoids the warning when mis-spelling a parameter name.
603+
context0.deprecationWarning(
604+
arg.pos,
605+
context0.owner,
606+
s"assignments in argument position are deprecated in favor of named arguments. Wrap the assignment in brackets, e.g., `{ $name = ... }`.",
607+
"2.12.4")
608+
}
597609
// prevent isNamed from being true when calling doTypedApply recursively,
598610
// treat the arg as an assignment of type Unit
599611
Assign(arg.lhs, rhs) setPos arg.pos
600612
case -1 =>
601-
UnknownParameterNameNamesDefaultError(arg, name)
613+
UnknownParameterNameNamesDefaultError(arg, name, isVariableInScope(context0, name))
602614
case paramPos if argPos contains paramPos =>
603615
val existingArgIndex = argPos.indexWhere(_ == paramPos)
604616
val otherName = Some(args(paramPos)) collect {
605617
case AssignOrNamedArg(Ident(oName), _) if oName != name => oName
606618
}
607619
DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName)
608-
case paramPos if isAmbiguousAssignment(typer, params(paramPos), arg) =>
620+
case paramPos if !settings.isScala213 && isAmbiguousAssignment(typer, params(paramPos), arg) =>
609621
AmbiguousReferenceInNamesDefaultError(arg, name)
610622
case paramPos if paramPos != argIndex =>
611623
positionalAllowed = false // named arg is not in original parameter order: require names after this

test/files/neg/checksensible.check

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
checksensible.scala:45: warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.
2+
signature: Any.==(x$1: Any): Boolean
3+
given arguments: <none>
4+
after adaptation: Any.==((): Unit)
5+
() == ()
6+
^
7+
checksensible.scala:48: warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.
8+
signature: Object.!=(x$1: Any): Boolean
9+
given arguments: <none>
10+
after adaptation: Object.!=((): Unit)
11+
scala.runtime.BoxedUnit.UNIT != ()
12+
^
13+
checksensible.scala:49: warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.
14+
signature: Any.!=(x$1: Any): Boolean
15+
given arguments: <none>
16+
after adaptation: Any.!=((): Unit)
17+
(scala.runtime.BoxedUnit.UNIT: java.io.Serializable) != () // shouldn't warn
18+
^
119
checksensible.scala:13: warning: comparing a fresh object using `eq' will always yield false
220
(new AnyRef) eq (new AnyRef)
321
^
@@ -97,7 +115,6 @@ checksensible.scala:84: warning: comparing values of types EqEqRefTest.this.C3 a
97115
checksensible.scala:95: warning: comparing values of types Unit and Int using `!=' will always yield true
98116
while ((c = in.read) != -1)
99117
^
100-
warning: there were three deprecation warnings (since 2.11.0); re-run with -deprecation for details
101118
error: No warnings can be incurred under -Xfatal-warnings.
102-
34 warnings found
119+
36 warnings found
103120
one error found

test/files/neg/checksensible.flags

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
-Xfatal-warnings
1+
-Xfatal-warnings -deprecation
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
names-defaults-neg-213.scala:7: warning: a pure expression does nothing in statement position
2+
f1(x = 1) // named arg in 2.13 (value discard), not ambiguous
3+
^
4+
names-defaults-neg-213.scala:8: error: unknown parameter name: x
5+
Note that assignments in argument position are no longer allowed since Scala 2.13.
6+
To express the assignment expression, wrap it in brackets, e.g., `{ x = ... }`.
7+
f2(x = 1) // error, no parameter named x. error message mentions change in 2.13
8+
^
9+
names-defaults-neg-213.scala:13: warning: a pure expression does nothing in statement position
10+
f1(x = 1) // ok, named arg (value discard)
11+
^
12+
names-defaults-neg-213.scala:14: error: unknown parameter name: x
13+
f2(x = 1) // error (no such parameter). no mention of new semantics in 2.13
14+
^
15+
two warnings found
16+
two errors found
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class C {
2+
def f1(x: Unit): Int = 0
3+
def f2(y: Unit): Int = 0
4+
5+
def t1 = {
6+
var x = 0
7+
f1(x = 1) // named arg in 2.13 (value discard), not ambiguous
8+
f2(x = 1) // error, no parameter named x. error message mentions change in 2.13
9+
}
10+
11+
def t2 = {
12+
val x = 0
13+
f1(x = 1) // ok, named arg (value discard)
14+
f2(x = 1) // error (no such parameter). no mention of new semantics in 2.13
15+
}
16+
}

test/files/neg/names-defaults-neg-warn.check

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ names-defaults-neg-warn.scala:11: warning: the parameter name s is deprecated: u
44
names-defaults-neg-warn.scala:12: warning: the parameter name x is deprecated: use s instead
55
deprNam2.g(x = "dlkjf")
66
^
7-
error: No warnings can be incurred under -Xfatal-warnings.
8-
two warnings found
9-
one error found
7+
names-defaults-neg-warn.scala:22: error: reference to x is ambiguous; it is both a method parameter and a variable in scope.
8+
f1(x = 1) // 2.12: error, ambiguous (named arg or assign). 2.13: named arg
9+
^
10+
names-defaults-neg-warn.scala:23: warning: assignments in argument position are deprecated in favor of named arguments. Wrap the assignment in brackets, e.g., `{ x = ... }`.
11+
f2(x = 1) // 2.12: deprecation warning, compiles. 2.13: error, no parameter named x
12+
^
13+
names-defaults-neg-warn.scala:34: warning: assignments in argument position are deprecated in favor of named arguments. Wrap the assignment in brackets, e.g., `{ x = ... }`.
14+
synchronized(x = 1) // deprecation warning in 2.12, error in 2.13
15+
^
16+
names-defaults-neg-warn.scala:42: warning: a pure expression does nothing in statement position
17+
f1(x = 1) // 2.12, 2.13: ok, named arg (value discard)
18+
^
19+
names-defaults-neg-warn.scala:43: error: reassignment to val
20+
f2(x = 1) // 2.12, 2.13: error (no such parameter). no deprecation warning in 2.12, x is not a variable.
21+
^
22+
5 warnings found
23+
two errors found

test/files/neg/names-defaults-neg-warn.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,34 @@ object Test extends App {
1212
deprNam2.g(x = "dlkjf")
1313
deprNam2.g(s = new Object)
1414
}
15+
16+
class C {
17+
def f1(x: Unit): Unit = ()
18+
def f2(y: Unit): Unit = ()
19+
20+
def t1 = {
21+
var x = 0
22+
f1(x = 1) // 2.12: error, ambiguous (named arg or assign). 2.13: named arg
23+
f2(x = 1) // 2.12: deprecation warning, compiles. 2.13: error, no parameter named x
24+
25+
// all of the following are assignments to x
26+
27+
f1((x = 1))
28+
f2((x = 1))
29+
f1({ x = 1 })
30+
f2({ x = 1 })
31+
f1 { x = 1 }
32+
f2 { x = 1 }
33+
34+
synchronized(x = 1) // deprecation warning in 2.12, error in 2.13
35+
synchronized((x = 1)) // ok
36+
synchronized({ x = 1 }) // ok
37+
synchronized { x = 1 } // ok
38+
}
39+
40+
def t2 = {
41+
val x = 0
42+
f1(x = 1) // 2.12, 2.13: ok, named arg (value discard)
43+
f2(x = 1) // 2.12, 2.13: error (no such parameter). no deprecation warning in 2.12, x is not a variable.
44+
}
45+
}

test/files/run/names-defaults.check

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ names-defaults.scala:269: warning: a pure expression does nothing in statement p
44
names-defaults.scala:269: warning: multiline expressions might require enclosing parentheses; a value can be silently discarded when Unit is expected
55
spawn(b = { val ttt = 1; ttt }, a = 0)
66
^
7-
warning: there were four deprecation warnings; re-run with -deprecation for details
7+
warning: there were four deprecation warnings
8+
warning: there was one deprecation warning (since 2.12.4)
9+
warning: there were 5 deprecation warnings in total; re-run with -deprecation for details
810
1: @
911
get: $
1012
get: 2

0 commit comments

Comments
 (0)
0