Skip to content

Commit 5410d60

Browse files
committed
Auto merge of #62262 - varkor:must_use-adt-components-ii, r=<try>
Extend `#[must_use]` to nested structures Extends the `#[must_use]` lint to apply when `#[must_use]` types are nested within `struct`s (or one-variant `enum`s), making the lint much more generally useful. This is in line with #61100 extending the lint to tuples. Fixes #39524. cc @rust-lang/lang and @rust-lang/compiler for discussion in case this is a controversial change. In particular, we might want to consider allowing annotations on fields containing `#[must_use]` types in user-defined types (e.g. `#[allow(unused_must_use)]`) to opt out of this behaviour, if there are cases where we this this is likely to have frequent false positives. (This is based on top of #62235.)
2 parents f690098 + a9d2a6c commit 5410d60

File tree

26 files changed

+272
-45
lines changed

26 files changed

+272
-45
lines changed

src/liballoc/string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,7 @@ impl String {
15711571
Unbounded => {},
15721572
};
15731573

1574-
unsafe {
1574+
let _ = unsafe {
15751575
self.as_mut_vec()
15761576
}.splice(range, replace_with.bytes());
15771577
}

src/liballoc/tests/vec.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ fn test_drain_inclusive_out_of_bounds() {
583583
fn test_splice() {
584584
let mut v = vec![1, 2, 3, 4, 5];
585585
let a = [10, 11, 12];
586-
v.splice(2..4, a.iter().cloned());
586+
let _ = v.splice(2..4, a.iter().cloned());
587587
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
588588
v.splice(1..3, Some(20));
589589
assert_eq!(v, &[1, 20, 11, 12, 5]);
@@ -606,15 +606,15 @@ fn test_splice_inclusive_range() {
606606
fn test_splice_out_of_bounds() {
607607
let mut v = vec![1, 2, 3, 4, 5];
608608
let a = [10, 11, 12];
609-
v.splice(5..6, a.iter().cloned());
609+
let _ = v.splice(5..6, a.iter().cloned());
610610
}
611611

612612
#[test]
613613
#[should_panic]
614614
fn test_splice_inclusive_out_of_bounds() {
615615
let mut v = vec![1, 2, 3, 4, 5];
616616
let a = [10, 11, 12];
617-
v.splice(5..=5, a.iter().cloned());
617+
let _ = v.splice(5..=5, a.iter().cloned());
618618
}
619619

620620
#[test]

src/librustc/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1948,7 +1948,7 @@ pub struct FieldDef {
19481948
pub struct AdtDef {
19491949
/// `DefId` of the struct, enum or union item.
19501950
pub did: DefId,
1951-
/// Variants of the ADT. If this is a struct or enum, then there will be a single variant.
1951+
/// Variants of the ADT. If this is a struct or union, then there will be a single variant.
19521952
pub variants: IndexVec<self::layout::VariantIdx, VariantDef>,
19531953
/// Flags of the ADT (e.g. is this a struct? is this non-exhaustive?)
19541954
flags: AdtFlags,

src/librustc_lint/unused.rs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use rustc::hir::def::{Res, DefKind};
22
use rustc::hir::def_id::DefId;
3+
use rustc::hir::HirVec;
34
use rustc::lint;
45
use rustc::ty::{self, Ty};
6+
use rustc::ty::subst::Subst;
57
use rustc::ty::adjustment;
8+
use rustc::mir::interpret::{GlobalId, ConstValue};
69
use rustc_data_structures::fx::FxHashMap;
710
use lint::{LateContext, EarlyContext, LintContext, LintArray};
811
use lint::{LintPass, EarlyLintPass, LateLintPass};
@@ -23,7 +26,7 @@ use log::debug;
2326

2427
declare_lint! {
2528
pub UNUSED_MUST_USE,
26-
Warn,
29+
Deny,
2730
"unused result of a type flagged as `#[must_use]`",
2831
report_in_external_macro: true
2932
}
@@ -151,8 +154,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
151154
let descr_pre = &format!("{}boxed ", descr_pre);
152155
check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural)
153156
}
154-
ty::Adt(def, _) => {
155-
check_must_use_def(cx, def.did, span, descr_pre, descr_post)
157+
ty::Adt(def, subst) => {
158+
// Check the type itself for `#[must_use]` annotations.
159+
let mut has_emitted = check_must_use_def(
160+
cx, def.did, span, descr_pre, descr_post);
161+
// Check any fields of the type for `#[must_use]` annotations.
162+
// We ignore ADTs with more than one variant for simplicity and to avoid
163+
// false positives.
164+
// Unions are also ignored (though in theory, we could lint if every field of
165+
// a union was `#[must_use]`).
166+
if def.variants.len() == 1 && !def.is_union() {
167+
let fields = match &expr.node {
168+
hir::ExprKind::Struct(_, fields, _) => {
169+
fields.iter().map(|f| &*f.expr).collect()
170+
}
171+
hir::ExprKind::Call(_, args) => args.iter().collect(),
172+
_ => HirVec::new(),
173+
};
174+
175+
for variant in &def.variants {
176+
for (i, field) in variant.fields.iter().enumerate() {
177+
let descr_post
178+
= &format!(" in field `{}`", field.ident.as_str());
179+
let ty = cx.tcx.type_of(field.did).subst(cx.tcx, subst);
180+
let (expr, span) = if let Some(&field) = fields.get(i) {
181+
(field, field.span)
182+
} else {
183+
(expr, span)
184+
};
185+
has_emitted |= check_must_use_ty(
186+
cx, ty, expr, span, descr_pre, descr_post, plural);
187+
}
188+
}
189+
}
190+
has_emitted
156191
}
157192
ty::Opaque(def, _) => {
158193
let mut has_emitted = false;
@@ -202,24 +237,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
202237
for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
203238
let descr_post = &format!(" in tuple element {}", i);
204239
let span = *spans.get(i).unwrap_or(&span);
205-
if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural) {
206-
has_emitted = true;
207-
}
240+
has_emitted |= check_must_use_ty(
241+
cx, ty, expr, span, descr_pre, descr_post, plural);
208242
}
209243
has_emitted
210244
}
211-
ty::Array(ty, len) => match len.assert_usize(cx.tcx) {
212-
// If the array is definitely non-empty, we can do `#[must_use]` checking.
213-
Some(n) if n != 0 => {
214-
let descr_pre = &format!(
215-
"{}array{} of ",
216-
descr_pre,
217-
plural_suffix,
218-
);
219-
check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true)
245+
ty::Array(ty, mut len) => {
246+
// Try to evaluate the length if it's unevaluated.
247+
// FIXME(59369): we should be able to remove this once we merge
248+
// https://github.com/rust-lang/rust/pull/59369.
249+
if let ConstValue::Unevaluated(def_id, substs) = len.val {
250+
let instance = ty::Instance::resolve(
251+
cx.tcx.global_tcx(),
252+
cx.param_env,
253+
def_id,
254+
substs,
255+
).unwrap();
256+
let global_id = GlobalId {
257+
instance,
258+
promoted: None
259+
};
260+
if let Ok(ct) = cx.tcx.const_eval(cx.param_env.and(global_id)) {
261+
len = ct;
262+
}
263+
}
264+
265+
match len.assert_usize(cx.tcx) {
266+
Some(0) => false, // Empty arrays won't contain any `#[must_use]` types.
267+
// If the array may be non-empty, we do `#[must_use]` checking.
268+
_ => {
269+
let descr_pre = &format!(
270+
"{}array{} of ",
271+
descr_pre,
272+
plural_suffix,
273+
);
274+
check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true)
275+
}
220276
}
221-
// Otherwise, we don't lint, to avoid false positives.
222-
_ => false,
223277
}
224278
_ => false,
225279
}

src/librustc_mir/transform/add_retag.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl MirPass for AddRetag {
9797
.filter(needs_retag)
9898
.collect::<Vec<_>>();
9999
// Emit their retags.
100-
basic_blocks[START_BLOCK].statements.splice(0..0,
100+
let _ = basic_blocks[START_BLOCK].statements.splice(0..0,
101101
places.into_iter().map(|place| Statement {
102102
source_info,
103103
kind: StatementKind::Retag(RetagKind::FnEntry, place),

src/libstd/panicking.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
103103
HOOK_LOCK.write_unlock();
104104

105105
if let Hook::Custom(ptr) = old_hook {
106-
#[allow(unused_must_use)] {
107-
Box::from_raw(ptr);
108-
}
106+
mem::drop(Box::from_raw(ptr));
109107
}
110108
}
111109
}

src/test/incremental/change_crate_order/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ use b::B;
2020

2121
//? #[rustc_clean(label="typeck_tables_of", cfg="rpass2")]
2222
pub fn main() {
23-
A + B;
23+
let _ = A + B;
2424
}

src/test/incremental/warnings-reemitted.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
#![warn(const_err)]
77

88
fn main() {
9-
255u8 + 1; //~ WARNING this expression will panic at run-time
9+
let _ = 255u8 + 1; //~ WARNING this expression will panic at run-time
1010
}

src/test/mir-opt/const_prop/ref_deref.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(unused_must_use)]
2+
13
fn main() {
24
*(&4);
35
}

src/test/pretty/block-disambig.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use std::cell::Cell;
99

10-
fn test1() { let val = &0; { } *val; }
10+
fn test1() { let val = &0; { } let _ = *val; }
1111

1212
fn test2() -> isize { let val = &0; { } *val }
1313

0 commit comments

Comments
 (0)