Skip to content

Commit 162703a

Browse files
committed
Improve Unit ascription escape hatch
1 parent e52986c commit 162703a

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,19 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
264264
case _ => false
265265
}
266266

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

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
670670
val checkedType = checkNotShadowed(ownType)
671671
val tree1 = checkedType match
672672
case checkedType: NamedType if !prefixIsElidable(checkedType) =>
673-
ref(checkedType).withSpan(tree.span)
673+
ref(checkedType).withSpan(tree.span).withAttachmentsFrom(tree)
674674
case _ =>
675675
def isScalaModuleRef = checkedType match
676676
case moduleRef: TypeRef if moduleRef.symbol.is(ModuleClass, butNot = JavaDefined) => true
@@ -4695,7 +4695,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
46954695
&& !ctx.isAfterTyper
46964696
&& !tree.isInstanceOf[Inlined]
46974697
&& !isThisTypeResult(tree)
4698-
&& !tree.hasAttachment(AscribedToUnit) then
4698+
&& !isAscribedToUnit(tree)
4699+
then
46994700
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
47004701

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

tests/warn/warn-value-discard.scala

+37-6
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)