Skip to content

Commit f930f55

Browse files
som-snytttgodzik
authored andcommitted
Improve Unit ascription escape hatch
[Cherry-picked 9e18bfe]
1 parent 626e873 commit f930f55

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,19 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
262262
case _ => false
263263
}
264264

265+
/** Expression was written `e: Unit` to quell warnings. Looks into adapted tree. */
266+
def isAscribedToUnit(tree: Tree): Boolean =
267+
import typer.Typer.AscribedToUnit
268+
tree.hasAttachment(AscribedToUnit)
269+
|| {
270+
def loop(tree: Tree): Boolean = tree match
271+
case Apply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn)
272+
case TypeApply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn)
273+
case Block(_, expr) => expr.hasAttachment(AscribedToUnit) || loop(expr)
274+
case _ => false
275+
loop(tree)
276+
}
277+
265278
/** Does this CaseDef catch Throwable? */
266279
def catchesThrowable(cdef: CaseDef)(using Context): Boolean =
267280
catchesAllOf(cdef, defn.ThrowableType)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
632632
val checkedType = checkNotShadowed(ownType)
633633
val tree1 = checkedType match
634634
case checkedType: NamedType if !prefixIsElidable(checkedType) =>
635-
ref(checkedType).withSpan(tree.span)
635+
ref(checkedType).withSpan(tree.span).withAttachmentsFrom(tree)
636636
case _ =>
637637
tree.withType(checkedType)
638638
val tree2 = toNotNullTermRef(tree1, pt)
@@ -4236,7 +4236,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
42364236
&& !ctx.isAfterTyper
42374237
&& !tree.isInstanceOf[Inlined]
42384238
&& !isThisTypeResult(tree)
4239-
&& !tree.hasAttachment(AscribedToUnit) then
4239+
&& !isAscribedToUnit(tree)
4240+
then
42404241
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
42414242

42424243
return tpd.Block(tree1 :: Nil, unitLiteral)

tests/warn/warn-value-discard.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
27 | mutable.Set.empty[String].remove("") // warn
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
| discarded non-Unit value of type Boolean. Add `: Unit` to discard silently.
5-
-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:39:41 -------------------------------------------
6-
39 | mutable.Set.empty[String].subtractOne("") // warn
5+
-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:37:41 -------------------------------------------
6+
37 | mutable.Set.empty[String].subtractOne("") // warn
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88
| discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently.
9-
-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:59:4 --------------------------------------------
10-
59 | mutable.Set.empty[String] += "" // warn
9+
-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:57:4 --------------------------------------------
10+
57 | mutable.Set.empty[String] += "" // warn
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
| discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently.
1313
-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:15:35 -------------------------------------------

tests/warn/warn-value-discard.scala

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ class ValueDiscardTest:
2626
// --> Warning
2727
mutable.Set.empty[String].remove("") // warn
2828

29-
// TODO IMHO we don't need to support this,
30-
// as it's just as easy to add a @nowarn annotation as a Unit ascription
31-
//def removeAscribed(): Unit = {
32-
// mutable.Set.empty[String].remove(""): Unit // nowarn
33-
//}
29+
def removeAscribed(): Unit = {
30+
mutable.Set.empty[String].remove(""): Unit // nowarn
31+
}
3432

3533
def subtract(): Unit =
3634
// - Set#subtractOne returns this.type
@@ -63,4 +61,37 @@ class ValueDiscardTest:
6361
// - receiver is a local variable
6462
// --> No warning
6563
val s: mutable.Set[String] = mutable.Set.empty[String]
66-
s += ""
64+
s += ""
65+
66+
// see also tests/warn/21557.scala
67+
class UnitAscription:
68+
import scala.concurrent.*, ExecutionContext.Implicits.given
69+
70+
case class C(c: Int):
71+
def f(i: Int, j: Int = c) = i + j
72+
73+
def f(i: Int, j: Int = 27) = i + j
74+
75+
def g[A]: List[A] = Nil
76+
77+
def i: Int = 42
78+
79+
def `default arg is inline`: Unit =
80+
f(i = 42): Unit // nowarn
81+
82+
def `default arg requires block`: Unit =
83+
C(27).f(i = 42): Unit // nowarn
84+
85+
def `application requires implicit arg`: Unit =
86+
Future(42): Unit // nowarn
87+
88+
def `application requires inferred type arg`: Unit =
89+
g: Unit // nowarn
90+
91+
def `implicit selection from this`: Unit =
92+
i: Unit // nowarn
93+
94+
object UnitAscription:
95+
def g[A]: List[A] = Nil
96+
def `application requires inferred type arg`: Unit =
97+
g: Unit // nowarn UnitAscription.g

0 commit comments

Comments
 (0)