diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 366191e2c85f3..3ec02f6ef6c46 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1571,7 +1571,7 @@ impl String { Unbounded => {}, }; - unsafe { + let _ = unsafe { self.as_mut_vec() }.splice(range, replace_with.bytes()); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 538afa6054ff9..9c8c4c21e1ea9 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1948,7 +1948,7 @@ pub struct FieldDef { pub struct AdtDef { /// `DefId` of the struct, enum or union item. pub did: DefId, - /// Variants of the ADT. If this is a struct or enum, then there will be a single variant. + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. pub variants: IndexVec, /// Flags of the ADT (e.g. is this a struct? is this non-exhaustive?) flags: AdtFlags, diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 4cccaa942b742..ee0114ced713b 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -1,8 +1,11 @@ use rustc::hir::def::{Res, DefKind}; use rustc::hir::def_id::DefId; +use rustc::hir::HirVec; use rustc::lint; use rustc::ty::{self, Ty}; +use rustc::ty::subst::Subst; use rustc::ty::adjustment; +use rustc::mir::interpret::{GlobalId, ConstValue}; use rustc_data_structures::fx::FxHashMap; use lint::{LateContext, EarlyContext, LintContext, LintArray}; use lint::{LintPass, EarlyLintPass, LateLintPass}; @@ -23,7 +26,7 @@ use log::debug; declare_lint! { pub UNUSED_MUST_USE, - Warn, + Deny, "unused result of a type flagged as `#[must_use]`", report_in_external_macro: true } @@ -151,8 +154,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { let descr_pre = &format!("{}boxed ", descr_pre); check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural) } - ty::Adt(def, _) => { - check_must_use_def(cx, def.did, span, descr_pre, descr_post) + ty::Adt(def, subst) => { + // Check the type itself for `#[must_use]` annotations. + let mut has_emitted = check_must_use_def( + cx, def.did, span, descr_pre, descr_post); + // Check any fields of the type for `#[must_use]` annotations. + // We ignore ADTs with more than one variant for simplicity and to avoid + // false positives. + // Unions are also ignored (though in theory, we could lint if every field of + // a union was `#[must_use]`). + if def.variants.len() == 1 && !def.is_union() { + let fields = match &expr.node { + hir::ExprKind::Struct(_, fields, _) => { + fields.iter().map(|f| &*f.expr).collect() + } + hir::ExprKind::Call(_, args) => args.iter().collect(), + _ => HirVec::new(), + }; + + for variant in &def.variants { + for (i, field) in variant.fields.iter().enumerate() { + let descr_post + = &format!(" in field `{}`", field.ident.as_str()); + let ty = cx.tcx.type_of(field.did).subst(cx.tcx, subst); + let (expr, span) = if let Some(&field) = fields.get(i) { + (field, field.span) + } else { + (expr, span) + }; + has_emitted |= check_must_use_ty( + cx, ty, expr, span, descr_pre, descr_post, plural); + } + } + } + has_emitted } ty::Opaque(def, _) => { let mut has_emitted = false; @@ -202,24 +237,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() { let descr_post = &format!(" in tuple element {}", i); let span = *spans.get(i).unwrap_or(&span); - if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural) { - has_emitted = true; - } + has_emitted |= check_must_use_ty( + cx, ty, expr, span, descr_pre, descr_post, plural); } has_emitted } - ty::Array(ty, len) => match len.assert_usize(cx.tcx) { - // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(n) if n != 0 => { - let descr_pre = &format!( - "{}array{} of ", - descr_pre, - plural_suffix, - ); - check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true) + ty::Array(ty, mut len) => { + // Try to evaluate the length if it's unevaluated. + // FIXME(59369): we should be able to remove this once we merge + // https://github.com/rust-lang/rust/pull/59369. + if let ConstValue::Unevaluated(def_id, substs) = len.val { + let instance = ty::Instance::resolve( + cx.tcx.global_tcx(), + cx.param_env, + def_id, + substs, + ).unwrap(); + let global_id = GlobalId { + instance, + promoted: None + }; + if let Ok(ct) = cx.tcx.const_eval(cx.param_env.and(global_id)) { + len = ct; + } + } + + match len.assert_usize(cx.tcx) { + Some(0) => false, // Empty arrays won't contain any `#[must_use]` types. + // If the array may be non-empty, we do `#[must_use]` checking. + _ => { + let descr_pre = &format!( + "{}array{} of ", + descr_pre, + plural_suffix, + ); + check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true) + } } - // Otherwise, we don't lint, to avoid false positives. - _ => false, } _ => false, } diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs index d573423906c2a..9149e5cc1e58e 100644 --- a/src/librustc_mir/transform/add_retag.rs +++ b/src/librustc_mir/transform/add_retag.rs @@ -97,7 +97,7 @@ impl MirPass for AddRetag { .filter(needs_retag) .collect::>(); // Emit their retags. - basic_blocks[START_BLOCK].statements.splice(0..0, + let _ = basic_blocks[START_BLOCK].statements.splice(0..0, places.into_iter().map(|place| Statement { source_info, kind: StatementKind::Retag(RetagKind::FnEntry, place), diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 952fd9ebfdf07..5cb87e7309c96 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -103,9 +103,7 @@ pub fn set_hook(hook: Box) + 'static + Sync + Send>) { HOOK_LOCK.write_unlock(); if let Hook::Custom(ptr) = old_hook { - #[allow(unused_must_use)] { - Box::from_raw(ptr); - } + mem::drop(Box::from_raw(ptr)); } } } diff --git a/src/test/run-pass/cross-crate/auxiliary/cci_capture_clause.rs b/src/test/run-pass/cross-crate/auxiliary/cci_capture_clause.rs index 4cd001ecc9e60..0f48f05185851 100644 --- a/src/test/run-pass/cross-crate/auxiliary/cci_capture_clause.rs +++ b/src/test/run-pass/cross-crate/auxiliary/cci_capture_clause.rs @@ -4,7 +4,7 @@ use std::sync::mpsc::{Receiver, channel}; pub fn foo(x: T) -> Receiver { let (tx, rx) = channel(); thread::spawn(move|| { - tx.send(x.clone()); + let _ = tx.send(x.clone()); }); rx } diff --git a/src/test/run-pass/issues/auxiliary/issue-2723-a.rs b/src/test/run-pass/issues/auxiliary/issue-2723-a.rs index 661b46d829dfe..edb93548475d9 100644 --- a/src/test/run-pass/issues/auxiliary/issue-2723-a.rs +++ b/src/test/run-pass/issues/auxiliary/issue-2723-a.rs @@ -1,3 +1,3 @@ pub unsafe fn f(xs: Vec ) { - xs.iter().map(|_x| { unsafe fn q() { panic!(); } }).collect::>(); + let _ = xs.iter().map(|_x| { unsafe fn q() { panic!(); } }).collect::>(); } diff --git a/src/test/run-pass/issues/auxiliary/issue-9906.rs b/src/test/run-pass/issues/auxiliary/issue-9906.rs index 8a3eea790a26a..9a45224086478 100644 --- a/src/test/run-pass/issues/auxiliary/issue-9906.rs +++ b/src/test/run-pass/issues/auxiliary/issue-9906.rs @@ -10,6 +10,6 @@ mod other { } pub fn foo(){ - 1+1; + let _ = 1 + 1; } } diff --git a/src/test/ui/lint/must_use-adt.rs b/src/test/ui/lint/must_use-adt.rs new file mode 100644 index 0000000000000..a958abfda5b19 --- /dev/null +++ b/src/test/ui/lint/must_use-adt.rs @@ -0,0 +1,77 @@ +#![deny(unused_must_use)] + +#[must_use] +struct S; + +#[must_use] +trait A {} + +struct B; + +impl A for B {} + +struct T(S); + +struct U { + x: (), + y: T, +} + +struct V { + a: S, +} + +struct W { + w: [(u8, Box); 2], + x: u32, + y: (B, B), + z: (S, S), + e: [(u8, Box); 2], + f: S, +} + +fn get_v() -> V { + V { a: S } +} + +struct Z([(u8, Box); 2]); + +fn get_wrapped_arr() -> Z { + Z([(0, Box::new(B)), (0, Box::new(B))]) +} + +fn get_tuple_arr() -> ([(u8, Box); 2],) { + ([(0, Box::new(B)), (0, Box::new(B))],) +} + +struct R { + r: T +} + +struct List(T, Option>); + +fn main() { + S; //~ ERROR unused `S` that must be used + T(S); //~ ERROR unused `S` in field `0` that must be used + U { x: (), y: T(S) }; //~ ERROR unused `S` in field `0` that must be used + get_v(); //~ ERROR unused `S` in field `a` that must be used + V { a: S }; //~ ERROR unused `S` in field `a` that must be used + W { + w: [(0, Box::new(B)), (0, Box::new(B))], + //~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used + x: 0, + y: (B, B), + z: (S, S), + //~^ unused `S` in tuple element 0 that must be used + //~^^ unused `S` in tuple element 1 that must be used + e: [(0, Box::new(B)), (0, Box::new(B))], + //~^ unused array of boxed `A` trait objects in tuple element 1 that must be used + f: S, //~ ERROR unused `S` in field `f` that must be used + }; + get_wrapped_arr(); + //~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be use + get_tuple_arr(); + //~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used + R { r: S }; //~ ERROR unused `S` in field `r` that must be used + List(S, Some(Box::new(List(S, None)))); //~ ERROR unused `S` in field `0` that must be used +} diff --git a/src/test/ui/lint/must_use-adt.stderr b/src/test/ui/lint/must_use-adt.stderr new file mode 100644 index 0000000000000..334696807e8ec --- /dev/null +++ b/src/test/ui/lint/must_use-adt.stderr @@ -0,0 +1,92 @@ +error: unused `S` that must be used + --> $DIR/must_use-adt.rs:54:5 + | +LL | S; + | ^^ + | +note: lint level defined here + --> $DIR/must_use-adt.rs:1:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ + +error: unused `S` in field `0` that must be used + --> $DIR/must_use-adt.rs:55:7 + | +LL | T(S); + | ^ + +error: unused `S` in field `0` that must be used + --> $DIR/must_use-adt.rs:56:21 + | +LL | U { x: (), y: T(S) }; + | ^ + +error: unused `S` in field `a` that must be used + --> $DIR/must_use-adt.rs:57:5 + | +LL | get_v(); + | ^^^^^^^^ + +error: unused `S` in field `a` that must be used + --> $DIR/must_use-adt.rs:58:12 + | +LL | V { a: S }; + | ^ + +error: unused array of boxed `A` trait objects in tuple element 1 that must be used + --> $DIR/must_use-adt.rs:60:12 + | +LL | w: [(0, Box::new(B)), (0, Box::new(B))], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unused `S` in tuple element 0 that must be used + --> $DIR/must_use-adt.rs:64:13 + | +LL | z: (S, S), + | ^ + +error: unused `S` in tuple element 1 that must be used + --> $DIR/must_use-adt.rs:64:16 + | +LL | z: (S, S), + | ^ + +error: unused array of boxed `A` trait objects in tuple element 1 that must be used + --> $DIR/must_use-adt.rs:67:12 + | +LL | e: [(0, Box::new(B)), (0, Box::new(B))], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unused `S` in field `f` that must be used + --> $DIR/must_use-adt.rs:69:12 + | +LL | f: S, + | ^ + +error: unused array of boxed `A` trait objects in tuple element 1 that must be used + --> $DIR/must_use-adt.rs:71:5 + | +LL | get_wrapped_arr(); + | ^^^^^^^^^^^^^^^^^^ + +error: unused array of boxed `A` trait objects in tuple element 1 that must be used + --> $DIR/must_use-adt.rs:73:5 + | +LL | get_tuple_arr(); + | ^^^^^^^^^^^^^^^^ + +error: unused `S` in field `r` that must be used + --> $DIR/must_use-adt.rs:75:12 + | +LL | R { r: S }; + | ^ + +error: unused `S` in field `0` that must be used + --> $DIR/must_use-adt.rs:76:10 + | +LL | List(S, Some(Box::new(List(S, None)))); + | ^ + +error: aborting due to 14 previous errors +