diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 9efad31e9796..2582c1e86dc9 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -262,6 +262,19 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case _ => false } + /** Expression was written `e: Unit` to quell warnings. Looks into adapted tree. */ + def isAscribedToUnit(tree: Tree): Boolean = + import typer.Typer.AscribedToUnit + tree.hasAttachment(AscribedToUnit) + || { + def loop(tree: Tree): Boolean = tree match + case Apply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn) + case TypeApply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn) + case Block(_, expr) => expr.hasAttachment(AscribedToUnit) || loop(expr) + case _ => false + loop(tree) + } + /** Does this CaseDef catch Throwable? */ def catchesThrowable(cdef: CaseDef)(using Context): Boolean = catchesAllOf(cdef, defn.ThrowableType) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 035d57cfc230..1f1cf72e3e12 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -632,7 +632,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val checkedType = checkNotShadowed(ownType) val tree1 = checkedType match case checkedType: NamedType if !prefixIsElidable(checkedType) => - ref(checkedType).withSpan(tree.span) + ref(checkedType).withSpan(tree.span).withAttachmentsFrom(tree) case _ => tree.withType(checkedType) val tree2 = toNotNullTermRef(tree1, pt) @@ -4236,7 +4236,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer && !ctx.isAfterTyper && !tree.isInstanceOf[Inlined] && !isThisTypeResult(tree) - && !tree.hasAttachment(AscribedToUnit) then + && !isAscribedToUnit(tree) + then report.warning(ValueDiscarding(tree.tpe), tree.srcPos) return tpd.Block(tree1 :: Nil, unitLiteral) diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala index ffd7377c8181..f7fdb1c36e6d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala @@ -62,7 +62,7 @@ object CompletionItemResolver extends ItemResolver: if companion == NoSymbol || gsym.is(JavaDefined) then if gsymDoc.isEmpty() then if gsym.isAliasType then - fullDocstring(gsym.info.deepDealias.typeSymbol, search) + fullDocstring(gsym.info.deepDealiasAndSimplify.typeSymbol, search) else if gsym.is(Method) then gsym.info.finalResultType match case tr @ TermRef(_, sym) => diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index 6c1ffa7d7fac..f26112ed9fdc 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -108,7 +108,7 @@ object HoverProvider: if symbol.name == nme.selectDynamic || symbol.name == nme.applyDynamic => fallbackToDynamics(path, printer, contentType) case symbolTpes @ ((symbol, tpe, _) :: _) => - val exprTpw = tpe.widenTermRefExpr.deepDealias + val exprTpw = tpe.widenTermRefExpr.deepDealiasAndSimplify val hoverString = tpw match // https://github.com/lampepfl/dotty/issues/8891 @@ -123,7 +123,7 @@ object HoverProvider: if tpe != NoType then tpe else tpw - printer.hoverSymbol(sym, finalTpe.deepDealias) + printer.hoverSymbol(sym, finalTpe.deepDealiasAndSimplify) end match end hoverString @@ -177,7 +177,7 @@ object HoverProvider: val resultType = rest match case Select(_, asInstanceOf) :: TypeApply(_, List(tpe)) :: _ if asInstanceOf == nme.asInstanceOfPM => - tpe.tpe.widenTermRefExpr.deepDealias + tpe.tpe.widenTermRefExpr.deepDealiasAndSimplify case _ if n == nme.selectDynamic => tpe.resultType case _ => tpe @@ -202,9 +202,9 @@ object HoverProvider: findRefinement(parent) case _ => None - val refTpe = sel.typeOpt.widen.deepDealias match + val refTpe = sel.typeOpt.widen.deepDealiasAndSimplify match case r: RefinedType => Some(r) - case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.deepDealias) + case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.deepDealiasAndSimplify) case _ => None refTpe.flatMap(findRefinement).asJava diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala index fe6d6bb436d5..bce3968a0aef 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala @@ -102,7 +102,7 @@ final class InferredTypeProvider( case _ => true if isInScope(tpe) then tpe - else tpe.deepDealias + else tpe.deepDealiasAndSimplify val printer = ShortenedTypePrinter( symbolSearch, diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 0f69e16831a0..6dcbe53570d5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -140,7 +140,7 @@ class PcInlayHintsProvider( isInScope(tycon) && args.forall(isInScope) case _ => true if isInScope(tpe) then tpe - else tpe.deepDealias(using indexedCtx.ctx) + else tpe.deepDealiasAndSimplify(using indexedCtx.ctx) val dealiased = optDealias(tpe) val tpeStr = printer.tpe(dealiased) diff --git a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala index 8bed605a87f8..09805fc76040 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala @@ -11,7 +11,7 @@ import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.* -import dotty.tools.pc.utils.InteractiveEnrichments.deepDealias +import dotty.tools.pc.utils.InteractiveEnrichments.deepDealiasAndSimplify import dotty.tools.pc.SemanticdbSymbols import dotty.tools.pc.utils.InteractiveEnrichments.allSymbols import dotty.tools.pc.utils.InteractiveEnrichments.stripBackticks @@ -51,7 +51,7 @@ class SymbolInformationProvider(using Context): collect(classSym) visited.toList.map(SemanticdbSymbols.symbolName) val dealisedSymbol = - if sym.isAliasType then sym.info.deepDealias.typeSymbol else sym + if sym.isAliasType then sym.info.deepDealiasAndSimplify.typeSymbol else sym val classOwner = sym.ownersIterator.drop(1).find(s => s.isClass || s.is(Flags.Module)) val overridden = sym.denot.allOverriddenSymbols.toList diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index 536163d6dacb..044b1d6c73f9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -93,7 +93,7 @@ object CaseKeywordCompletion: case (Ident(v), tpe) => v.decoded == value case (Select(_, v), tpe) => v.decoded == value case t => false - .map((_, id) => argPts(id).widen.deepDealias) + .map((_, id) => argPts(id).widen.deepDealiasAndSimplify) /* Parent is a function expecting a case match expression */ case TreeApply(fun, _) if !fun.tpe.isErroneous => fun.tpe.paramInfoss match @@ -103,12 +103,12 @@ object CaseKeywordCompletion: ) => val args = head.argTypes.init if args.length > 1 then - Some(definitions.tupleType(args).widen.deepDealias) - else args.headOption.map(_.widen.deepDealias) + Some(definitions.tupleType(args).widen.deepDealiasAndSimplify) + else args.headOption.map(_.widen.deepDealiasAndSimplify) case _ => None case _ => None case sel => - Some(sel.tpe.widen.deepDealias) + Some(sel.tpe.widen.deepDealiasAndSimplify) selTpe .collect { case selTpe if selTpe != NoType => @@ -279,8 +279,8 @@ object CaseKeywordCompletion: clientSupportsSnippets ) - val tpeStr = printer.tpe(selector.tpe.widen.deepDealias.bounds.hi) - val tpe = selector.typeOpt.widen.deepDealias.bounds.hi match + val tpeStr = printer.tpe(selector.tpe.widen.deepDealiasAndSimplify.bounds.hi) + val tpe = selector.typeOpt.widen.deepDealiasAndSimplify.bounds.hi match case tr @ TypeRef(_, _) => tr.underlying case t => t diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala index 53c4e01980bc..621ef2e5c158 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala @@ -42,7 +42,7 @@ object SingletonCompletions: } yield value private def collectSingletons(tpe: Type)(using Context): List[Constant] = - tpe.deepDealias match + tpe.deepDealiasAndSimplify match case ConstantType(value) => List(value) case OrType(tpe1, tpe2) => collectSingletons(tpe1) ++ collectSingletons(tpe2) diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala index aaf3d6ddb1e7..395c55eaaff4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala @@ -178,7 +178,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: if sym.is(Module) then sym.companionClass else sym.companionModule def dealiasType: Symbol = - if sym.isType then sym.info.deepDealias.typeSymbol else sym + if sym.isType then sym.info.deepDealiasAndSimplify.typeSymbol else sym def nameBackticked: String = nameBackticked(Set.empty[String]) @@ -402,16 +402,16 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: end extension extension (tpe: Type) - def deepDealias(using Context): Type = - tpe.dealias match + def deepDealiasAndSimplify(using Context): Type = + val dealiased = tpe.dealias match case app @ AppliedType(tycon, params) => - AppliedType(tycon, params.map(_.deepDealias)) + AppliedType(tycon, params.map(_.deepDealiasAndSimplify)) case aliasingBounds: AliasingBounds => aliasingBounds.derivedAlias(aliasingBounds.alias.deepDealiasAndSimplify) case TypeBounds(lo, hi) => TypeBounds(lo.dealias, hi.dealias) case RefinedType(parent, name, refinedInfo) => - RefinedType(parent.dealias, name, refinedInfo.deepDealias) + RefinedType(parent.dealias, name, refinedInfo.deepDealiasAndSimplify) case dealised => dealised dealiased.simplified diff --git a/tests/pos/i20070.scala b/tests/pos/i20070.scala deleted file mode 100644 index c3061239f6d7..000000000000 --- a/tests/pos/i20070.scala +++ /dev/null @@ -1,7 +0,0 @@ - -trait F[A] -given F[Int] = new F {} -def f[A: F] = { (x: A) => x } - -@main def test = - println(f[Int](1)) diff --git a/tests/warn/warn-value-discard.check b/tests/warn/warn-value-discard.check index dcd6d62c00e0..03ada5c68621 100644 --- a/tests/warn/warn-value-discard.check +++ b/tests/warn/warn-value-discard.check @@ -2,12 +2,12 @@ 27 | mutable.Set.empty[String].remove("") // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type Boolean. Add `: Unit` to discard silently. --- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:39:41 ------------------------------------------- -39 | mutable.Set.empty[String].subtractOne("") // warn +-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:37:41 ------------------------------------------- +37 | mutable.Set.empty[String].subtractOne("") // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently. --- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:59:4 -------------------------------------------- -59 | mutable.Set.empty[String] += "" // warn +-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:57:4 -------------------------------------------- +57 | mutable.Set.empty[String] += "" // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:15:35 ------------------------------------------- diff --git a/tests/warn/warn-value-discard.scala b/tests/warn/warn-value-discard.scala index 8bb9aebf515b..93c2c056b67c 100644 --- a/tests/warn/warn-value-discard.scala +++ b/tests/warn/warn-value-discard.scala @@ -26,11 +26,9 @@ class ValueDiscardTest: // --> Warning mutable.Set.empty[String].remove("") // warn - // TODO IMHO we don't need to support this, - // as it's just as easy to add a @nowarn annotation as a Unit ascription - //def removeAscribed(): Unit = { - // mutable.Set.empty[String].remove(""): Unit // nowarn - //} + def removeAscribed(): Unit = { + mutable.Set.empty[String].remove(""): Unit // nowarn + } def subtract(): Unit = // - Set#subtractOne returns this.type @@ -63,4 +61,37 @@ class ValueDiscardTest: // - receiver is a local variable // --> No warning val s: mutable.Set[String] = mutable.Set.empty[String] - s += "" \ No newline at end of file + s += "" + +// see also tests/warn/21557.scala +class UnitAscription: + import scala.concurrent.*, ExecutionContext.Implicits.given + + case class C(c: Int): + def f(i: Int, j: Int = c) = i + j + + def f(i: Int, j: Int = 27) = i + j + + def g[A]: List[A] = Nil + + def i: Int = 42 + + def `default arg is inline`: Unit = + f(i = 42): Unit // nowarn + + def `default arg requires block`: Unit = + C(27).f(i = 42): Unit // nowarn + + def `application requires implicit arg`: Unit = + Future(42): Unit // nowarn + + def `application requires inferred type arg`: Unit = + g: Unit // nowarn + + def `implicit selection from this`: Unit = + i: Unit // nowarn + +object UnitAscription: + def g[A]: List[A] = Nil + def `application requires inferred type arg`: Unit = + g: Unit // nowarn UnitAscription.g