@@ -53,6 +53,20 @@ declare_lint! {
5353 report_in_external_macro
5454}
5555
56+ declare_lint ! {
57+ pub UNMUSTUSE_IN_ALWAYS_OK ,
58+ Warn ,
59+ "" ,
60+ report_in_external_macro
61+ }
62+
63+ declare_lint ! {
64+ pub MUSTUSE_IN_ALWAYS_OK ,
65+ Warn ,
66+ "" ,
67+ report_in_external_macro
68+ }
69+
5670declare_lint ! {
5771 /// The `unused_results` lint checks for the unused result of an
5872 /// expression in a statement.
@@ -92,7 +106,7 @@ declare_lint! {
92106 "unused result of an expression in a statement"
93107}
94108
95- declare_lint_pass ! ( UnusedResults => [ UNUSED_MUST_USE , UNUSED_RESULTS ] ) ;
109+ declare_lint_pass ! ( UnusedResults => [ UNUSED_MUST_USE , UNUSED_RESULTS , UNMUSTUSE_IN_ALWAYS_OK , MUSTUSE_IN_ALWAYS_OK ] ) ;
96110
97111impl < ' tcx > LateLintPass < ' tcx > for UnusedResults {
98112 fn check_stmt ( & mut self , cx : & LateContext < ' _ > , s : & hir:: Stmt < ' _ > ) {
@@ -136,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
136150
137151 let ty = cx. typeck_results ( ) . expr_ty ( expr) ;
138152
139- let must_use_result = is_ty_must_use ( cx, ty, expr, expr. span ) ;
153+ let must_use_result = is_ty_must_use ( cx, ty, expr, expr. span , false ) ;
140154 let type_lint_emitted_or_suppressed = match must_use_result {
141155 Some ( path) => {
142156 emit_must_use_untranslated ( cx, & path, "" , "" , 1 , false , expr_is_from_block) ;
@@ -209,6 +223,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
209223 cx. emit_span_lint ( UNUSED_RESULTS , s. span , UnusedResult { ty } ) ;
210224 }
211225 }
226+
227+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx rustc_hir:: Expr < ' tcx > ) {
228+ let ty = cx. typeck_results ( ) . expr_ty ( expr) ;
229+
230+ _ = is_ty_must_use ( cx, ty, expr, expr. span , true ) ;
231+ }
212232}
213233
214234fn check_fn_must_use ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > , expr_is_from_block : bool ) -> bool {
@@ -259,12 +279,33 @@ enum MustUsePath {
259279 Coroutine ( Span ) ,
260280}
261281
282+ fn is_suppressed ( p : & MustUsePath ) -> bool {
283+ match p {
284+ MustUsePath :: Suppressed => true ,
285+
286+ MustUsePath :: Def ( ..) | MustUsePath :: Closure ( ..) | MustUsePath :: Coroutine ( ..) => false ,
287+
288+ MustUsePath :: Boxed ( must_use_path)
289+ | MustUsePath :: Pinned ( must_use_path)
290+ | MustUsePath :: Opaque ( must_use_path)
291+ | MustUsePath :: Array ( must_use_path, _)
292+ | MustUsePath :: TraitObject ( must_use_path)
293+ | MustUsePath :: Result ( must_use_path)
294+ | MustUsePath :: ControlFlow ( must_use_path) => is_suppressed ( must_use_path) ,
295+
296+ MustUsePath :: TupleElement ( items) => {
297+ items. iter ( ) . all ( |( _, must_use_path) | is_suppressed ( must_use_path) )
298+ }
299+ }
300+ }
301+
262302#[ instrument( skip( cx, expr) , level = "debug" , ret) ]
263303fn is_ty_must_use < ' tcx > (
264304 cx : & LateContext < ' tcx > ,
265305 ty : Ty < ' tcx > ,
266306 expr : & hir:: Expr < ' _ > ,
267307 span : Span ,
308+ tmp_lint : bool ,
268309) -> Option < MustUsePath > {
269310 if ty. is_unit ( ) {
270311 return Some ( MustUsePath :: Suppressed ) ;
@@ -276,11 +317,12 @@ fn is_ty_must_use<'tcx>(
276317 match * ty. kind ( ) {
277318 _ if is_uninhabited ( ty) => Some ( MustUsePath :: Suppressed ) ,
278319 ty:: Adt ( ..) if let Some ( boxed) = ty. boxed_ty ( ) => {
279- is_ty_must_use ( cx, boxed, expr, span) . map ( |inner| MustUsePath :: Boxed ( Box :: new ( inner) ) )
320+ is_ty_must_use ( cx, boxed, expr, span, tmp_lint)
321+ . map ( |inner| MustUsePath :: Boxed ( Box :: new ( inner) ) )
280322 }
281323 ty:: Adt ( def, args) if cx. tcx . is_lang_item ( def. did ( ) , LangItem :: Pin ) => {
282324 let pinned_ty = args. type_at ( 0 ) ;
283- is_ty_must_use ( cx, pinned_ty, expr, span)
325+ is_ty_must_use ( cx, pinned_ty, expr, span, tmp_lint )
284326 . map ( |inner| MustUsePath :: Pinned ( Box :: new ( inner) ) )
285327 }
286328 // Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
@@ -289,15 +331,59 @@ fn is_ty_must_use<'tcx>(
289331 && is_uninhabited ( args. type_at ( 1 ) ) =>
290332 {
291333 let ok_ty = args. type_at ( 0 ) ;
292- is_ty_must_use ( cx, ok_ty, expr, span) . map ( Box :: new) . map ( MustUsePath :: Result )
334+ let res = is_ty_must_use ( cx, ok_ty, expr, span, tmp_lint)
335+ . map ( Box :: new)
336+ . map ( MustUsePath :: Result ) ;
337+
338+ if tmp_lint
339+ && !matches ! ( ok_ty. kind( ) , ty:: Param ( _) )
340+ && res. as_ref ( ) . is_none_or ( is_suppressed)
341+ {
342+ cx. span_lint ( UNMUSTUSE_IN_ALWAYS_OK , span, |d| {
343+ d. primary_message ( format ! ( "this type will no longer be must used: {ty}" ) ) ;
344+ } ) ;
345+ }
346+
347+ if tmp_lint
348+ && !matches ! ( ok_ty. kind( ) , ty:: Param ( _) )
349+ && res. as_ref ( ) . is_some_and ( |x| !is_suppressed ( x) )
350+ {
351+ cx. span_lint ( MUSTUSE_IN_ALWAYS_OK , span, |d| {
352+ d. primary_message ( format ! ( "this type continue be must used: {ty}" ) ) ;
353+ } ) ;
354+ }
355+
356+ res
293357 }
294358 // Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
295359 ty:: Adt ( def, args)
296360 if cx. tcx . is_diagnostic_item ( sym:: ControlFlow , def. did ( ) )
297361 && is_uninhabited ( args. type_at ( 0 ) ) =>
298362 {
299363 let continue_ty = args. type_at ( 1 ) ;
300- is_ty_must_use ( cx, continue_ty, expr, span) . map ( Box :: new) . map ( MustUsePath :: ControlFlow )
364+ let res = is_ty_must_use ( cx, continue_ty, expr, span, tmp_lint)
365+ . map ( Box :: new)
366+ . map ( MustUsePath :: ControlFlow ) ;
367+
368+ if tmp_lint
369+ && !matches ! ( continue_ty. kind( ) , ty:: Param ( _) )
370+ && res. as_ref ( ) . is_none_or ( is_suppressed)
371+ {
372+ cx. span_lint ( UNMUSTUSE_IN_ALWAYS_OK , span, |d| {
373+ d. primary_message ( format ! ( "this type will no longer be must used: {ty}" ) ) ;
374+ } ) ;
375+ }
376+
377+ if tmp_lint
378+ && !matches ! ( continue_ty. kind( ) , ty:: Param ( _) )
379+ && res. as_ref ( ) . is_some_and ( |x| !is_suppressed ( x) )
380+ {
381+ cx. span_lint ( MUSTUSE_IN_ALWAYS_OK , span, |d| {
382+ d. primary_message ( format ! ( "this type continue be must used: {ty}" ) ) ;
383+ } ) ;
384+ }
385+
386+ res
301387 }
302388 ty:: Adt ( def, _) => is_def_must_use ( cx, def. did ( ) , span) ,
303389 ty:: Alias ( ty:: Opaque | ty:: Projection , ty:: AliasTy { def_id : def, .. } ) => {
@@ -343,7 +429,7 @@ fn is_ty_must_use<'tcx>(
343429 . zip ( elem_exprs)
344430 . enumerate ( )
345431 . filter_map ( |( i, ( ty, expr) ) | {
346- is_ty_must_use ( cx, ty, expr, expr. span ) . map ( |path| ( i, path) )
432+ is_ty_must_use ( cx, ty, expr, expr. span , tmp_lint ) . map ( |path| ( i, path) )
347433 } )
348434 . collect :: < Vec < _ > > ( ) ;
349435
@@ -357,7 +443,7 @@ fn is_ty_must_use<'tcx>(
357443 // If the array is empty we don't lint, to avoid false positives
358444 Some ( 0 ) | None => None ,
359445 // If the array is definitely non-empty, we can do `#[must_use]` checking.
360- Some ( len) => is_ty_must_use ( cx, ty, expr, span)
446+ Some ( len) => is_ty_must_use ( cx, ty, expr, span, tmp_lint )
361447 . map ( |inner| MustUsePath :: Array ( Box :: new ( inner) , len) ) ,
362448 } ,
363449 ty:: Closure ( ..) | ty:: CoroutineClosure ( ..) => Some ( MustUsePath :: Closure ( span) ) ,
0 commit comments