Skip to content

Commit 7392cc1

Browse files
committed
Use #[must_use] determination from the compiler
1 parent e645f93 commit 7392cc1

7 files changed

Lines changed: 31 additions & 47 deletions

File tree

clippy_lints/src/drop_forget_ref.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
9898
sym::mem_drop
9999
if !(arg_ty.needs_drop(cx.tcx, cx.typing_env())
100100
|| is_must_use_func_call(cx, arg)
101-
|| is_must_use_ty(cx, arg_ty)
101+
|| is_must_use_ty(cx, arg_ty, expr.hir_id)
102102
|| drop_is_single_call_in_arm) =>
103103
{
104104
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))

clippy_lints/src/functions/must_use.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
3939
cx,
4040
sig.decl,
4141
item.owner_id,
42+
item.hir_id(),
4243
item.span,
4344
fn_header_span,
4445
*attr_span,
@@ -71,6 +72,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
7172
cx,
7273
sig.decl,
7374
item.owner_id,
75+
item.hir_id(),
7476
item.span,
7577
fn_header_span,
7678
*attr_span,
@@ -104,6 +106,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
104106
cx,
105107
sig.decl,
106108
item.owner_id,
109+
item.hir_id(),
107110
item.span,
108111
fn_header_span,
109112
*attr_span,
@@ -134,6 +137,7 @@ fn check_needless_must_use(
134137
cx: &LateContext<'_>,
135138
decl: &hir::FnDecl<'_>,
136139
item_id: hir::OwnerId,
140+
item_hir_id: hir::HirId,
137141
item_span: Span,
138142
fn_header_span: Span,
139143
attr_span: Span,
@@ -170,12 +174,12 @@ fn check_needless_must_use(
170174
"remove `must_use`",
171175
);
172176
}
173-
} else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
177+
} else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id), item_hir_id) {
174178
// Ignore async functions unless Future::Output type is a must_use type
175179
if sig.header.is_async() {
176180
let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
177181
if let Some(future_ty) = infcx.err_ctxt().get_impl_future_output_ty(return_ty(cx, item_id))
178-
&& !is_must_use_ty(cx, future_ty)
182+
&& !is_must_use_ty(cx, future_ty, item_hir_id)
179183
{
180184
return;
181185
}
@@ -206,7 +210,7 @@ fn check_must_use_candidate<'tcx>(
206210
|| item_span.in_external_macro(cx.sess().source_map())
207211
|| returns_unit(decl)
208212
|| !cx.effective_visibilities.is_exported(item_id.def_id)
209-
|| is_must_use_ty(cx, return_ty(cx, item_id))
213+
|| is_must_use_ty(cx, return_ty(cx, item_id), body.value.hir_id)
210214
|| item_span.from_expansion()
211215
|| is_entrypoint_fn(cx, item_id.def_id.to_def_id())
212216
{

clippy_lints/src/let_underscore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
175175
diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`");
176176
},
177177
);
178-
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
178+
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init), init.hir_id) {
179179
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
180180
span_lint_and_then(
181181
cx,

clippy_lints/src/return_self_not_must_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa
8787
// there is one, we shouldn't emit a warning!
8888
&& self_arg.peel_refs() == ret_ty
8989
// If `Self` is already marked as `#[must_use]`, no need for the attribute here.
90-
&& !is_must_use_ty(cx, ret_ty)
90+
&& !is_must_use_ty(cx, ret_ty, cx.tcx.local_def_id_to_hir_id(fn_def))
9191
{
9292
span_lint_and_help(
9393
cx,

clippy_utils/src/ty/mod.rs

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
99
use rustc_hir as hir;
1010
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
1111
use rustc_hir::def_id::DefId;
12-
use rustc_hir::{Expr, FnDecl, LangItem, find_attr};
12+
use rustc_hir::{Expr, ExprKind, FnDecl, HirId, LangItem};
1313
use rustc_hir_analysis::lower_ty;
1414
use rustc_infer::infer::TyCtxtInferExt;
1515
use rustc_lint::LateContext;
16+
use rustc_lint::unused::must_use::{IsTyMustUse, is_ty_must_use};
1617
use rustc_middle::mir::ConstValue;
1718
use rustc_middle::mir::interpret::Scalar;
1819
use rustc_middle::traits::EvaluationResult;
@@ -310,46 +311,13 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
310311
// Returns whether the `ty` has `#[must_use]` attribute. If `ty` is a `Result`/`ControlFlow`
311312
// whose `Err`/`Break` payload is an uninhabited type, the `Ok`/`Continue` payload type
312313
// will be used instead. See <https://github.com/rust-lang/rust/pull/148214>.
313-
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
314-
match ty.kind() {
315-
ty::Adt(adt, args) => match cx.tcx.get_diagnostic_name(adt.did()) {
316-
Some(sym::Result) if args.type_at(1).is_privately_uninhabited(cx.tcx, cx.typing_env()) => {
317-
is_must_use_ty(cx, args.type_at(0))
318-
},
319-
Some(sym::ControlFlow) if args.type_at(0).is_privately_uninhabited(cx.tcx, cx.typing_env()) => {
320-
is_must_use_ty(cx, args.type_at(1))
321-
},
322-
_ => find_attr!(cx.tcx, adt.did(), MustUse { .. }),
323-
},
324-
ty::Foreign(did) => find_attr!(cx.tcx, *did, MustUse { .. }),
325-
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => {
326-
// for the Array case we don't need to care for the len == 0 case
327-
// because we don't want to lint functions returning empty arrays
328-
is_must_use_ty(cx, *ty)
329-
},
330-
ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)),
331-
ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
332-
for (predicate, _) in cx.tcx.explicit_item_self_bounds(*def_id).skip_binder() {
333-
if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
334-
&& find_attr!(cx.tcx, trait_predicate.trait_ref.def_id, MustUse { .. })
335-
{
336-
return true;
337-
}
338-
}
339-
false
340-
},
341-
ty::Dynamic(binder, _) => {
342-
for predicate in *binder {
343-
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
344-
&& find_attr!(cx.tcx, trait_ref.def_id, MustUse { .. })
345-
{
346-
return true;
347-
}
348-
}
349-
false
350-
},
351-
_ => false,
352-
}
314+
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, hir_id: HirId) -> bool {
315+
let dummy_expr = Expr {
316+
hir_id,
317+
span: DUMMY_SP,
318+
kind: ExprKind::Ret(None),
319+
};
320+
matches!(is_ty_must_use(cx, ty, &dummy_expr, true), IsTyMustUse::Yes(_))
353321
}
354322

355323
/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any

tests/ui/must_use_candidates.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,9 @@ pub fn result_uninhabited() -> Result<i32, std::convert::Infallible> {
120120
pub fn controlflow_uninhabited() -> std::ops::ControlFlow<std::convert::Infallible, i32> {
121121
todo!()
122122
}
123+
124+
// Do not lint: even though `Box` itself is not `#[must_use]`, if the content is, the
125+
// compiler will treat the box as-is.
126+
pub fn with_box() -> Box<Result<u32, u32>> {
127+
Box::new(Ok(0))
128+
}

tests/ui/must_use_candidates.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,9 @@ pub fn result_uninhabited() -> Result<i32, std::convert::Infallible> {
113113
pub fn controlflow_uninhabited() -> std::ops::ControlFlow<std::convert::Infallible, i32> {
114114
todo!()
115115
}
116+
117+
// Do not lint: even though `Box` itself is not `#[must_use]`, if the content is, the
118+
// compiler will treat the box as a `#[must_use]` type already.
119+
pub fn with_box() -> Box<Result<u32, u32>> {
120+
Box::new(Ok(0))
121+
}

0 commit comments

Comments
 (0)