Skip to main content

rustc_lint/unused/
must_use.rs

1use std::iter;
2
3use rustc_attr_ir::find_attr;
4use rustc_attr_ir::lang_items::LangItem;
5use rustc_errors::pluralize;
6use rustc_hir as hir;
7use rustc_hir::def::{DefKind, Res};
8use rustc_hir::def_id::DefId;
9use rustc_infer::traits::util::elaborate;
10use rustc_lint_defs::{declare_lint, declare_lint_pass};
11use rustc_middle::ty::{self, Ty, Unnormalized};
12use rustc_span::{Span, Symbol, sym};
13use tracing::instrument;
14
15use crate::diagnostics::{
16    UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, UnusedOp, UnusedOpSuggestion,
17    UnusedResult,
18};
19use crate::{LateContext, LateLintPass, LintContext};
20
21#[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]`",
            report_in_external_macro: true,
            ..
        };declare_lint! {
22    /// The `unused_must_use` lint detects unused result of a type flagged as
23    /// `#[must_use]`.
24    ///
25    /// ### Example
26    ///
27    /// ```rust
28    /// fn returns_result() -> Result<(), ()> {
29    ///     Ok(())
30    /// }
31    ///
32    /// fn main() {
33    ///     returns_result();
34    /// }
35    /// ```
36    ///
37    /// {{produces}}
38    ///
39    /// ### Explanation
40    ///
41    /// The `#[must_use]` attribute is an indicator that it is a mistake to
42    /// ignore the value. See [the reference] for more details.
43    ///
44    /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
45    pub UNUSED_MUST_USE,
46    Warn,
47    "unused result of a type flagged as `#[must_use]`",
48    report_in_external_macro
49}
50
51#[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",
            ..
        };declare_lint! {
52    /// The `unused_results` lint checks for the unused result of an
53    /// expression in a statement.
54    ///
55    /// ### Example
56    ///
57    /// ```rust,compile_fail
58    /// #![deny(unused_results)]
59    /// fn foo<T>() -> T { panic!() }
60    ///
61    /// fn main() {
62    ///     foo::<usize>();
63    /// }
64    /// ```
65    ///
66    /// {{produces}}
67    ///
68    /// ### Explanation
69    ///
70    /// Ignoring the return value of a function may indicate a mistake. In
71    /// cases were it is almost certain that the result should be used, it is
72    /// recommended to annotate the function with the [`must_use` attribute].
73    /// Failure to use such a return value will trigger the [`unused_must_use`
74    /// lint] which is warn-by-default. The `unused_results` lint is
75    /// essentially the same, but triggers for *all* return values.
76    ///
77    /// This lint is "allow" by default because it can be noisy, and may not be
78    /// an actual problem. For example, calling the `remove` method of a `Vec`
79    /// or `HashMap` returns the previous value, which you may not care about.
80    /// Using this lint would require explicitly ignoring or discarding such
81    /// values.
82    ///
83    /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
84    /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
85    pub UNUSED_RESULTS,
86    Allow,
87    "unused result of an expression in a statement"
88}
89
90pub 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) -> Self { *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]);
91
92/// Must the type be used?
93#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IsTyMustUse {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Yes(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Yes",
                    &__self_0),
            Self::No => ::core::fmt::Formatter::write_str(f, "No"),
            Self::Trivial => ::core::fmt::Formatter::write_str(f, "Trivial"),
        }
    }
}Debug)]
94pub enum IsTyMustUse {
95    /// Yes, `MustUsePath` contains an explanation for why the type must be used.
96    /// This will result in `unused_must_use` lint.
97    Yes(MustUsePath),
98    /// No, an ordinary type that may be ignored.
99    /// This will result in `unused_results` lint.
100    No,
101    /// No, the type is trivial and thus should always be ignored.
102    /// (this suppresses `unused_results` lint)
103    Trivial,
104}
105
106impl IsTyMustUse {
107    fn map(self, f: impl FnOnce(MustUsePath) -> MustUsePath) -> Self {
108        match self {
109            Self::Yes(must_use_path) => Self::Yes(f(must_use_path)),
110            _ => self,
111        }
112    }
113}
114
115/// A path through a type to a `must_use` source. Contains useful info for the lint.
116#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MustUsePath {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Def(__self_0, __self_1, __self_2) =>
                ::core::fmt::Formatter::debug_tuple_field3_finish(f, "Def",
                    __self_0, __self_1, &__self_2),
            Self::Boxed(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Boxed",
                    &__self_0),
            Self::Pinned(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Pinned",
                    &__self_0),
            Self::Opaque(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Opaque",
                    &__self_0),
            Self::TraitObject(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "TraitObject", &__self_0),
            Self::TupleElement(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "TupleElement", &__self_0),
            Self::Result(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Result",
                    &__self_0),
            Self::ControlFlow(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ControlFlow", &__self_0),
            Self::Array(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f, "Array",
                    __self_0, &__self_1),
            Self::Closure(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Closure", &__self_0),
            Self::Coroutine(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Coroutine", &__self_0),
        }
    }
}Debug)]
117pub enum MustUsePath {
118    /// The root of the normal `must_use` lint with an optional message.
119    Def(Span, DefId, Option<Symbol>),
120    Boxed(Box<Self>),
121    Pinned(Box<Self>),
122    Opaque(Box<Self>),
123    TraitObject(Box<Self>),
124    TupleElement(Vec<(usize, Self)>),
125    /// `Result<T, Uninhabited>`
126    Result(Box<Self>),
127    /// `ControlFlow<Uninhabited, T>`
128    ControlFlow(Box<Self>),
129    Array(Box<Self>, u64),
130    /// The root of the unused_closures lint.
131    Closure(Span),
132    /// The root of the unused_coroutines lint.
133    Coroutine(Span),
134}
135
136/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
137/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
138{}
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("is_ty_must_use",
                                "rustc_lint::unused::must_use", ::tracing::Level::DEBUG,
                                ::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/unused/must_use.rs"),
                                ::tracing_core::__macro_support::Option::Some(138u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_lint::unused::must_use"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("ty")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("ty");
                                                    NAME.as_str()
                                                }], ::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};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ty)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        };
    __tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
    (move ||
                {

                    #[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: IsTyMustUse = loop {};
                        return __tracing_attr_fake_return;
                    }
                    {
                        if ty.is_unit() { return IsTyMustUse::Trivial; }
                        let parent_mod_did = cx.tcx.parent_module(expr.hir_id);
                        let is_uninhabited =
                            |t: Ty<'tcx>|
                                !t.is_inhabited_from(cx.tcx, parent_mod_did,
                                        cx.typing_env());
                        match *ty.kind() {
                            _ if is_uninhabited(ty) => IsTyMustUse::Trivial,
                            ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
                                is_ty_must_use(cx, boxed,
                                        expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
                            }
                            ty::Adt(def, args) if
                                cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
                                let pinned_ty = args.type_at(0);
                                is_ty_must_use(cx, pinned_ty,
                                        expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
                            }
                            ty::Adt(def, args) if
                                cx.tcx.is_diagnostic_item(sym::Result, def.did()) &&
                                    is_uninhabited(args.type_at(1)) => {
                                let ok_ty = args.type_at(0);
                                is_ty_must_use(cx, ok_ty,
                                        expr).map(|path| MustUsePath::Result(Box::new(path)))
                            }
                            ty::Adt(def, args) if
                                cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) &&
                                    is_uninhabited(args.type_at(0)) => {
                                let continue_ty = args.type_at(1);
                                is_ty_must_use(cx, continue_ty,
                                        expr).map(|path| MustUsePath::ControlFlow(Box::new(path)))
                            }
                            ty::Adt(def, _) => {
                                is_def_must_use(cx, def.did(),
                                        expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
                            }
                            ty::Alias(_, ty::AliasTy {
                                kind: ty::Opaque { def_id: def } | ty::Projection {
                                    def_id: def }, .. }) => {
                                elaborate(cx.tcx,
                                                    cx.tcx.explicit_item_self_bounds(def).iter_identity_copied().map(Unnormalized::skip_norm_wip)).filter_only_self().find_map(|(pred,
                                                    _span)|
                                                {
                                                    if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
                                                            pred.kind().skip_binder() {
                                                        let def_id = poly_trait_predicate.trait_ref.def_id;
                                                        is_def_must_use(cx, def_id, expr.span)
                                                    } else { None }
                                                }).map(|inner|
                                            MustUsePath::Opaque(Box::new(inner))).map_or(IsTyMustUse::No,
                                    IsTyMustUse::Yes)
                            }
                            ty::Dynamic(binders, _) =>
                                binders.iter().find_map(|predicate|
                                            {
                                                if let ty::ExistentialPredicate::Trait(ref trait_ref) =
                                                        predicate.skip_binder() {
                                                    let def_id = trait_ref.def_id;
                                                    is_def_must_use(cx, def_id,
                                                            expr.span).map(|inner|
                                                            MustUsePath::TraitObject(Box::new(inner)))
                                                } else { None }
                                            }).map_or(IsTyMustUse::No, IsTyMustUse::Yes),
                            ty::Tuple(tys) => {
                                let elem_exprs =
                                    if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
                                        if true {
                                            {
                                                match (&elem_exprs.len(), &tys.len()) {
                                                    (left_val, right_val) => {
                                                        if !(*left_val == *right_val) {
                                                            let kind = ::core::panicking::AssertKind::Eq;
                                                            ::core::panicking::assert_failed(kind, &*left_val,
                                                                &*right_val, ::core::option::Option::None);
                                                        }
                                                    }
                                                }
                                            };
                                        };
                                        elem_exprs
                                    } else { &[] };
                                let elem_exprs =
                                    elem_exprs.iter().chain(iter::repeat(expr));
                                let mut all_trivial = true;
                                let mut nested_must_use = Vec::new();
                                tys.iter().zip(elem_exprs).enumerate().for_each(|(i,
                                            (ty, expr))|
                                        {
                                            let must_use = is_ty_must_use(cx, ty, expr);
                                            all_trivial &=
                                                #[allow(non_exhaustive_omitted_patterns)] match must_use {
                                                    IsTyMustUse::Trivial => true,
                                                    _ => false,
                                                };
                                            if let IsTyMustUse::Yes(path) = must_use {
                                                nested_must_use.push((i, path));
                                            }
                                        });
                                if all_trivial {
                                    IsTyMustUse::Trivial
                                } else if !nested_must_use.is_empty() {
                                    IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use))
                                } else { IsTyMustUse::No }
                            }
                            ty::Array(ty, len) =>
                                match len.try_to_target_usize(cx.tcx) {
                                    Some(0) | None => IsTyMustUse::No,
                                    Some(len) => {
                                        is_ty_must_use(cx, ty,
                                                expr).map(|inner| MustUsePath::Array(Box::new(inner), len))
                                    }
                                },
                            ty::Closure(..) | ty::CoroutineClosure(..) => {
                                IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
                            }
                            ty::Coroutine(def_id, ..) => {
                                if cx.tcx.coroutine_is_async(def_id) &&
                                        let Some(def_id) = cx.tcx.lang_items().future_trait() {
                                    IsTyMustUse::Yes(MustUsePath::Opaque(Box::new(is_def_must_use(cx,
                                                        def_id,
                                                        expr.span).expect("future trait is marked as `#[must_use]`"))))
                                } else {
                                    IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span))
                                }
                            }
                            _ => IsTyMustUse::No,
                        }
                    }
                })();
{
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/unused/must_use.rs:138",
                        "rustc_lint::unused::must_use", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/unused/must_use.rs"),
                        ::tracing_core::__macro_support::Option::Some(138u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::unused::must_use"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("return")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("return");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};
x;#[instrument(skip(cx, expr), level = "debug", ret)]
139pub fn is_ty_must_use<'tcx>(
140    cx: &LateContext<'tcx>,
141    ty: Ty<'tcx>,
142    expr: &hir::Expr<'_>,
143) -> IsTyMustUse {
144    if ty.is_unit() {
145        return IsTyMustUse::Trivial;
146    }
147
148    let parent_mod_did = cx.tcx.parent_module(expr.hir_id);
149    let is_uninhabited =
150        |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
151
152    match *ty.kind() {
153        _ if is_uninhabited(ty) => IsTyMustUse::Trivial,
154        ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
155            is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
156        }
157        ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
158            let pinned_ty = args.type_at(0);
159            is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
160        }
161        // Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
162        ty::Adt(def, args)
163            if cx.tcx.is_diagnostic_item(sym::Result, def.did())
164                && is_uninhabited(args.type_at(1)) =>
165        {
166            let ok_ty = args.type_at(0);
167            is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path)))
168        }
169        // Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
170        ty::Adt(def, args)
171            if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
172                && is_uninhabited(args.type_at(0)) =>
173        {
174            let continue_ty = args.type_at(1);
175            is_ty_must_use(cx, continue_ty, expr)
176                .map(|path| MustUsePath::ControlFlow(Box::new(path)))
177        }
178        ty::Adt(def, _) => {
179            is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
180        }
181        ty::Alias(
182            _,
183            ty::AliasTy {
184                kind: ty::Opaque { def_id: def } | ty::Projection { def_id: def }, ..
185            },
186        ) => {
187            elaborate(
188                cx.tcx,
189                cx.tcx
190                    .explicit_item_self_bounds(def)
191                    .iter_identity_copied()
192                    .map(Unnormalized::skip_norm_wip),
193            )
194            // We only care about self bounds for the impl-trait
195            .filter_only_self()
196            .find_map(|(pred, _span)| {
197                // We only look at the `DefId`, so it is safe to skip the binder here.
198                if let ty::ClauseKind::Trait(ref poly_trait_predicate) = pred.kind().skip_binder() {
199                    let def_id = poly_trait_predicate.trait_ref.def_id;
200
201                    is_def_must_use(cx, def_id, expr.span)
202                } else {
203                    None
204                }
205            })
206            .map(|inner| MustUsePath::Opaque(Box::new(inner)))
207            .map_or(IsTyMustUse::No, IsTyMustUse::Yes)
208        }
209        ty::Dynamic(binders, _) => binders
210            .iter()
211            .find_map(|predicate| {
212                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
213                    let def_id = trait_ref.def_id;
214                    is_def_must_use(cx, def_id, expr.span)
215                        .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
216                } else {
217                    None
218                }
219            })
220            .map_or(IsTyMustUse::No, IsTyMustUse::Yes),
221        // NB: unit is checked up above; this is only reachable for tuples with at least one element
222        ty::Tuple(tys) => {
223            let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
224                debug_assert_eq!(elem_exprs.len(), tys.len());
225                elem_exprs
226            } else {
227                &[]
228            };
229
230            // Default to `expr`.
231            let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
232
233            let mut all_trivial = true;
234            let mut nested_must_use = Vec::new();
235
236            tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
237                let must_use = is_ty_must_use(cx, ty, expr);
238
239                all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
240                if let IsTyMustUse::Yes(path) = must_use {
241                    nested_must_use.push((i, path));
242                }
243            });
244
245            if all_trivial {
246                // If all tuple elements are trivial, mark the whole tuple as such.
247                // i.e. don't emit `unused_results` for types such as `((), ())`
248                IsTyMustUse::Trivial
249            } else if !nested_must_use.is_empty() {
250                IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use))
251            } else {
252                IsTyMustUse::No
253            }
254        }
255        ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
256            // If the array is empty we don't lint, to avoid false positives
257            Some(0) | None => IsTyMustUse::No,
258            // If the array is definitely non-empty, we can do `#[must_use]` checking.
259            Some(len) => {
260                is_ty_must_use(cx, ty, expr).map(|inner| MustUsePath::Array(Box::new(inner), len))
261            }
262        },
263        ty::Closure(..) | ty::CoroutineClosure(..) => {
264            IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
265        }
266        ty::Coroutine(def_id, ..) => {
267            // async fn should be treated as "implementor of `Future`"
268            if cx.tcx.coroutine_is_async(def_id)
269                && let Some(def_id) = cx.tcx.lang_items().future_trait()
270            {
271                IsTyMustUse::Yes(MustUsePath::Opaque(Box::new(
272                    is_def_must_use(cx, def_id, expr.span)
273                        .expect("future trait is marked as `#[must_use]`"),
274                )))
275            } else {
276                IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span))
277            }
278        }
279        _ => IsTyMustUse::No,
280    }
281}
282
283impl<'tcx> LateLintPass<'tcx> for UnusedResults {
284    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
285        let hir::StmtKind::Semi(mut expr) = s.kind else {
286            return;
287        };
288
289        let mut expr_is_from_block = false;
290        while let hir::ExprKind::Block(blk, ..) = expr.kind
291            && let hir::Block { expr: Some(e), .. } = blk
292        {
293            expr = e;
294            expr_is_from_block = true;
295        }
296
297        if let hir::ExprKind::Ret(..) = expr.kind {
298            return;
299        }
300
301        let ty = cx.typeck_results().expr_ty(expr);
302        let needs_binding = ty.has_significant_drop(cx.tcx, cx.typing_env());
303
304        if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
305            && let ty = cx.typeck_results().expr_ty(await_expr)
306            && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind()
307            && cx.tcx.ty_is_opaque_future(ty)
308            && let async_fn_def_id = cx.tcx.parent(*future_def_id)
309            && #[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)
310            // Check that this `impl Future` actually comes from an `async fn`
311            && cx.tcx.asyncness(async_fn_def_id).is_async()
312            && check_must_use_def(
313                cx,
314                async_fn_def_id,
315                expr.span,
316                "output of future returned by ",
317                "",
318                expr_is_from_block,
319                needs_binding,
320            )
321        {
322            // We have a bare `foo().await;` on an opaque type from an async function that was
323            // annotated with `#[must_use]`.
324            return;
325        }
326
327        let must_use_result = is_ty_must_use(cx, ty, expr);
328        let type_lint_emitted_or_trivial = match must_use_result {
329            IsTyMustUse::Yes(path) => {
330                emit_must_use_untranslated(
331                    cx,
332                    &path,
333                    "",
334                    "",
335                    1,
336                    false,
337                    expr_is_from_block,
338                    needs_binding,
339                );
340                true
341            }
342            IsTyMustUse::Trivial => true,
343            IsTyMustUse::No => false,
344        };
345
346        let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block, needs_binding);
347
348        if !fn_warned && type_lint_emitted_or_trivial {
349            // We don't warn about unused unit or uninhabited types.
350            // (See https://github.com/rust-lang/rust/issues/43806 for details.)
351            return;
352        }
353
354        let must_use_op = match expr.kind {
355            // Hardcoding operators here seemed more expedient than the
356            // refactoring that would be needed to look up the `#[must_use]`
357            // attribute which does exist on the comparison trait methods
358            hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
359                hir::BinOpKind::Eq
360                | hir::BinOpKind::Lt
361                | hir::BinOpKind::Le
362                | hir::BinOpKind::Ne
363                | hir::BinOpKind::Ge
364                | hir::BinOpKind::Gt => Some("comparison"),
365                hir::BinOpKind::Add
366                | hir::BinOpKind::Sub
367                | hir::BinOpKind::Div
368                | hir::BinOpKind::Mul
369                | hir::BinOpKind::Rem => Some("arithmetic operation"),
370                hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
371                hir::BinOpKind::BitXor
372                | hir::BinOpKind::BitAnd
373                | hir::BinOpKind::BitOr
374                | hir::BinOpKind::Shl
375                | hir::BinOpKind::Shr => Some("bitwise operation"),
376            },
377            hir::ExprKind::AddrOf(..) => Some("borrow"),
378            hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
379            hir::ExprKind::Unary(..) => Some("unary operation"),
380            // The `offset_of` macro wraps its contents inside a `const` block.
381            hir::ExprKind::ConstBlock(block) => {
382                let body = cx.tcx.hir_body(block.body);
383                if let hir::ExprKind::Block(block, _) = body.value.kind
384                    && let Some(expr) = block.expr
385                    && let hir::ExprKind::OffsetOf(..) = expr.kind
386                {
387                    Some("`offset_of` call")
388                } else {
389                    None
390                }
391            }
392            _ => None,
393        };
394
395        let op_warned = match must_use_op {
396            Some(must_use_op) => {
397                let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
398                cx.emit_span_lint(
399                    UNUSED_MUST_USE,
400                    expr.span,
401                    UnusedOp {
402                        op: must_use_op,
403                        label: expr.span,
404                        suggestion: if expr_is_from_block {
405                            UnusedOpSuggestion::BlockTailExpr {
406                                before_span: span.shrink_to_lo(),
407                                after_span: span.shrink_to_hi(),
408                            }
409                        } else {
410                            UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() }
411                        },
412                    },
413                );
414                true
415            }
416            None => false,
417        };
418
419        // Only emit unused results lint if we haven't emitted any of the more specific lints and the expression type is non trivial.
420        if !(type_lint_emitted_or_trivial || fn_warned || op_warned) {
421            cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
422        }
423    }
424}
425
426/// Checks if `expr` is a \[method\] call expression marked as `#[must_use]` and emits a lint if so.
427/// Returns `true` if the lint has been emitted.
428fn check_fn_must_use(
429    cx: &LateContext<'_>,
430    expr: &hir::Expr<'_>,
431    expr_is_from_block: bool,
432    needs_binding: bool,
433) -> bool {
434    let maybe_def_id = match expr.kind {
435        hir::ExprKind::Call(callee, _) => {
436            if let hir::ExprKind::Path(ref qpath) = callee.kind
437                // `Res::Local` if it was a closure, for which we
438                // do not currently support must-use linting
439                && let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) =
440                    cx.qpath_res(qpath, callee.hir_id)
441            {
442                Some(def_id)
443            } else {
444                None
445            }
446        }
447        hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
448        _ => None,
449    };
450
451    match maybe_def_id {
452        Some(def_id) => check_must_use_def(
453            cx,
454            def_id,
455            expr.span,
456            "return value of ",
457            "",
458            expr_is_from_block,
459            needs_binding,
460        ),
461        None => false,
462    }
463}
464
465fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
466    // check for #[must_use = "..."]
467    {
    {
        'done:
            {
            for i in ::rustc_attr_ir::HasAttrs::get_attrs(def_id, &cx.tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(MustUse { reason, .. })
                        => {
                        break 'done Some(reason);
                    }
                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(cx.tcx, def_id, MustUse { reason, .. } => reason)
468        .map(|reason| MustUsePath::Def(span, def_id, *reason))
469}
470
471/// Returns whether further errors should be suppressed because a lint has been emitted.
472fn check_must_use_def(
473    cx: &LateContext<'_>,
474    def_id: DefId,
475    span: Span,
476    descr_pre_path: &str,
477    descr_post_path: &str,
478    expr_is_from_block: bool,
479    needs_binding: 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                needs_binding,
492            )
493        })
494        .is_some()
495}
496
497{}
#[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("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/unused/must_use.rs"),
                                    ::tracing_core::__macro_support::Option::Some(497u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::unused::must_use"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("path")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("path");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("descr_pre")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("descr_pre");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("descr_post")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("descr_post");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("plural_len")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("plural_len");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("is_inner")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("is_inner");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("expr_is_from_block")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("expr_is_from_block");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("needs_binding")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("needs_binding");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&path)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&descr_pre as
                                                            &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&descr_post as
                                                            &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&plural_len as
                                                            &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&is_inner as
                                                            &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&expr_is_from_block
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&needs_binding as
                                                            &dyn ::tracing::field::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, needs_binding);
                }
                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, needs_binding);
                }
                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, needs_binding);
                }
                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, needs_binding);
                }
                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, needs_binding);
                    }
                }
                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, needs_binding);
                }
                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, needs_binding);
                }
                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, needs_binding);
                }
                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: if is_inner {
                                None
                            } else {
                                let lo = suggestion_span.shrink_to_lo();
                                let hi = suggestion_span.shrink_to_hi();
                                Some(match (needs_binding, expr_is_from_block) {
                                        (true, true) =>
                                            UnusedDefSuggestion::BindingBlockTailExpr {
                                                before_span: lo,
                                                after_span: hi,
                                            },
                                        (true, false) =>
                                            UnusedDefSuggestion::BindingExpr { span: lo },
                                        (false, true) =>
                                            UnusedDefSuggestion::BlockTailExpr {
                                                before_span: lo,
                                                after_span: hi,
                                            },
                                        (false, false) =>
                                            UnusedDefSuggestion::NormalExpr { span: lo },
                                    })
                            },
                        });
                }
            }
        }
    }
}#[instrument(skip(cx), level = "debug")]
498fn emit_must_use_untranslated(
499    cx: &LateContext<'_>,
500    path: &MustUsePath,
501    descr_pre: &str,
502    descr_post: &str,
503    plural_len: usize,
504    is_inner: bool,
505    expr_is_from_block: bool,
506    needs_binding: bool,
507) {
508    let plural_suffix = pluralize!(plural_len);
509
510    match path {
511        MustUsePath::Boxed(path) => {
512            let descr_pre = &format!("{descr_pre}boxed ");
513            emit_must_use_untranslated(
514                cx,
515                path,
516                descr_pre,
517                descr_post,
518                plural_len,
519                true,
520                expr_is_from_block,
521                needs_binding,
522            );
523        }
524        MustUsePath::Pinned(path) => {
525            let descr_pre = &format!("{descr_pre}pinned ");
526            emit_must_use_untranslated(
527                cx,
528                path,
529                descr_pre,
530                descr_post,
531                plural_len,
532                true,
533                expr_is_from_block,
534                needs_binding,
535            );
536        }
537        MustUsePath::Opaque(path) => {
538            let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
539            emit_must_use_untranslated(
540                cx,
541                path,
542                descr_pre,
543                descr_post,
544                plural_len,
545                true,
546                expr_is_from_block,
547                needs_binding,
548            );
549        }
550        MustUsePath::TraitObject(path) => {
551            let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
552            emit_must_use_untranslated(
553                cx,
554                path,
555                descr_pre,
556                descr_post,
557                plural_len,
558                true,
559                expr_is_from_block,
560                needs_binding,
561            );
562        }
563        MustUsePath::TupleElement(elems) => {
564            for (index, path) in elems {
565                let descr_post = &format!(" in tuple element {index}");
566                emit_must_use_untranslated(
567                    cx,
568                    path,
569                    descr_pre,
570                    descr_post,
571                    plural_len,
572                    true,
573                    expr_is_from_block,
574                    needs_binding,
575                );
576            }
577        }
578        MustUsePath::Result(path) => {
579            let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}");
580            emit_must_use_untranslated(
581                cx,
582                path,
583                descr_pre,
584                descr_post,
585                plural_len,
586                true,
587                expr_is_from_block,
588                needs_binding,
589            );
590        }
591        MustUsePath::ControlFlow(path) => {
592            let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}");
593            emit_must_use_untranslated(
594                cx,
595                path,
596                descr_pre,
597                descr_post,
598                plural_len,
599                true,
600                expr_is_from_block,
601                needs_binding,
602            );
603        }
604        MustUsePath::Array(path, len) => {
605            let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
606            emit_must_use_untranslated(
607                cx,
608                path,
609                descr_pre,
610                descr_post,
611                plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
612                true,
613                expr_is_from_block,
614                needs_binding,
615            );
616        }
617        MustUsePath::Closure(span) => {
618            cx.emit_span_lint(
619                UNUSED_MUST_USE,
620                *span,
621                UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
622            );
623        }
624        MustUsePath::Coroutine(span) => {
625            cx.emit_span_lint(
626                UNUSED_MUST_USE,
627                *span,
628                UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
629            );
630        }
631        MustUsePath::Def(span, def_id, reason) => {
632            let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span);
633            let is_redundant_let_ignore = cx
634                .sess()
635                .source_map()
636                .span_to_prev_source(ancenstor_span)
637                .ok()
638                .map(|prev| prev.trim_end().ends_with("let _ ="))
639                .unwrap_or(false);
640            let suggestion_span = if is_redundant_let_ignore { *span } else { ancenstor_span };
641            cx.emit_span_lint(
642                UNUSED_MUST_USE,
643                ancenstor_span,
644                UnusedDef {
645                    pre: descr_pre,
646                    post: descr_post,
647                    cx,
648                    def_id: *def_id,
649                    note: *reason,
650                    suggestion: if is_inner {
651                        None
652                    } else {
653                        let lo = suggestion_span.shrink_to_lo();
654                        let hi = suggestion_span.shrink_to_hi();
655                        Some(match (needs_binding, expr_is_from_block) {
656                            (true, true) => UnusedDefSuggestion::BindingBlockTailExpr {
657                                before_span: lo,
658                                after_span: hi,
659                            },
660                            (true, false) => UnusedDefSuggestion::BindingExpr { span: lo },
661                            (false, true) => UnusedDefSuggestion::BlockTailExpr {
662                                before_span: lo,
663                                after_span: hi,
664                            },
665                            (false, false) => UnusedDefSuggestion::NormalExpr { span: lo },
666                        })
667                    },
668                },
669            );
670        }
671    }
672}