Skip to content

Commit

Permalink
Backport "Improve message for discarded pure non-Unit values" to LTS (#…
Browse files Browse the repository at this point in the history
…20731)

Backports #18723 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
  • Loading branch information
WojciechMazur authored Jun 23, 2024
2 parents b26ae8e + fb14996 commit bf50ebd
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 24 deletions.
12 changes: 11 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2380,12 +2380,22 @@ class MemberWithSameNameAsStatic()(using Context)
class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)(using Context)
extends Message(PureExpressionInStatementPositionID) {
def kind = MessageKind.PotentialIssue
def msg(using Context) = "A pure expression does nothing in statement position; you may be omitting necessary parentheses"
def msg(using Context) = "A pure expression does nothing in statement position"
def explain(using Context) =
i"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere.
|It can be removed without changing the semantics of the program. This may indicate an error."""
}

class PureUnitExpression(stat: untpd.Tree, tpe: Type)(using Context)
extends Message(PureUnitExpressionID) {
def kind = MessageKind.PotentialIssue
def msg(using Context) = i"Discarded non-Unit value of type ${tpe.widen}. You may want to use `()`."
def explain(using Context) =
i"""As this expression is not of type Unit, it is desugared into `{ $stat; () }`.
|Here the `$stat` expression is a pure statement that can be discarded.
|Therefore the expression is effectively equivalent to `()`."""
}

class UnqualifiedCallToAnyRefMethod(stat: untpd.Tree, method: Symbol)(using Context)
extends Message(UnqualifiedCallToAnyRefMethodID) {
def kind = MessageKind.PotentialIssue
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4127,7 +4127,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// local adaptation makes sure every adapted tree conforms to its pt
// so will take the code path that decides on inlining
val tree1 = adapt(tree, WildcardType, locked)
checkStatementPurity(tree1)(tree, ctx.owner)
checkStatementPurity(tree1)(tree, ctx.owner, isUnitExpr = true)
if (!ctx.isAfterTyper && !tree.isInstanceOf[Inlined] && ctx.settings.WvalueDiscard.value && !isThisTypeResult(tree)) {
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
}
Expand Down Expand Up @@ -4424,7 +4424,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
else false
}

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

Expand Down
2 changes: 1 addition & 1 deletion compiler/test-resources/repl/nowarn.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ scala> def f = { 1; 2 }
-- [E129] Potential Issue Warning: ---------------------------------------------
1 | def f = { 1; 2 }
| ^
|A pure expression does nothing in statement position; you may be omitting necessary parentheses
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
def f: Int
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DiagnosticsTest {
|}"""
.diagnostics(m1,
(m1 to m2,
"A pure expression does nothing in statement position; you may be omitting necessary parentheses",
"A pure expression does nothing in statement position",
Warning, Some(PureExpressionInStatementPositionID)))

@Test def diagnosticWorksheetPureExpression: Unit =
Expand Down
4 changes: 2 additions & 2 deletions tests/neg-custom-args/captures/real-try.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- [E129] Potential Issue Warning: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------
-- [E190] Potential Issue Warning: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------
30 | b.x
| ^^^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type () -> Unit. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg-custom-args/captures/real-try.scala:12:2 -----------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/annot-suspend-cycle.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- [E129] Potential Issue Warning: tests/neg-macros/annot-suspend-cycle/Macro.scala:7:4 --------------------------------
7 | new Foo
| ^^^^^^^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
Cyclic macro dependencies in tests/neg-macros/annot-suspend-cycle/Test.scala.
Expand Down
18 changes: 18 additions & 0 deletions tests/neg/i18408a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- [E103] Syntax Error: tests/neg/i18408a.scala:2:0 --------------------------------------------------------------------
2 |fa(42) // error
|^^
|Illegal start of toplevel definition
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/neg/i18408a.scala:3:15 --------------------------------------------------------
3 |def test1 = fa(42)
| ^^
| Discarded non-Unit value of type Int. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/i18408a.scala:4:16 --------------------------------------------------------
4 |def test2 = fa({42; ()})
| ^^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
4 changes: 4 additions & 0 deletions tests/neg/i18408a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def fa(f: String ?=> Unit): Unit = ???
fa(42) // error
def test1 = fa(42)
def test2 = fa({42; ()})
18 changes: 18 additions & 0 deletions tests/neg/i18408b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- [E103] Syntax Error: tests/neg/i18408b.scala:2:0 --------------------------------------------------------------------
2 |fa(42) // error
|^^
|Illegal start of toplevel definition
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/neg/i18408b.scala:3:15 --------------------------------------------------------
3 |def test1 = fa(42)
| ^^
| Discarded non-Unit value of type Int. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/i18408b.scala:4:16 --------------------------------------------------------
4 |def test2 = fa({42; ()})
| ^^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
4 changes: 4 additions & 0 deletions tests/neg/i18408b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def fa(f: => Unit): Unit = ???
fa(42) // error
def test1 = fa(42)
def test2 = fa({42; ()})
18 changes: 18 additions & 0 deletions tests/neg/i18408c.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- [E103] Syntax Error: tests/neg/i18408c.scala:2:0 --------------------------------------------------------------------
2 |fa(42) // error
|^^
|Illegal start of toplevel definition
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/neg/i18408c.scala:3:15 --------------------------------------------------------
3 |def test1 = fa(42)
| ^^
| Discarded non-Unit value of type Int. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/i18408c.scala:4:16 --------------------------------------------------------
4 |def test2 = fa({42; ()})
| ^^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
4 changes: 4 additions & 0 deletions tests/neg/i18408c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def fa(f: Unit): Unit = ???
fa(42) // error
def test1 = fa(42)
def test2 = fa({42; ()})
44 changes: 44 additions & 0 deletions tests/neg/i18722.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-- [E190] Potential Issue Error: tests/neg/i18722.scala:3:15 -----------------------------------------------------------
3 |def f1: Unit = null // error
| ^^^^
| Discarded non-Unit value of type Null. You may want to use `()`.
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| As this expression is not of type Unit, it is desugared into `{ null; () }`.
| Here the `null` expression is a pure statement that can be discarded.
| Therefore the expression is effectively equivalent to `()`.
---------------------------------------------------------------------------------------------------------------------
-- [E190] Potential Issue Error: tests/neg/i18722.scala:4:15 -----------------------------------------------------------
4 |def f2: Unit = 1 // error
| ^
| Discarded non-Unit value of type Int. You may want to use `()`.
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| As this expression is not of type Unit, it is desugared into `{ 1; () }`.
| Here the `1` expression is a pure statement that can be discarded.
| Therefore the expression is effectively equivalent to `()`.
---------------------------------------------------------------------------------------------------------------------
-- [E190] Potential Issue Error: tests/neg/i18722.scala:5:15 -----------------------------------------------------------
5 |def f3: Unit = "a" // error
| ^^^
| Discarded non-Unit value of type String. You may want to use `()`.
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| As this expression is not of type Unit, it is desugared into `{ "a"; () }`.
| Here the `"a"` expression is a pure statement that can be discarded.
| Therefore the expression is effectively equivalent to `()`.
---------------------------------------------------------------------------------------------------------------------
-- [E190] Potential Issue Error: tests/neg/i18722.scala:7:15 -----------------------------------------------------------
7 |def f4: Unit = i // error
| ^
| Discarded non-Unit value of type Int. You may want to use `()`.
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| As this expression is not of type Unit, it is desugared into `{ i; () }`.
| Here the `i` expression is a pure statement that can be discarded.
| Therefore the expression is effectively equivalent to `()`.
---------------------------------------------------------------------------------------------------------------------
9 changes: 9 additions & 0 deletions tests/neg/i18722.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//> using options -Werror -explain

def f1: Unit = null // error
def f2: Unit = 1 // error
def f3: Unit = "a" // error
val i: Int = 1
def f4: Unit = i // error
val u: Unit = ()
def f5: Unit = u
4 changes: 2 additions & 2 deletions tests/neg/nowarn.check
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Matching filters for @nowarn or -Wconf:
-- [E129] Potential Issue Warning: tests/neg/nowarn.scala:15:11 --------------------------------------------------------
15 |def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything)
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- Warning: tests/neg/nowarn.scala:14:8 --------------------------------------------------------------------------------
Expand All @@ -43,7 +43,7 @@ Matching filters for @nowarn or -Wconf:
-- [E129] Potential Issue Warning: tests/neg/nowarn.scala:18:12 --------------------------------------------------------
18 |def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence)
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- Warning: tests/neg/nowarn.scala:17:8 --------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/spaces-vs-tabs.check
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
| The start of this line does not match any of the previous indentation widths.
| Indentation width of current line : 1 tab, 2 spaces
| This falls between previous widths: 1 tab and 1 tab, 4 spaces
-- [E129] Potential Issue Warning: tests/neg/spaces-vs-tabs.scala:13:7 -------------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/spaces-vs-tabs.scala:13:7 -------------------------------------------------
13 | 1
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Int. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
24 changes: 12 additions & 12 deletions tests/neg/syntax-error-recovery.check
Original file line number Diff line number Diff line change
Expand Up @@ -94,45 +94,45 @@
| Not found: bam
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:7:2 -------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:7:2 -------------------------------------------
6 | 2
7 | }
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:15:2 ------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:15:2 ------------------------------------------
14 | 2
15 | println(baz) // error
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:27:2 ------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:27:2 ------------------------------------------
26 | 2
27 | println(bam) // error
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:36:2 ------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:36:2 ------------------------------------------
35 | 2
36 | }
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:44:2 ------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:44:2 ------------------------------------------
43 | 2
44 | println(baz) // error
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:56:2 ------------------------------------------
-- [E190] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:56:2 ------------------------------------------
55 | 2
56 | println(bam) // error
| ^
| A pure expression does nothing in statement position; you may be omitting necessary parentheses
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`

0 comments on commit bf50ebd

Please sign in to comment.