@@ -127,6 +127,10 @@ pub enum MustUsePath {
127127 Opaque ( Box < Self > ) ,
128128 TraitObject ( Box < Self > ) ,
129129 TupleElement ( Vec < ( usize , Self ) > ) ,
130+ /// `Result<T, Uninhabited>`
131+ Result ( Box < Self > ) ,
132+ /// `ControlFlow<Uninhabited, T>`
133+ ControlFlow ( Box < Self > ) ,
130134 Array ( Box < Self > , u64 ) ,
131135 /// The root of the unused_closures lint.
132136 Closure ( Span ) ,
@@ -136,12 +140,19 @@ pub enum MustUsePath {
136140
137141/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
138142/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
143+ ///
144+ /// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
145+ /// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
146+ /// so clippy can use this).
147+ //
148+ // FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
139149#[ instrument( skip( cx, expr) , level = "debug" , ret) ]
140150pub fn is_ty_must_use < ' tcx > (
141151 cx : & LateContext < ' tcx > ,
142152 ty : Ty < ' tcx > ,
143153 expr : & hir:: Expr < ' _ > ,
144154 span : Span ,
155+ simplify_uninhabited : bool ,
145156) -> IsTyMustUse {
146157 if ty. is_unit ( ) {
147158 return IsTyMustUse :: Trivial ;
@@ -154,13 +165,34 @@ pub fn is_ty_must_use<'tcx>(
154165 match * ty. kind ( ) {
155166 _ if is_uninhabited ( ty) => IsTyMustUse :: Trivial ,
156167 ty:: Adt ( ..) if let Some ( boxed) = ty. boxed_ty ( ) => {
157- is_ty_must_use ( cx, boxed, expr, span) . map ( |inner| MustUsePath :: Boxed ( Box :: new ( inner) ) )
168+ is_ty_must_use ( cx, boxed, expr, span, simplify_uninhabited)
169+ . map ( |inner| MustUsePath :: Boxed ( Box :: new ( inner) ) )
158170 }
159171 ty:: Adt ( def, args) if cx. tcx . is_lang_item ( def. did ( ) , LangItem :: Pin ) => {
160172 let pinned_ty = args. type_at ( 0 ) ;
161- is_ty_must_use ( cx, pinned_ty, expr, span)
173+ is_ty_must_use ( cx, pinned_ty, expr, span, simplify_uninhabited )
162174 . map ( |inner| MustUsePath :: Pinned ( Box :: new ( inner) ) )
163175 }
176+ // Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
177+ ty:: Adt ( def, args)
178+ if simplify_uninhabited
179+ && cx. tcx . is_diagnostic_item ( sym:: Result , def. did ( ) )
180+ && is_uninhabited ( args. type_at ( 1 ) ) =>
181+ {
182+ let ok_ty = args. type_at ( 0 ) ;
183+ is_ty_must_use ( cx, ok_ty, expr, span, simplify_uninhabited)
184+ . map ( |path| MustUsePath :: Result ( Box :: new ( path) ) )
185+ }
186+ // Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
187+ ty:: Adt ( def, args)
188+ if simplify_uninhabited
189+ && cx. tcx . is_diagnostic_item ( sym:: ControlFlow , def. did ( ) )
190+ && is_uninhabited ( args. type_at ( 0 ) ) =>
191+ {
192+ let continue_ty = args. type_at ( 1 ) ;
193+ is_ty_must_use ( cx, continue_ty, expr, span, simplify_uninhabited)
194+ . map ( |path| MustUsePath :: ControlFlow ( Box :: new ( path) ) )
195+ }
164196 // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
165197 ty:: Adt ( def, args)
166198 if cx. tcx . is_diagnostic_item ( sym:: Result , def. did ( ) )
@@ -228,7 +260,9 @@ pub fn is_ty_must_use<'tcx>(
228260 . zip ( elem_exprs)
229261 . enumerate ( )
230262 . filter_map ( |( i, ( ty, expr) ) | {
231- is_ty_must_use ( cx, ty, expr, expr. span ) . yes ( ) . map ( |path| ( i, path) )
263+ is_ty_must_use ( cx, ty, expr, expr. span , simplify_uninhabited)
264+ . yes ( )
265+ . map ( |path| ( i, path) )
232266 } )
233267 . collect :: < Vec < _ > > ( ) ;
234268
@@ -242,7 +276,7 @@ pub fn is_ty_must_use<'tcx>(
242276 // If the array is empty we don't lint, to avoid false positives
243277 Some ( 0 ) | None => IsTyMustUse :: No ,
244278 // If the array is definitely non-empty, we can do `#[must_use]` checking.
245- Some ( len) => is_ty_must_use ( cx, ty, expr, span)
279+ Some ( len) => is_ty_must_use ( cx, ty, expr, span, simplify_uninhabited )
246280 . map ( |inner| MustUsePath :: Array ( Box :: new ( inner) , len) ) ,
247281 } ,
248282 ty:: Closure ( ..) | ty:: CoroutineClosure ( ..) => IsTyMustUse :: Yes ( MustUsePath :: Closure ( span) ) ,
@@ -305,7 +339,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
305339
306340 let ty = cx. typeck_results ( ) . expr_ty ( expr) ;
307341
308- let must_use_result = is_ty_must_use ( cx, ty, expr, expr. span ) ;
342+ let must_use_result = is_ty_must_use ( cx, ty, expr, expr. span , false ) ;
309343 let type_lint_emitted_or_trivial = match must_use_result {
310344 IsTyMustUse :: Yes ( path) => {
311345 emit_must_use_untranslated ( cx, & path, "" , "" , 1 , false , expr_is_from_block) ;
@@ -528,6 +562,30 @@ fn emit_must_use_untranslated(
528562 ) ;
529563 }
530564 }
565+ MustUsePath :: Result ( path) => {
566+ let descr_post = & format ! ( " in a `Result` with an uninhabited error{descr_post}" ) ;
567+ emit_must_use_untranslated (
568+ cx,
569+ path,
570+ descr_pre,
571+ descr_post,
572+ plural_len,
573+ true ,
574+ expr_is_from_block,
575+ ) ;
576+ }
577+ MustUsePath :: ControlFlow ( path) => {
578+ let descr_post = & format ! ( " in a `ControlFlow` with an uninhabited break {descr_post}" ) ;
579+ emit_must_use_untranslated (
580+ cx,
581+ path,
582+ descr_pre,
583+ descr_post,
584+ plural_len,
585+ true ,
586+ expr_is_from_block,
587+ ) ;
588+ }
531589 MustUsePath :: Array ( path, len) => {
532590 let descr_pre = & format ! ( "{descr_pre}array{plural_suffix} of " ) ;
533591 emit_must_use_untranslated (
0 commit comments