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::consts::ConstExt;
12use rustc_middle::ty::{self, Ty, Unnormalized};
13use rustc_span::{Span, Symbol, sym};
14use tracing::instrument;
15
16use crate::diagnostics::{
17 UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, UnusedOp, UnusedOpSuggestion,
18 UnusedResult,
19};
20use crate::{LateContext, LateLintPass, LintContext};
21
22#[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! {
23 pub UNUSED_MUST_USE,
47 Warn,
48 "unused result of a type flagged as `#[must_use]`",
49 report_in_external_macro
50}
51
52#[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! {
53 pub UNUSED_RESULTS,
87 Allow,
88 "unused result of an expression in a statement"
89}
90
91pub 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]);
92
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)]
95pub enum IsTyMustUse {
96 Yes(MustUsePath),
99 No,
102 Trivial,
105}
106
107impl IsTyMustUse {
108 fn map(self, f: impl FnOnce(MustUsePath) -> MustUsePath) -> Self {
109 match self {
110 Self::Yes(must_use_path) => Self::Yes(f(must_use_path)),
111 _ => self,
112 }
113 }
114}
115
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)]
118pub enum MustUsePath {
119 Def(Span, DefId, Option<Symbol>),
121 Boxed(Box<Self>),
122 Pinned(Box<Self>),
123 Opaque(Box<Self>),
124 TraitObject(Box<Self>),
125 TupleElement(Vec<(usize, Self)>),
126 Result(Box<Self>),
128 ControlFlow(Box<Self>),
130 Array(Box<Self>, u64),
131 Closure(Span),
133 Coroutine(Span),
135}
136
137{}
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/c36f1457196e315bc204b9564a6a5a7fe7f5a51f/compiler/rustc_lint/src/unused/must_use.rs"),
::tracing_core::__macro_support::Option::Some(139u32),
::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/c36f1457196e315bc204b9564a6a5a7fe7f5a51f/compiler/rustc_lint/src/unused/must_use.rs:139",
"rustc_lint::unused::must_use", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/c36f1457196e315bc204b9564a6a5a7fe7f5a51f/compiler/rustc_lint/src/unused/must_use.rs"),
::tracing_core::__macro_support::Option::Some(139u32),
::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)]
140pub fn is_ty_must_use<'tcx>(
141 cx: &LateContext<'tcx>,
142 ty: Ty<'tcx>,
143 expr: &hir::Expr<'_>,
144) -> IsTyMustUse {
145 if ty.is_unit() {
146 return IsTyMustUse::Trivial;
147 }
148
149 let parent_mod_did = cx.tcx.parent_module(expr.hir_id);
150 let is_uninhabited =
151 |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
152
153 match *ty.kind() {
154 _ if is_uninhabited(ty) => IsTyMustUse::Trivial,
155 ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
156 is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
157 }
158 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
159 let pinned_ty = args.type_at(0);
160 is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
161 }
162 ty::Adt(def, args)
164 if cx.tcx.is_diagnostic_item(sym::Result, def.did())
165 && is_uninhabited(args.type_at(1)) =>
166 {
167 let ok_ty = args.type_at(0);
168 is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path)))
169 }
170 ty::Adt(def, args)
172 if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
173 && is_uninhabited(args.type_at(0)) =>
174 {
175 let continue_ty = args.type_at(1);
176 is_ty_must_use(cx, continue_ty, expr)
177 .map(|path| MustUsePath::ControlFlow(Box::new(path)))
178 }
179 ty::Adt(def, _) => {
180 is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
181 }
182 ty::Alias(
183 _,
184 ty::AliasTy {
185 kind: ty::Opaque { def_id: def } | ty::Projection { def_id: def }, ..
186 },
187 ) => {
188 elaborate(
189 cx.tcx,
190 cx.tcx
191 .explicit_item_self_bounds(def)
192 .iter_identity_copied()
193 .map(Unnormalized::skip_norm_wip),
194 )
195 .filter_only_self()
197 .find_map(|(pred, _span)| {
198 if let ty::ClauseKind::Trait(ref poly_trait_predicate) = pred.kind().skip_binder() {
200 let def_id = poly_trait_predicate.trait_ref.def_id;
201
202 is_def_must_use(cx, def_id, expr.span)
203 } else {
204 None
205 }
206 })
207 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
208 .map_or(IsTyMustUse::No, IsTyMustUse::Yes)
209 }
210 ty::Dynamic(binders, _) => binders
211 .iter()
212 .find_map(|predicate| {
213 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
214 let def_id = trait_ref.def_id;
215 is_def_must_use(cx, def_id, expr.span)
216 .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
217 } else {
218 None
219 }
220 })
221 .map_or(IsTyMustUse::No, IsTyMustUse::Yes),
222 ty::Tuple(tys) => {
224 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
225 debug_assert_eq!(elem_exprs.len(), tys.len());
226 elem_exprs
227 } else {
228 &[]
229 };
230
231 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
233
234 let mut all_trivial = true;
235 let mut nested_must_use = Vec::new();
236
237 tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
238 let must_use = is_ty_must_use(cx, ty, expr);
239
240 all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
241 if let IsTyMustUse::Yes(path) = must_use {
242 nested_must_use.push((i, path));
243 }
244 });
245
246 if all_trivial {
247 IsTyMustUse::Trivial
250 } else if !nested_must_use.is_empty() {
251 IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use))
252 } else {
253 IsTyMustUse::No
254 }
255 }
256 ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
257 Some(0) | None => IsTyMustUse::No,
259 Some(len) => {
261 is_ty_must_use(cx, ty, expr).map(|inner| MustUsePath::Array(Box::new(inner), len))
262 }
263 },
264 ty::Closure(..) | ty::CoroutineClosure(..) => {
265 IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
266 }
267 ty::Coroutine(def_id, ..) => {
268 if cx.tcx.coroutine_is_async(def_id)
270 && let Some(def_id) = cx.tcx.lang_items().future_trait()
271 {
272 IsTyMustUse::Yes(MustUsePath::Opaque(Box::new(
273 is_def_must_use(cx, def_id, expr.span)
274 .expect("future trait is marked as `#[must_use]`"),
275 )))
276 } else {
277 IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span))
278 }
279 }
280 _ => IsTyMustUse::No,
281 }
282}
283
284impl<'tcx> LateLintPass<'tcx> for UnusedResults {
285 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
286 let hir::StmtKind::Semi(mut expr) = s.kind else {
287 return;
288 };
289
290 let mut expr_is_from_block = false;
291 while let hir::ExprKind::Block(blk, ..) = expr.kind
292 && let hir::Block { expr: Some(e), .. } = blk
293 {
294 expr = e;
295 expr_is_from_block = true;
296 }
297
298 if let hir::ExprKind::Ret(..) = expr.kind {
299 return;
300 }
301
302 let ty = cx.typeck_results().expr_ty(expr);
303 let needs_binding = ty.has_significant_drop(cx.tcx, cx.typing_env());
304
305 if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
306 && let ty = cx.typeck_results().expr_ty(await_expr)
307 && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind()
308 && cx.tcx.ty_is_opaque_future(ty)
309 && let async_fn_def_id = cx.tcx.parent(*future_def_id)
310 && #[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)
311 && cx.tcx.asyncness(async_fn_def_id).is_async()
313 && check_must_use_def(
314 cx,
315 async_fn_def_id,
316 expr.span,
317 "output of future returned by ",
318 "",
319 expr_is_from_block,
320 needs_binding,
321 )
322 {
323 return;
326 }
327
328 let must_use_result = is_ty_must_use(cx, ty, expr);
329 let type_lint_emitted_or_trivial = match must_use_result {
330 IsTyMustUse::Yes(path) => {
331 emit_must_use_untranslated(
332 cx,
333 &path,
334 "",
335 "",
336 1,
337 false,
338 expr_is_from_block,
339 needs_binding,
340 );
341 true
342 }
343 IsTyMustUse::Trivial => true,
344 IsTyMustUse::No => false,
345 };
346
347 let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block, needs_binding);
348
349 if !fn_warned && type_lint_emitted_or_trivial {
350 return;
353 }
354
355 let must_use_op = match expr.kind {
356 hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
360 hir::BinOpKind::Eq
361 | hir::BinOpKind::Lt
362 | hir::BinOpKind::Le
363 | hir::BinOpKind::Ne
364 | hir::BinOpKind::Ge
365 | hir::BinOpKind::Gt => Some("comparison"),
366 hir::BinOpKind::Add
367 | hir::BinOpKind::Sub
368 | hir::BinOpKind::Div
369 | hir::BinOpKind::Mul
370 | hir::BinOpKind::Rem => Some("arithmetic operation"),
371 hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
372 hir::BinOpKind::BitXor
373 | hir::BinOpKind::BitAnd
374 | hir::BinOpKind::BitOr
375 | hir::BinOpKind::Shl
376 | hir::BinOpKind::Shr => Some("bitwise operation"),
377 },
378 hir::ExprKind::AddrOf(..) => Some("borrow"),
379 hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
380 hir::ExprKind::Unary(..) => Some("unary operation"),
381 hir::ExprKind::ConstBlock(block) => {
383 let body = cx.tcx.hir_body(block.body);
384 if let hir::ExprKind::Block(block, _) = body.value.kind
385 && let Some(expr) = block.expr
386 && let hir::ExprKind::OffsetOf(..) = expr.kind
387 {
388 Some("`offset_of` call")
389 } else {
390 None
391 }
392 }
393 _ => None,
394 };
395
396 let op_warned = match must_use_op {
397 Some(must_use_op) => {
398 let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
399 cx.emit_span_lint(
400 UNUSED_MUST_USE,
401 expr.span,
402 UnusedOp {
403 op: must_use_op,
404 label: expr.span,
405 suggestion: if expr_is_from_block {
406 UnusedOpSuggestion::BlockTailExpr {
407 before_span: span.shrink_to_lo(),
408 after_span: span.shrink_to_hi(),
409 }
410 } else {
411 UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() }
412 },
413 },
414 );
415 true
416 }
417 None => false,
418 };
419
420 if !(type_lint_emitted_or_trivial || fn_warned || op_warned) {
422 cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
423 }
424 }
425}
426
427fn check_fn_must_use(
430 cx: &LateContext<'_>,
431 expr: &hir::Expr<'_>,
432 expr_is_from_block: bool,
433 needs_binding: bool,
434) -> bool {
435 let maybe_def_id = match expr.kind {
436 hir::ExprKind::Call(callee, _) => {
437 if let hir::ExprKind::Path(ref qpath) = callee.kind
438 && let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) =
441 cx.qpath_res(qpath, callee.hir_id)
442 {
443 Some(def_id)
444 } else {
445 None
446 }
447 }
448 hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
449 _ => None,
450 };
451
452 match maybe_def_id {
453 Some(def_id) => check_must_use_def(
454 cx,
455 def_id,
456 expr.span,
457 "return value of ",
458 "",
459 expr_is_from_block,
460 needs_binding,
461 ),
462 None => false,
463 }
464}
465
466fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
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)
469 .map(|reason| MustUsePath::Def(span, def_id, *reason))
470}
471
472fn 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 needs_binding: bool,
481) -> bool {
482 is_def_must_use(cx, def_id, span)
483 .map(|must_use_path| {
484 emit_must_use_untranslated(
485 cx,
486 &must_use_path,
487 descr_pre_path,
488 descr_post_path,
489 1,
490 false,
491 expr_is_from_block,
492 needs_binding,
493 )
494 })
495 .is_some()
496}
497
498{}
#[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/c36f1457196e315bc204b9564a6a5a7fe7f5a51f/compiler/rustc_lint/src/unused/must_use.rs"),
::tracing_core::__macro_support::Option::Some(498u32),
::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")]
499fn emit_must_use_untranslated(
500 cx: &LateContext<'_>,
501 path: &MustUsePath,
502 descr_pre: &str,
503 descr_post: &str,
504 plural_len: usize,
505 is_inner: bool,
506 expr_is_from_block: bool,
507 needs_binding: bool,
508) {
509 let plural_suffix = pluralize!(plural_len);
510
511 match path {
512 MustUsePath::Boxed(path) => {
513 let descr_pre = &format!("{descr_pre}boxed ");
514 emit_must_use_untranslated(
515 cx,
516 path,
517 descr_pre,
518 descr_post,
519 plural_len,
520 true,
521 expr_is_from_block,
522 needs_binding,
523 );
524 }
525 MustUsePath::Pinned(path) => {
526 let descr_pre = &format!("{descr_pre}pinned ");
527 emit_must_use_untranslated(
528 cx,
529 path,
530 descr_pre,
531 descr_post,
532 plural_len,
533 true,
534 expr_is_from_block,
535 needs_binding,
536 );
537 }
538 MustUsePath::Opaque(path) => {
539 let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
540 emit_must_use_untranslated(
541 cx,
542 path,
543 descr_pre,
544 descr_post,
545 plural_len,
546 true,
547 expr_is_from_block,
548 needs_binding,
549 );
550 }
551 MustUsePath::TraitObject(path) => {
552 let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
553 emit_must_use_untranslated(
554 cx,
555 path,
556 descr_pre,
557 descr_post,
558 plural_len,
559 true,
560 expr_is_from_block,
561 needs_binding,
562 );
563 }
564 MustUsePath::TupleElement(elems) => {
565 for (index, path) in elems {
566 let descr_post = &format!(" in tuple element {index}");
567 emit_must_use_untranslated(
568 cx,
569 path,
570 descr_pre,
571 descr_post,
572 plural_len,
573 true,
574 expr_is_from_block,
575 needs_binding,
576 );
577 }
578 }
579 MustUsePath::Result(path) => {
580 let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}");
581 emit_must_use_untranslated(
582 cx,
583 path,
584 descr_pre,
585 descr_post,
586 plural_len,
587 true,
588 expr_is_from_block,
589 needs_binding,
590 );
591 }
592 MustUsePath::ControlFlow(path) => {
593 let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}");
594 emit_must_use_untranslated(
595 cx,
596 path,
597 descr_pre,
598 descr_post,
599 plural_len,
600 true,
601 expr_is_from_block,
602 needs_binding,
603 );
604 }
605 MustUsePath::Array(path, len) => {
606 let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
607 emit_must_use_untranslated(
608 cx,
609 path,
610 descr_pre,
611 descr_post,
612 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
613 true,
614 expr_is_from_block,
615 needs_binding,
616 );
617 }
618 MustUsePath::Closure(span) => {
619 cx.emit_span_lint(
620 UNUSED_MUST_USE,
621 *span,
622 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
623 );
624 }
625 MustUsePath::Coroutine(span) => {
626 cx.emit_span_lint(
627 UNUSED_MUST_USE,
628 *span,
629 UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
630 );
631 }
632 MustUsePath::Def(span, def_id, reason) => {
633 let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span);
634 let is_redundant_let_ignore = cx
635 .sess()
636 .source_map()
637 .span_to_prev_source(ancenstor_span)
638 .ok()
639 .map(|prev| prev.trim_end().ends_with("let _ ="))
640 .unwrap_or(false);
641 let suggestion_span = if is_redundant_let_ignore { *span } else { ancenstor_span };
642 cx.emit_span_lint(
643 UNUSED_MUST_USE,
644 ancenstor_span,
645 UnusedDef {
646 pre: descr_pre,
647 post: descr_post,
648 cx,
649 def_id: *def_id,
650 note: *reason,
651 suggestion: if is_inner {
652 None
653 } else {
654 let lo = suggestion_span.shrink_to_lo();
655 let hi = suggestion_span.shrink_to_hi();
656 Some(match (needs_binding, expr_is_from_block) {
657 (true, true) => UnusedDefSuggestion::BindingBlockTailExpr {
658 before_span: lo,
659 after_span: hi,
660 },
661 (true, false) => UnusedDefSuggestion::BindingExpr { span: lo },
662 (false, true) => UnusedDefSuggestion::BlockTailExpr {
663 before_span: lo,
664 after_span: hi,
665 },
666 (false, false) => UnusedDefSuggestion::NormalExpr { span: lo },
667 })
668 },
669 },
670 );
671 }
672 }
673}