Skip to content

Commit 5ac1de7

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 45859b7 + 8dbc243 commit 5ac1de7

File tree

26 files changed

+244
-36
lines changed

26 files changed

+244
-36
lines changed

src/liballoc/string.rs

+1-1
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

+3-3
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_lint/unused.rs

+43-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
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;
68
use rustc_data_structures::fx::FxHashMap;
79
use lint::{LateContext, EarlyContext, LintContext, LintArray};
@@ -23,7 +25,7 @@ use log::debug;
2325

2426
declare_lint! {
2527
pub UNUSED_MUST_USE,
26-
Warn,
28+
Deny,
2729
"unused result of a type flagged as `#[must_use]`",
2830
report_in_external_macro: true
2931
}
@@ -151,8 +153,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
151153
let descr_pre = &format!("{}boxed ", descr_pre);
152154
check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural)
153155
}
154-
ty::Adt(def, _) => {
155-
check_must_use_def(cx, def.did, span, descr_pre, descr_post)
156+
ty::Adt(def, subst) => {
157+
// Check the type itself for `#[must_use]` annotations.
158+
let mut has_emitted = check_must_use_def(
159+
cx, def.did, span, descr_pre, descr_post);
160+
// Check any fields of the type for `#[must_use]` annotations.
161+
// We ignore ADTs with more than one variant for simplicity and to avoid
162+
// false positives.
163+
// Unions are also ignored (though in theory, we could lint if every field of
164+
// a union was `#[must_use]`).
165+
if def.variants.len() == 1 && !def.is_union() {
166+
let fields = match &expr.node {
167+
hir::ExprKind::Struct(_, fields, _) => {
168+
fields.iter().map(|f| &*f.expr).collect()
169+
}
170+
hir::ExprKind::Call(_, args) => args.iter().collect(),
171+
_ => HirVec::new(),
172+
};
173+
174+
for variant in &def.variants {
175+
for (i, field) in variant.fields.iter().enumerate() {
176+
let descr_post
177+
= &format!(" in field `{}`", field.ident.as_str());
178+
let ty = cx.tcx.type_of(field.did).subst(cx.tcx, subst);
179+
let (expr, span) = if let Some(&field) = fields.get(i) {
180+
(field, field.span)
181+
} else {
182+
(expr, span)
183+
};
184+
has_emitted |= check_must_use_ty(
185+
cx, ty, expr, span, descr_pre, descr_post, plural);
186+
}
187+
}
188+
}
189+
has_emitted
156190
}
157191
ty::Opaque(def, _) => {
158192
let mut has_emitted = false;
@@ -202,24 +236,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
202236
for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
203237
let descr_post = &format!(" in tuple element {}", i);
204238
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-
}
239+
has_emitted |= check_must_use_ty(
240+
cx, ty, expr, span, descr_pre, descr_post, plural);
208241
}
209242
has_emitted
210243
}
211244
ty::Array(ty, len) => match len.try_eval_usize(cx.tcx, cx.param_env) {
212-
// If the array is definitely non-empty, we can do `#[must_use]` checking.
213-
Some(n) if n != 0 => {
245+
// Empty arrays won't contain any `#[must_use]` types.
246+
Some(0) => false,
247+
// If the array may be non-empty, we do `#[must_use]` checking.
248+
_ => {
214249
let descr_pre = &format!(
215250
"{}array{} of ",
216251
descr_pre,
217252
plural_suffix,
218253
);
219254
check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true)
220255
}
221-
// Otherwise, we don't lint, to avoid false positives.
222-
_ => false,
223256
}
224257
_ => false,
225258
}

src/librustc_mir/transform/add_retag.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
9393
.filter(needs_retag)
9494
.collect::<Vec<_>>();
9595
// Emit their retags.
96-
basic_blocks[START_BLOCK].statements.splice(0..0,
96+
let _ = basic_blocks[START_BLOCK].statements.splice(0..0,
9797
places.into_iter().map(|place| Statement {
9898
source_info,
9999
kind: StatementKind::Retag(RetagKind::FnEntry, place),

src/libstd/panicking.rs

+1-3
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

+1-1
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

+1-1
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

+2
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

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use std::cell::Cell;
99

10+
#[allow(unused_must_use)]
1011
fn test1() { let val = &0; { } *val; }
1112

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

src/test/pretty/unary-op-disambig.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ fn alt_semi() -> isize { match true { true => { f() } _ => { } }; -1 }
1616

1717
fn alt_no_semi() -> isize { (match true { true => { 0 } _ => { 1 } }) - 1 }
1818

19+
#[allow(unused_must_use)]
1920
fn stmt() { { f() }; -1; }

src/test/run-fail/binop-fail-3.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ fn foo() -> ! {
55

66
#[allow(resolve_trait_on_defaulted_unit)]
77
fn main() {
8-
foo() == foo(); // these types wind up being defaulted to ()
8+
let _ = foo() == foo(); // these types wind up being defaulted to ()
99
}

src/test/run-fail/binop-panic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ fn my_err(s: String) -> ! {
44
panic!("quux");
55
}
66
fn main() {
7-
3_usize == my_err("bye".to_string());
7+
let _ = 3_usize == my_err("bye".to_string());
88
}

src/test/run-fail/generator-resume-after-panic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn main() {
1515
panic!();
1616
yield;
1717
};
18-
panic::catch_unwind(panic::AssertUnwindSafe(|| {
18+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
1919
let x = Pin::new(&mut g).resume();
2020
}));
2121
Pin::new(&mut g).resume();

src/test/run-fail/issue-28934.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ impl<'i, 't> Parser<'i, 't> {
1919

2020
fn main() {
2121
let x = 0u8;
22-
Parser(&x, &x).parse_nested_block(|input| input.expect_exhausted()).unwrap();
22+
let _ = Parser(&x, &x).parse_nested_block(|input| input.expect_exhausted()).unwrap();
2323
}

src/test/run-fail/panic-set-unset-handler.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ fn main() {
66
panic::set_hook(Box::new(|i| {
77
eprint!("greetings from the panic handler");
88
}));
9-
panic::take_hook();
9+
let _ = panic::take_hook();
1010
panic!("foobar");
1111
}

src/test/run-fail/panic-take-handler-nop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
use std::panic;
44

55
fn main() {
6-
panic::take_hook();
6+
let _ = panic::take_hook();
77
panic!("foobar");
88
}

src/test/run-make-fulldeps/save-analysis-fail/foo.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
#![ crate_name = "test" ]
1+
#![crate_name = "test"]
22
#![feature(box_syntax)]
33
#![feature(rustc_private)]
44

5+
#![allow(unused_must_use)]
6+
57
extern crate graphviz;
68
// A simple rust project
79

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#![ crate_name = "krate2" ]
2-
#![ crate_type = "lib" ]
1+
#![crate_name = "krate2"]
2+
#![crate_type = "lib"]
33

44
use std::io::Write;
55

66
pub fn hello() {
7-
std::io::stdout().write_all(b"hello world!\n");
7+
let _ = std::io::stdout().write_all(b"hello world!\n");
88
}

src/test/run-make-fulldeps/save-analysis/foo.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
#![ crate_name = "test" ]
1+
#![crate_name = "test"]
22
#![feature(box_syntax)]
33
#![feature(rustc_private)]
44
#![feature(associated_type_defaults)]
55
#![feature(external_doc)]
66

7+
#![allow(unused_must_use)]
8+
79
extern crate graphviz;
810
// A simple rust project
911

src/test/run-make-fulldeps/save-analysis/krate2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
use std::io::Write;
55

66
pub fn hello() {
7-
std::io::stdout().write_all(b"hello world!\n");
7+
let _ = std::io::stdout().write_all(b"hello world!\n");
88
}

src/test/ui/cross-crate/auxiliary/cci_capture_clause.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::mpsc::{Receiver, channel};
44
pub fn foo<T:'static + Send + Clone>(x: T) -> Receiver<T> {
55
let (tx, rx) = channel();
66
thread::spawn(move|| {
7-
tx.send(x.clone());
7+
let _ = tx.send(x.clone());
88
});
99
rx
1010
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
pub unsafe fn f(xs: Vec<isize> ) {
2-
xs.iter().map(|_x| { unsafe fn q() { panic!(); } }).collect::<Vec<()>>();
2+
let _ = xs.iter().map(|_x| { unsafe fn q() { panic!(); } }).collect::<Vec<()>>();
33
}

src/test/ui/issues/auxiliary/issue-9906.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ mod other {
1010
}
1111

1212
pub fn foo(){
13-
1+1;
13+
let _ = 1 + 1;
1414
}
1515
}

src/test/ui/lint/must_use-adt.rs

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![deny(unused_must_use)]
2+
3+
#[must_use]
4+
struct S;
5+
6+
#[must_use]
7+
trait A {}
8+
9+
struct B;
10+
11+
impl A for B {}
12+
13+
struct T(S);
14+
15+
struct U {
16+
x: (),
17+
y: T,
18+
}
19+
20+
struct V {
21+
a: S,
22+
}
23+
24+
struct W {
25+
w: [(u8, Box<dyn A>); 2],
26+
x: u32,
27+
y: (B, B),
28+
z: (S, S),
29+
e: [(u8, Box<dyn A>); 2],
30+
f: S,
31+
}
32+
33+
fn get_v() -> V {
34+
V { a: S }
35+
}
36+
37+
struct Z([(u8, Box<dyn A>); 2]);
38+
39+
fn get_wrapped_arr() -> Z {
40+
Z([(0, Box::new(B)), (0, Box::new(B))])
41+
}
42+
43+
fn get_tuple_arr() -> ([(u8, Box<dyn A>); 2],) {
44+
([(0, Box::new(B)), (0, Box::new(B))],)
45+
}
46+
47+
struct R<T> {
48+
r: T
49+
}
50+
51+
struct List<T>(T, Option<Box<Self>>);
52+
53+
fn main() {
54+
S; //~ ERROR unused `S` that must be used
55+
T(S); //~ ERROR unused `S` in field `0` that must be used
56+
U { x: (), y: T(S) }; //~ ERROR unused `S` in field `0` that must be used
57+
get_v(); //~ ERROR unused `S` in field `a` that must be used
58+
V { a: S }; //~ ERROR unused `S` in field `a` that must be used
59+
W {
60+
w: [(0, Box::new(B)), (0, Box::new(B))],
61+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used
62+
x: 0,
63+
y: (B, B),
64+
z: (S, S),
65+
//~^ unused `S` in tuple element 0 that must be used
66+
//~^^ unused `S` in tuple element 1 that must be used
67+
e: [(0, Box::new(B)), (0, Box::new(B))],
68+
//~^ unused array of boxed `A` trait objects in tuple element 1 that must be used
69+
f: S, //~ ERROR unused `S` in field `f` that must be used
70+
};
71+
get_wrapped_arr();
72+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be use
73+
get_tuple_arr();
74+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used
75+
R { r: S }; //~ ERROR unused `S` in field `r` that must be used
76+
List(S, Some(Box::new(List(S, None)))); //~ ERROR unused `S` in field `0` that must be used
77+
}

0 commit comments

Comments
 (0)