Skip to main content

rustc_lint/unused/
must_use.rs

1use std::iter;
2
3use rustc_errors::pluralize;
4use rustc_hir::def::{DefKind, Res};
5use rustc_hir::def_id::DefId;
6use rustc_hir::{self as hir, LangItem, find_attr};
7use rustc_infer::traits::util::elaborate;
8use rustc_middle::ty::{self, Ty, Unnormalized};
9use rustc_session::{declare_lint, declare_lint_pass};
10use rustc_span::{Span, Symbol, sym};
11use tracing::instrument;
12
13use crate::lints::{
14    UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, UnusedOp, UnusedOpSuggestion,
15    UnusedResult,
16};
17use crate::{LateContext, LateLintPass, LintContext};
18
19#[doc =
r" The `unused_must_use` lint detects unused result of a type flagged as"]
#[doc = r" `#[must_use]`."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" fn returns_result() -> Result<(), ()> {"]
#[doc = r"     Ok(())"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" fn main() {"]
#[doc = r"     returns_result();"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" The `#[must_use]` attribute is an indicator that it is a mistake to"]
#[doc = r" ignore the value. See [the reference] for more details."]
#[doc = r""]
#[doc =
r" [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"]
pub static UNUSED_MUST_USE: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "UNUSED_MUST_USE",
            default_level: ::rustc_lint_defs::Warn,
            desc: "unused result of a type flagged as `#[must_use]`",
            is_externally_loaded: false,
            report_in_external_macro: true,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
20    /// The `unused_must_use` lint detects unused result of a type flagged as
21    /// `#[must_use]`.
22    ///
23    /// ### Example
24    ///
25    /// ```rust
26    /// fn returns_result() -> Result<(), ()> {
27    ///     Ok(())
28    /// }
29    ///
30    /// fn main() {
31    ///     returns_result();
32    /// }
33    /// ```
34    ///
35    /// {{produces}}
36    ///
37    /// ### Explanation
38    ///
39    /// The `#[must_use]` attribute is an indicator that it is a mistake to
40    /// ignore the value. See [the reference] for more details.
41    ///
42    /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
43    pub UNUSED_MUST_USE,
44    Warn,
45    "unused result of a type flagged as `#[must_use]`",
46    report_in_external_macro
47}
48
49#[doc = r" The `unused_results` lint checks for the unused result of an"]
#[doc = r" expression in a statement."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(unused_results)]"]
#[doc = r" fn foo<T>() -> T { panic!() }"]
#[doc = r""]
#[doc = r" fn main() {"]
#[doc = r"     foo::<usize>();"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Ignoring the return value of a function may indicate a mistake. In"]
#[doc =
r" cases were it is almost certain that the result should be used, it is"]
#[doc =
r" recommended to annotate the function with the [`must_use` attribute]."]
#[doc =
r" Failure to use such a return value will trigger the [`unused_must_use`"]
#[doc = r" lint] which is warn-by-default. The `unused_results` lint is"]
#[doc = r" essentially the same, but triggers for *all* return values."]
#[doc = r""]
#[doc =
r#" This lint is "allow" by default because it can be noisy, and may not be"#]
#[doc =
r" an actual problem. For example, calling the `remove` method of a `Vec`"]
#[doc =
r" or `HashMap` returns the previous value, which you may not care about."]
#[doc =
r" Using this lint would require explicitly ignoring or discarding such"]
#[doc = r" values."]
#[doc = r""]
#[doc =
r" [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"]
#[doc = r" [`unused_must_use` lint]: warn-by-default.html#unused-must-use"]
pub static UNUSED_RESULTS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "UNUSED_RESULTS",
            default_level: ::rustc_lint_defs::Allow,
            desc: "unused result of an expression in a statement",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
50    /// The `unused_results` lint checks for the unused result of an
51    /// expression in a statement.
52    ///
53    /// ### Example
54    ///
55    /// ```rust,compile_fail
56    /// #![deny(unused_results)]
57    /// fn foo<T>() -> T { panic!() }
58    ///
59    /// fn main() {
60    ///     foo::<usize>();
61    /// }
62    /// ```
63    ///
64    /// {{produces}}
65    ///
66    /// ### Explanation
67    ///
68    /// Ignoring the return value of a function may indicate a mistake. In
69    /// cases were it is almost certain that the result should be used, it is
70    /// recommended to annotate the function with the [`must_use` attribute].
71    /// Failure to use such a return value will trigger the [`unused_must_use`
72    /// lint] which is warn-by-default. The `unused_results` lint is
73    /// essentially the same, but triggers for *all* return values.
74    ///
75    /// This lint is "allow" by default because it can be noisy, and may not be
76    /// an actual problem. For example, calling the `remove` method of a `Vec`
77    /// or `HashMap` returns the previous value, which you may not care about.
78    /// Using this lint would require explicitly ignoring or discarding such
79    /// values.
80    ///
81    /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
82    /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
83    pub UNUSED_RESULTS,
84    Allow,
85    "unused result of an expression in a statement"
86}
87
88pub struct UnusedResults;
#[automatically_derived]
impl ::core::marker::Copy for UnusedResults { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for UnusedResults { }
#[automatically_derived]
impl ::core::clone::Clone for UnusedResults {
    #[inline]
    fn clone(&self) -> UnusedResults { *self }
}
impl ::rustc_lint_defs::LintPass for UnusedResults {
    fn name(&self) -> &'static str { "UnusedResults" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [UNUSED_MUST_USE, UNUSED_RESULTS]))
    }
}
impl UnusedResults {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [UNUSED_MUST_USE, UNUSED_RESULTS]))
    }
}declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
89
90/// Must the type be used?
91#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IsTyMustUse {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            IsTyMustUse::Yes(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Yes",
                    &__self_0),
            IsTyMustUse::No => ::core::fmt::Formatter::write_str(f, "No"),
            IsTyMustUse::Trivial =>
                ::core::fmt::Formatter::write_str(f, "Trivial"),
        }
    }
}Debug)]
92pub enum IsTyMustUse {
93    /// Yes, `MustUsePath` contains an explanation for why the type must be used.
94    /// This will result in `unused_must_use` lint.
95    Yes(MustUsePath),
96    /// No, an ordinary type that may be ignored.
97    /// This will result in `unused_results` lint.
98    No,
99    /// No, the type is trivial and thus should always be ignored.
100    /// (this suppresses `unused_results` lint)
101    Trivial,
102}
103
104impl IsTyMustUse {
105    fn map(self, f: impl FnOnce(MustUsePath) -> MustUsePath) -> Self {
106        match self {
107            Self::Yes(must_use_path) => Self::Yes(f(must_use_path)),
108            _ => self,
109        }
110    }
111}
112
113/// A path through a type to a `must_use` source. Contains useful info for the lint.
114#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MustUsePath {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            MustUsePath::Def(__self_0, __self_1, __self_2) =>
                ::core::fmt::Formatter::debug_tuple_field3_finish(f, "Def",
                    __self_0, __self_1, &__self_2),
            MustUsePath::Boxed(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Boxed",
                    &__self_0),
            MustUsePath::Pinned(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Pinned",
                    &__self_0),
            MustUsePath::Opaque(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Opaque",
                    &__self_0),
            MustUsePath::TraitObject(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "TraitObject", &__self_0),
            MustUsePath::TupleElement(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "TupleElement", &__self_0),
            MustUsePath::Result(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Result",
                    &__self_0),
            MustUsePath::ControlFlow(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ControlFlow", &__self_0),
            MustUsePath::Array(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f, "Array",
                    __self_0, &__self_1),
            MustUsePath::Closure(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Closure", &__self_0),
            MustUsePath::Coroutine(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Coroutine", &__self_0),
        }
    }
}Debug)]
115pub enum MustUsePath {
116    /// The root of the normal `must_use` lint with an optional message.
117    Def(Span, DefId, Option<Symbol>),
118    Boxed(Box<Self>),
119    Pinned(Box<Self>),
120    Opaque(Box<Self>),
121    TraitObject(Box<Self>),
122    TupleElement(Vec<(usize, Self)>),
123    /// `Result<T, Uninhabited>`
124    Result(Box<Self>),
125    /// `ControlFlow<Uninhabited, T>`
126    ControlFlow(Box<Self>),
127    Array(Box<Self>, u64),
128    /// The root of the unused_closures lint.
129    Closure(Span),
130    /// The root of the unused_coroutines lint.
131    Coroutine(Span),
132}
133
134/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
135/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
136///
137/// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
138/// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
139/// so clippy can use this).
140//
141// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
142x;#[instrument(skip(cx, expr), level = "debug", ret)]
143pub fn is_ty_must_use<'tcx>(
144    cx: &LateContext<'tcx>,
145    ty: Ty<'tcx>,
146    expr: &hir::Expr<'_>,
147    simplify_uninhabited: bool,
148) -> IsTyMustUse {
149    if ty.is_unit() {
150        return IsTyMustUse::Trivial;
151    }
152
153    let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id();
154    let is_uninhabited =
155        |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
156
157    match *ty.kind() {
158        _ if is_uninhabited(ty) => IsTyMustUse::Trivial,
159        ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
160            is_ty_must_use(cx, boxed, expr, simplify_uninhabited)
161                .map(|inner| MustUsePath::Boxed(Box::new(inner)))
162        }
163        ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
164            let pinned_ty = args.type_at(0);
165            is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited)
166                .map(|inner| MustUsePath::Pinned(Box::new(inner)))
167        }
168        // Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
169        ty::Adt(def, args)
170            if simplify_uninhabited
171                && cx.tcx.is_diagnostic_item(sym::Result, def.did())
172                && is_uninhabited(args.type_at(1)) =>
173        {
174            let ok_ty = args.type_at(0);
175            is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited)
176                .map(|path| MustUsePath::Result(Box::new(path)))
177        }
178        // Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
179        ty::Adt(def, args)
180            if simplify_uninhabited
181                && cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
182                && is_uninhabited(args.type_at(0)) =>
183        {
184            let continue_ty = args.type_at(1);
185            is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited)
186                .map(|path| MustUsePath::ControlFlow(Box::new(path)))
187        }
188        // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
189        ty::Adt(def, args)
190            if cx.tcx.is_diagnostic_item(sym::Result, def.did())
191                && args.type_at(0).is_unit()
192                && is_uninhabited(args.type_at(1)) =>
193        {
194            IsTyMustUse::Trivial
195        }
196        // Suppress warnings on `ControlFlow<Uninhabited, ()>` (e.g. `ControlFlow<!, ()>`).
197        ty::Adt(def, args)
198            if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
199                && args.type_at(1).is_unit()
200                && is_uninhabited(args.type_at(0)) =>
201        {
202            IsTyMustUse::Trivial
203        }
204        ty::Adt(def, _) => {
205            is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
206        }
207        ty::Alias(ty::AliasTy {
208            kind: ty::Opaque { def_id: def } | ty::Projection { def_id: def },
209            ..
210        }) => {
211            elaborate(
212                cx.tcx,
213                cx.tcx
214                    .explicit_item_self_bounds(def)
215                    .iter_identity_copied()
216                    .map(Unnormalized::skip_norm_wip),
217            )
218            // We only care about self bounds for the impl-trait
219            .filter_only_self()
220            .find_map(|(pred, _span)| {
221                // We only look at the `DefId`, so it is safe to skip the binder here.
222                if let ty::ClauseKind::Trait(ref poly_trait_predicate) = pred.kind().skip_binder() {
223                    let def_id = poly_trait_predicate.trait_ref.def_id;
224
225                    is_def_must_use(cx, def_id, expr.span)
226                } else {
227                    None
228                }
229            })
230            .map(|inner| MustUsePath::Opaque(Box::new(inner)))
231            .map_or(IsTyMustUse::No, IsTyMustUse::Yes)
232        }
233        ty::Dynamic(binders, _) => binders
234            .iter()
235            .find_map(|predicate| {
236                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
237                    let def_id = trait_ref.def_id;
238                    is_def_must_use(cx, def_id, expr.span)
239                        .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
240                } else {
241                    None
242                }
243            })
244            .map_or(IsTyMustUse::No, IsTyMustUse::Yes),
245        // NB: unit is checked up above; this is only reachable for tuples with at least one element
246        ty::Tuple(tys) => {
247            let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
248                debug_assert_eq!(elem_exprs.len(), tys.len());
249                elem_exprs
250            } else {
251                &[]
252            };
253
254            // Default to `expr`.
255            let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
256
257            let mut all_trivial = true;
258            let mut nested_must_use = Vec::new();
259
260            tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
261                let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited);
262
263                all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
264                if let IsTyMustUse::Yes(path) = must_use {
265                    nested_must_use.push((i, path));
266                }
267            });
268
269            if all_trivial {
270                // If all tuple elements are trivial, mark the whole tuple as such.
271                // i.e. don't emit `unused_results` for types such as `((), ())`
272                IsTyMustUse::Trivial
273            } else if !nested_must_use.is_empty() {
274                IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use))
275            } else {
276                IsTyMustUse::No
277            }
278        }
279        ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
280            // If the array is empty we don't lint, to avoid false positives
281            Some(0) | None => IsTyMustUse::No,
282            // If the array is definitely non-empty, we can do `#[must_use]` checking.
283            Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited)
284                .map(|inner| MustUsePath::Array(Box::new(inner), len)),
285        },
286        ty::Closure(..) | ty::CoroutineClosure(..) => {
287            IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
288        }
289        ty::Coroutine(def_id, ..) => {
290            // async fn should be treated as "implementor of `Future`"
291            if cx.tcx.coroutine_is_async(def_id)
292                && let Some(def_id) = cx.tcx.lang_items().future_trait()
293            {
294                IsTyMustUse::Yes(MustUsePath::Opaque(Box::new(
295                    is_def_must_use(cx, def_id, expr.span)
296                        .expect("future trait is marked as `#[must_use]`"),
297                )))
298            } else {
299                IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span))
300            }
301        }
302        _ => IsTyMustUse::No,
303    }
304}
305
306impl<'tcx> LateLintPass<'tcx> for UnusedResults {
307    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
308        let hir::StmtKind::Semi(mut expr) = s.kind else {
309            return;
310        };
311
312        let mut expr_is_from_block = false;
313        while let hir::ExprKind::Block(blk, ..) = expr.kind
314            && let hir::Block { expr: Some(e), .. } = blk
315        {
316            expr = e;
317            expr_is_from_block = true;
318        }
319
320        if let hir::ExprKind::Ret(..) = expr.kind {
321            return;
322        }
323
324        if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
325            && let ty = cx.typeck_results().expr_ty(await_expr)
326            && let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind()
327            && cx.tcx.ty_is_opaque_future(ty)
328            && let async_fn_def_id = cx.tcx.parent(*future_def_id)
329            && #[allow(non_exhaustive_omitted_patterns)] match cx.tcx.def_kind(async_fn_def_id)
    {
    DefKind::Fn | DefKind::AssocFn => true,
    _ => false,
}matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn)
330            // Check that this `impl Future` actually comes from an `async fn`
331            && cx.tcx.asyncness(async_fn_def_id).is_async()
332            && check_must_use_def(
333                cx,
334                async_fn_def_id,
335                expr.span,
336                "output of future returned by ",
337                "",
338                expr_is_from_block,
339            )
340        {
341            // We have a bare `foo().await;` on an opaque type from an async function that was
342            // annotated with `#[must_use]`.
343            return;
344        }
345
346        let ty = cx.typeck_results().expr_ty(expr);
347
348        let must_use_result = is_ty_must_use(cx, ty, expr, false);
349        let type_lint_emitted_or_trivial = match must_use_result {
350            IsTyMustUse::Yes(path) => {
351                emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
352                true
353            }
354            IsTyMustUse::Trivial => true,
355            IsTyMustUse::No => false,
356        };
357
358        let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
359
360        if !fn_warned && type_lint_emitted_or_trivial {
361            // We don't warn about unused unit or uninhabited types.
362            // (See https://github.com/rust-lang/rust/issues/43806 for details.)
363            return;
364        }
365
366        let must_use_op = match expr.kind {
367            // Hardcoding operators here seemed more expedient than the
368            // refactoring that would be needed to look up the `#[must_use]`
369            // attribute which does exist on the comparison trait methods
370            hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
371                hir::BinOpKind::Eq
372                | hir::BinOpKind::Lt
373                | hir::BinOpKind::Le
374                | hir::BinOpKind::Ne
375                | hir::BinOpKind::Ge
376                | hir::BinOpKind::Gt => Some("comparison"),
377                hir::BinOpKind::Add
378                | hir::BinOpKind::Sub
379                | hir::BinOpKind::Div
380                | hir::BinOpKind::Mul
381                | hir::BinOpKind::Rem => Some("arithmetic operation"),
382                hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
383                hir::BinOpKind::BitXor
384                | hir::BinOpKind::BitAnd
385                | hir::BinOpKind::BitOr
386                | hir::BinOpKind::Shl
387                | hir::BinOpKind::Shr => Some("bitwise operation"),
388            },
389            hir::ExprKind::AddrOf(..) => Some("borrow"),
390            hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
391            hir::ExprKind::Unary(..) => Some("unary operation"),
392            // The `offset_of` macro wraps its contents inside a `const` block.
393            hir::ExprKind::ConstBlock(block) => {
394                let body = cx.tcx.hir_body(block.body);
395                if let hir::ExprKind::Block(block, _) = body.value.kind
396                    && let Some(expr) = block.expr
397                    && let hir::ExprKind::OffsetOf(..) = expr.kind
398                {
399                    Some("`offset_of` call")
400                } else {
401                    None
402                }
403            }
404            _ => None,
405        };
406
407        let op_warned = match must_use_op {
408            Some(must_use_op) => {
409                let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
410                cx.emit_span_lint(
411                    UNUSED_MUST_USE,
412                    expr.span,
413                    UnusedOp {
414                        op: must_use_op,
415                        label: expr.span,
416                        suggestion: if expr_is_from_block {
417                            UnusedOpSuggestion::BlockTailExpr {
418                                before_span: span.shrink_to_lo(),
419                                after_span: span.shrink_to_hi(),
420                            }
421                        } else {
422                            UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() }
423                        },
424                    },
425                );
426                true
427            }
428            None => false,
429        };
430
431        // Only emit unused results lint if we haven't emitted any of the more specific lints and the expression type is non trivial.
432        if !(type_lint_emitted_or_trivial || fn_warned || op_warned) {
433            cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
434        }
435    }
436}
437
438/// Checks if `expr` is a \[method\] call expression marked as `#[must_use]` and emits a lint if so.
439/// Returns `true` if the lint has been emitted.
440fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool {
441    let maybe_def_id = match expr.kind {
442        hir::ExprKind::Call(callee, _) => {
443            if let hir::ExprKind::Path(ref qpath) = callee.kind
444                // `Res::Local` if it was a closure, for which we
445                // do not currently support must-use linting
446                && let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) =
447                    cx.qpath_res(qpath, callee.hir_id)
448            {
449                Some(def_id)
450            } else {
451                None
452            }
453        }
454        hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
455        _ => None,
456    };
457
458    match maybe_def_id {
459        Some(def_id) => {
460            check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block)
461        }
462        None => false,
463    }
464}
465
466fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
467    // check for #[must_use = "..."]
468    {
    {
        'done:
            {
            for i in ::rustc_hir::attrs::HasAttrs::get_attrs(def_id, &cx.tcx)
                {
                #[allow(unused_imports)]
                use rustc_hir::attrs::AttributeKind::*;
                let i: &rustc_hir::Attribute = i;
                match i {
                    rustc_hir::Attribute::Parsed(MustUse { reason, .. }) => {
                        break 'done Some(reason);
                    }
                    rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(cx.tcx, def_id, MustUse { reason, .. } => reason)
469        .map(|reason| MustUsePath::Def(span, def_id, *reason))
470}
471
472/// Returns whether further errors should be suppressed because a lint has been emitted.
473fn check_must_use_def(
474    cx: &LateContext<'_>,
475    def_id: DefId,
476    span: Span,
477    descr_pre_path: &str,
478    descr_post_path: &str,
479    expr_is_from_block: bool,
480) -> bool {
481    is_def_must_use(cx, def_id, span)
482        .map(|must_use_path| {
483            emit_must_use_untranslated(
484                cx,
485                &must_use_path,
486                descr_pre_path,
487                descr_post_path,
488                1,
489                false,
490                expr_is_from_block,
491            )
492        })
493        .is_some()
494}
495
496#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("emit_must_use_untranslated",
                                    "rustc_lint::unused::must_use", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/unused/must_use.rs"),
                                    ::tracing_core::__macro_support::Option::Some(496u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::unused::must_use"),
                                    ::tracing_core::field::FieldSet::new(&["path", "descr_pre",
                                                    "descr_post", "plural_len", "is_inner",
                                                    "expr_is_from_block"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&path)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&descr_pre as
                                                            &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&descr_post as
                                                            &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&plural_len as
                                                            &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&is_inner as
                                                            &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&expr_is_from_block
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let plural_suffix = if plural_len == 1 { "" } else { "s" };
            match path {
                MustUsePath::Boxed(path) => {
                    let descr_pre =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}boxed ", descr_pre))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::Pinned(path) => {
                    let descr_pre =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}pinned ", descr_pre))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::Opaque(path) => {
                    let descr_pre =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}implementer{1} of ",
                                            descr_pre, plural_suffix))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::TraitObject(path) => {
                    let descr_post =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!(" trait object{0}{1}",
                                            plural_suffix, descr_post))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::TupleElement(elems) => {
                    for (index, path) in elems {
                        let descr_post =
                            &::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!(" in tuple element {0}",
                                                index))
                                    });
                        emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                            plural_len, true, expr_is_from_block);
                    }
                }
                MustUsePath::Result(path) => {
                    let descr_post =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!(" in a `Result` with an uninhabited error{0}",
                                            descr_post))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::ControlFlow(path) => {
                    let descr_post =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!(" in a `ControlFlow` with an uninhabited break {0}",
                                            descr_post))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len, true, expr_is_from_block);
                }
                MustUsePath::Array(path, len) => {
                    let descr_pre =
                        &::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}array{1} of ",
                                            descr_pre, plural_suffix))
                                });
                    emit_must_use_untranslated(cx, path, descr_pre, descr_post,
                        plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
                        true, expr_is_from_block);
                }
                MustUsePath::Closure(span) => {
                    cx.emit_span_lint(UNUSED_MUST_USE, *span,
                        UnusedClosure {
                            count: plural_len,
                            pre: descr_pre,
                            post: descr_post,
                        });
                }
                MustUsePath::Coroutine(span) => {
                    cx.emit_span_lint(UNUSED_MUST_USE, *span,
                        UnusedCoroutine {
                            count: plural_len,
                            pre: descr_pre,
                            post: descr_post,
                        });
                }
                MustUsePath::Def(span, def_id, reason) => {
                    let ancenstor_span =
                        span.find_ancestor_not_from_macro().unwrap_or(*span);
                    let is_redundant_let_ignore =
                        cx.sess().source_map().span_to_prev_source(ancenstor_span).ok().map(|prev|
                                    prev.trim_end().ends_with("let _ =")).unwrap_or(false);
                    let suggestion_span =
                        if is_redundant_let_ignore {
                            *span
                        } else { ancenstor_span };
                    cx.emit_span_lint(UNUSED_MUST_USE, ancenstor_span,
                        UnusedDef {
                            pre: descr_pre,
                            post: descr_post,
                            cx,
                            def_id: *def_id,
                            note: *reason,
                            suggestion: (!is_inner).then_some(if expr_is_from_block {
                                    UnusedDefSuggestion::BlockTailExpr {
                                        before_span: suggestion_span.shrink_to_lo(),
                                        after_span: suggestion_span.shrink_to_hi(),
                                    }
                                } else {
                                    UnusedDefSuggestion::NormalExpr {
                                        span: suggestion_span.shrink_to_lo(),
                                    }
                                }),
                        });
                }
            }
        }
    }
}#[instrument(skip(cx), level = "debug")]
497fn emit_must_use_untranslated(
498    cx: &LateContext<'_>,
499    path: &MustUsePath,
500    descr_pre: &str,
501    descr_post: &str,
502    plural_len: usize,
503    is_inner: bool,
504    expr_is_from_block: bool,
505) {
506    let plural_suffix = pluralize!(plural_len);
507
508    match path {
509        MustUsePath::Boxed(path) => {
510            let descr_pre = &format!("{descr_pre}boxed ");
511            emit_must_use_untranslated(
512                cx,
513                path,
514                descr_pre,
515                descr_post,
516                plural_len,
517                true,
518                expr_is_from_block,
519            );
520        }
521        MustUsePath::Pinned(path) => {
522            let descr_pre = &format!("{descr_pre}pinned ");
523            emit_must_use_untranslated(
524                cx,
525                path,
526                descr_pre,
527                descr_post,
528                plural_len,
529                true,
530                expr_is_from_block,
531            );
532        }
533        MustUsePath::Opaque(path) => {
534            let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
535            emit_must_use_untranslated(
536                cx,
537                path,
538                descr_pre,
539                descr_post,
540                plural_len,
541                true,
542                expr_is_from_block,
543            );
544        }
545        MustUsePath::TraitObject(path) => {
546            let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
547            emit_must_use_untranslated(
548                cx,
549                path,
550                descr_pre,
551                descr_post,
552                plural_len,
553                true,
554                expr_is_from_block,
555            );
556        }
557        MustUsePath::TupleElement(elems) => {
558            for (index, path) in elems {
559                let descr_post = &format!(" in tuple element {index}");
560                emit_must_use_untranslated(
561                    cx,
562                    path,
563                    descr_pre,
564                    descr_post,
565                    plural_len,
566                    true,
567                    expr_is_from_block,
568                );
569            }
570        }
571        MustUsePath::Result(path) => {
572            let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}");
573            emit_must_use_untranslated(
574                cx,
575                path,
576                descr_pre,
577                descr_post,
578                plural_len,
579                true,
580                expr_is_from_block,
581            );
582        }
583        MustUsePath::ControlFlow(path) => {
584            let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}");
585            emit_must_use_untranslated(
586                cx,
587                path,
588                descr_pre,
589                descr_post,
590                plural_len,
591                true,
592                expr_is_from_block,
593            );
594        }
595        MustUsePath::Array(path, len) => {
596            let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
597            emit_must_use_untranslated(
598                cx,
599                path,
600                descr_pre,
601                descr_post,
602                plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
603                true,
604                expr_is_from_block,
605            );
606        }
607        MustUsePath::Closure(span) => {
608            cx.emit_span_lint(
609                UNUSED_MUST_USE,
610                *span,
611                UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
612            );
613        }
614        MustUsePath::Coroutine(span) => {
615            cx.emit_span_lint(
616                UNUSED_MUST_USE,
617                *span,
618                UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
619            );
620        }
621        MustUsePath::Def(span, def_id, reason) => {
622            let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span);
623            let is_redundant_let_ignore = cx
624                .sess()
625                .source_map()
626                .span_to_prev_source(ancenstor_span)
627                .ok()
628                .map(|prev| prev.trim_end().ends_with("let _ ="))
629                .unwrap_or(false);
630            let suggestion_span = if is_redundant_let_ignore { *span } else { ancenstor_span };
631            cx.emit_span_lint(
632                UNUSED_MUST_USE,
633                ancenstor_span,
634                UnusedDef {
635                    pre: descr_pre,
636                    post: descr_post,
637                    cx,
638                    def_id: *def_id,
639                    note: *reason,
640                    suggestion: (!is_inner).then_some(if expr_is_from_block {
641                        UnusedDefSuggestion::BlockTailExpr {
642                            before_span: suggestion_span.shrink_to_lo(),
643                            after_span: suggestion_span.shrink_to_hi(),
644                        }
645                    } else {
646                        UnusedDefSuggestion::NormalExpr { span: suggestion_span.shrink_to_lo() }
647                    }),
648                },
649            );
650        }
651    }
652}