Skip to content

Commit 5af78d8

Browse files
Improve message for discarded pure non-Unit values (#18723)
Fixes #18408 Fixes #18722
2 parents 9a64c09 + 7f48230 commit 5af78d8

File tree

18 files changed

+158
-26
lines changed

18 files changed

+158
-26
lines changed

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,11 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
199199
case ClosureCannotHaveInternalParameterDependenciesID // errorNumber: 183
200200
case MatchTypeNoCasesID // errorNumber: 184
201201
case UnimportedAndImportedID // errorNumber: 185
202-
case ImplausiblePatternWarningID // erorNumber: 186
202+
case ImplausiblePatternWarningID // errorNumber: 186
203203
case SynchronizedCallOnBoxedClassID // errorNumber: 187
204-
case VarArgsParamCannotBeGivenID // erorNumber: 188
204+
case VarArgsParamCannotBeGivenID // errorNumber: 188
205205
case ExtractorNotFoundID // errorNumber: 189
206+
case PureUnitExpressionID // errorNumber: 190
206207

207208
def errorNumber = ordinal - 1
208209

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2403,12 +2403,22 @@ class MemberWithSameNameAsStatic()(using Context)
24032403
class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)(using Context)
24042404
extends Message(PureExpressionInStatementPositionID) {
24052405
def kind = MessageKind.PotentialIssue
2406-
def msg(using Context) = "A pure expression does nothing in statement position; you may be omitting necessary parentheses"
2406+
def msg(using Context) = "A pure expression does nothing in statement position"
24072407
def explain(using Context) =
24082408
i"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere.
24092409
|It can be removed without changing the semantics of the program. This may indicate an error."""
24102410
}
24112411

2412+
class PureUnitExpression(stat: untpd.Tree, tpe: Type)(using Context)
2413+
extends Message(PureUnitExpressionID) {
2414+
def kind = MessageKind.PotentialIssue
2415+
def msg(using Context) = i"Discarded non-Unit value of type ${tpe.widen}. You may want to use `()`."
2416+
def explain(using Context) =
2417+
i"""As this expression is not of type Unit, it is desugared into `{ $stat; () }`.
2418+
|Here the `$stat` expression is a pure statement that can be discarded.
2419+
|Therefore the expression is effectively equivalent to `()`."""
2420+
}
2421+
24122422
class UnqualifiedCallToAnyRefMethod(stat: untpd.Tree, method: Symbol)(using Context)
24132423
extends Message(UnqualifiedCallToAnyRefMethodID) {
24142424
def kind = MessageKind.PotentialIssue

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4226,7 +4226,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
42264226
// local adaptation makes sure every adapted tree conforms to its pt
42274227
// so will take the code path that decides on inlining
42284228
val tree1 = adapt(tree, WildcardType, locked)
4229-
checkStatementPurity(tree1)(tree, ctx.owner)
4229+
checkStatementPurity(tree1)(tree, ctx.owner, isUnitExpr = true)
42304230
if (!ctx.isAfterTyper && !tree.isInstanceOf[Inlined] && ctx.settings.WvalueDiscard.value && !isThisTypeResult(tree)) {
42314231
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
42324232
}
@@ -4432,7 +4432,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
44324432
typedExpr(cmp, defn.BooleanType)
44334433
case _ =>
44344434

4435-
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol)(using Context): Unit =
4435+
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean = false)(using Context): Unit =
44364436
if !tree.tpe.isErroneous
44374437
&& !ctx.isAfterTyper
44384438
&& !tree.isInstanceOf[Inlined]
@@ -4450,6 +4450,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
44504450
// sometimes we do not have the original anymore and use the transformed tree instead.
44514451
// But taken together, the two criteria are quite accurate.
44524452
missingArgs(tree, tree.tpe.widen)
4453+
case _ if isUnitExpr =>
4454+
report.warning(PureUnitExpression(original, tree.tpe), original.srcPos)
44534455
case _ =>
44544456
report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos)
44554457

compiler/test-resources/repl/nowarn.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ scala> def f = { 1; 2 }
2727
-- [E129] Potential Issue Warning: ---------------------------------------------
2828
1 | def f = { 1; 2 }
2929
| ^
30-
|A pure expression does nothing in statement position; you may be omitting necessary parentheses
30+
| A pure expression does nothing in statement position
3131
|
3232
| longer explanation available when compiling with `-explain`
3333
def f: Int

language-server/test/dotty/tools/languageserver/DiagnosticsTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class DiagnosticsTest {
3030
|}"""
3131
.diagnostics(m1,
3232
(m1 to m2,
33-
"A pure expression does nothing in statement position; you may be omitting necessary parentheses",
33+
"A pure expression does nothing in statement position",
3434
Warning, Some(PureExpressionInStatementPositionID)))
3535

3636
@Test def diagnosticWorksheetPureExpression: Unit =

tests/neg-custom-args/captures/real-try.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
-- [E129] Potential Issue Warning: tests/neg-custom-args/captures/real-try.scala:36:4 ----------------------------------
1+
-- [E190] Potential Issue Warning: tests/neg-custom-args/captures/real-try.scala:36:4 ----------------------------------
22
36 | b.x
33
| ^^^
4-
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
4+
| Discarded non-Unit value of type () -> Unit. You may want to use `()`.
55
|
66
| longer explanation available when compiling with `-explain`
77
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/real-try.scala:19:4 --------------------------------------

tests/neg-macros/annot-suspend-cycle.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- [E129] Potential Issue Warning: tests/neg-macros/annot-suspend-cycle/Macro.scala:7:4 --------------------------------
22
7 | new Foo
33
| ^^^^^^^
4-
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
4+
| A pure expression does nothing in statement position
55
|
66
| longer explanation available when compiling with `-explain`
77
Cyclic macro dependencies in tests/neg-macros/annot-suspend-cycle/Test.scala.

tests/neg/i18408a.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- [E103] Syntax Error: tests/neg/i18408a.scala:2:0 --------------------------------------------------------------------
2+
2 |fa(42) // error
3+
|^^
4+
|Illegal start of toplevel definition
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E190] Potential Issue Warning: tests/neg/i18408a.scala:3:15 --------------------------------------------------------
8+
3 |def test1 = fa(42)
9+
| ^^
10+
| Discarded non-Unit value of type Int. You may want to use `()`.
11+
|
12+
| longer explanation available when compiling with `-explain`
13+
-- [E129] Potential Issue Warning: tests/neg/i18408a.scala:4:16 --------------------------------------------------------
14+
4 |def test2 = fa({42; ()})
15+
| ^^
16+
| A pure expression does nothing in statement position
17+
|
18+
| longer explanation available when compiling with `-explain`

tests/neg/i18408a.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def fa(f: String ?=> Unit): Unit = ???
2+
fa(42) // error
3+
def test1 = fa(42)
4+
def test2 = fa({42; ()})

tests/neg/i18408b.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- [E103] Syntax Error: tests/neg/i18408b.scala:2:0 --------------------------------------------------------------------
2+
2 |fa(42) // error
3+
|^^
4+
|Illegal start of toplevel definition
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E190] Potential Issue Warning: tests/neg/i18408b.scala:3:15 --------------------------------------------------------
8+
3 |def test1 = fa(42)
9+
| ^^
10+
| Discarded non-Unit value of type Int. You may want to use `()`.
11+
|
12+
| longer explanation available when compiling with `-explain`
13+
-- [E129] Potential Issue Warning: tests/neg/i18408b.scala:4:16 --------------------------------------------------------
14+
4 |def test2 = fa({42; ()})
15+
| ^^
16+
| A pure expression does nothing in statement position
17+
|
18+
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)