Rollup merge of #74869 - tmiasko:must-use-closures, r=ecstatic-morse

Make closures and generators a must use types

Warn about unused expressions with closure or generator type. This follows
existing precedence of must use annotations present on `FnOnce`, `FnMut`, `Fn`
traits, which already indirectly apply to closures in some cases, e.g.,:

```rust
fn f() -> impl FnOnce() {
    || {}
}

fn main() {
    // an existing warning: unused implementer of `std::ops::FnOnce` that must be used:
    f();

    // a new warning: unused closure that must be used:
    || {};
}
```

Closes #74691.
This commit is contained in:
Manish Goregaokar 2020-07-29 16:38:24 -07:00 committed by GitHub
commit 4230f96bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 355 additions and 31 deletions

View File

@ -203,6 +203,28 @@ fn check_must_use_ty<'tcx>(
// Otherwise, we don't lint, to avoid false positives. // Otherwise, we don't lint, to avoid false positives.
_ => false, _ => false,
}, },
ty::Closure(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let mut err = lint.build(&format!(
"unused {}closure{}{} that must be used",
descr_pre, plural_suffix, descr_post,
));
err.note("closures are lazy and do nothing unless called");
err.emit();
});
true
}
ty::Generator(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let mut err = lint.build(&format!(
"unused {}generator{}{} that must be used",
descr_pre, plural_suffix, descr_post,
));
err.note("generators are lazy and do nothing unless resumed");
err.emit();
});
true
}
_ => false, _ => false,
} }
} }

View File

@ -14,14 +14,14 @@ fn test(&self, a: ()) {}
fn main() { fn main() {
// Test that the MIR local with type &A created for the auto-borrow adjustment // Test that the MIR local with type &A created for the auto-borrow adjustment
// is caught by typeck // is caught by typeck
move || { move || { //~ WARN unused generator that must be used
A.test(yield); A.test(yield);
}; };
// Test that the std::cell::Ref temporary returned from the `borrow` call // Test that the std::cell::Ref temporary returned from the `borrow` call
// is caught by typeck // is caught by typeck
let y = RefCell::new(true); let y = RefCell::new(true);
static move || { static move || { //~ WARN unused generator that must be used
yield *y.borrow(); yield *y.borrow();
return "Done"; return "Done";
}; };

View File

@ -0,0 +1,24 @@
warning: unused generator that must be used
--> $DIR/issue-52398.rs:17:5
|
LL | / move || {
LL | | A.test(yield);
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: unused generator that must be used
--> $DIR/issue-52398.rs:24:5
|
LL | / static move || {
LL | | yield *y.borrow();
LL | | return "Done";
LL | | };
| |______^
|
= note: generators are lazy and do nothing unless resumed
warning: 2 warnings emitted

View File

@ -19,7 +19,7 @@ fn with<F>(f: F) -> impl Generator<Yield=(), Return=()>
fn main() { fn main() {
let data = &vec![1]; let data = &vec![1];
|| { || { //~ WARN unused generator that must be used
let _to_pin = with(move || println!("{:p}", data)); let _to_pin = with(move || println!("{:p}", data));
loop { loop {
yield yield

View File

@ -0,0 +1,16 @@
warning: unused generator that must be used
--> $DIR/issue-57084.rs:22:5
|
LL | / || {
LL | | let _to_pin = with(move || println!("{:p}", data));
LL | | loop {
LL | | yield
LL | | }
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -9,7 +9,7 @@ enum Enum {
} }
fn main() { fn main() {
|| { || { //~ WARN unused generator that must be used
loop { loop {
if let true = true { if let true = true {
match Enum::A(String::new()) { match Enum::A(String::new()) {

View File

@ -0,0 +1,17 @@
warning: unused generator that must be used
--> $DIR/match-bindings.rs:12:5
|
LL | / || {
LL | | loop {
LL | | if let true = true {
LL | | match Enum::A(String::new()) {
... |
LL | | }
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -3,7 +3,7 @@
#![feature(generators)] #![feature(generators)]
fn _run(bar: &mut i32) { fn _run(bar: &mut i32) {
|| { || { //~ WARN unused generator that must be used
{ {
let _baz = &*bar; let _baz = &*bar;
yield; yield;

View File

@ -0,0 +1,17 @@
warning: unused generator that must be used
--> $DIR/reborrow-mut-upvar.rs:6:5
|
LL | / || {
LL | | {
LL | | let _baz = &*bar;
LL | | yield;
... |
LL | | *bar = 2;
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -5,7 +5,7 @@
fn main() { fn main() {
unsafe { unsafe {
static move || { static move || { //~ WARN unused generator that must be used
// Tests that the generator transformation finds out that `a` is not live // Tests that the generator transformation finds out that `a` is not live
// during the yield expression. Type checking will also compute liveness // during the yield expression. Type checking will also compute liveness
// and it should also find out that `a` is not live. // and it should also find out that `a` is not live.

View File

@ -0,0 +1,17 @@
warning: unused generator that must be used
--> $DIR/too-live-local-in-immovable-gen.rs:8:9
|
LL | / static move || {
LL | | // Tests that the generator transformation finds out that `a` is not live
LL | | // during the yield expression. Type checking will also compute liveness
LL | | // and it should also find out that `a` is not live.
... |
LL | | &a;
LL | | };
| |__________^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -10,7 +10,7 @@
fn foo(_a: (), _b: &bool) {} fn foo(_a: (), _b: &bool) {}
fn bar() { fn bar() {
|| { || { //~ WARN unused generator that must be used
let b = true; let b = true;
foo(yield, &b); foo(yield, &b);
}; };

View File

@ -0,0 +1,14 @@
warning: unused generator that must be used
--> $DIR/yield-in-args-rev.rs:13:5
|
LL | / || {
LL | | let b = true;
LL | | foo(yield, &b);
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -6,7 +6,7 @@
fn main() { fn main() {
let x = 0i32; let x = 0i32;
|| { || { //~ WARN unused generator that must be used
let y = 2u32; let y = 2u32;
{ {
let _t = box (&x, yield 0, &y); let _t = box (&x, yield 0, &y);

View File

@ -0,0 +1,17 @@
warning: unused generator that must be used
--> $DIR/yield-in-box.rs:9:5
|
LL | / || {
LL | | let y = 2u32;
LL | | {
LL | | let _t = box (&x, yield 0, &y);
... |
LL | | }
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -3,7 +3,7 @@
#![feature(generators)] #![feature(generators)]
fn main() { fn main() {
static || { static || { //~ WARN unused generator that must be used
loop { loop {
// Test that `opt` is not live across the yield, even when borrowed in a loop // Test that `opt` is not live across the yield, even when borrowed in a loop
// See https://github.com/rust-lang/rust/issues/52792 // See https://github.com/rust-lang/rust/issues/52792

View File

@ -0,0 +1,17 @@
warning: unused generator that must be used
--> $DIR/yield-in-initializer.rs:6:5
|
LL | / static || {
LL | | loop {
LL | | // Test that `opt` is not live across the yield, even when borrowed in a loop
LL | | // See https://github.com/rust-lang/rust/issues/52792
... |
LL | | }
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -8,7 +8,7 @@ fn bar<'a>() {
let a: &'static str = "hi"; let a: &'static str = "hi";
let b: &'a str = a; let b: &'a str = a;
|| { || { //~ WARN unused generator that must be used
yield a; yield a;
yield b; yield b;
}; };

View File

@ -0,0 +1,14 @@
warning: unused generator that must be used
--> $DIR/yield-subtype.rs:11:5
|
LL | / || {
LL | | yield a;
LL | | yield b;
LL | | };
| |______^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -3,5 +3,5 @@
// pretty-expanded FIXME #23616 // pretty-expanded FIXME #23616
pub fn main() { pub fn main() {
{|i: u32| if 1 == i { }}; {|i: u32| if 1 == i { }}; //~ WARN unused closure that must be used
} }

View File

@ -0,0 +1,11 @@
warning: unused closure that must be used
--> $DIR/issue-1460.rs:6:5
|
LL | {|i: u32| if 1 == i { }};
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: closures are lazy and do nothing unless called
warning: 1 warning emitted

View File

@ -3,5 +3,5 @@
fn main() { fn main() {
let mut buf = Vec::new(); let mut buf = Vec::new();
|c: u8| buf.push(c); |c: u8| buf.push(c); //~ WARN unused closure that must be used
} }

View File

@ -0,0 +1,11 @@
warning: unused closure that must be used
--> $DIR/issue-16256.rs:6:5
|
LL | |c: u8| buf.push(c);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: closures are lazy and do nothing unless called
warning: 1 warning emitted

View File

@ -27,7 +27,7 @@ pub fn f() {
let mut c = 0; let mut c = 0;
// Captured by value, but variable is dead on entry. // Captured by value, but variable is dead on entry.
move || { let _ = move || {
c = 1; //~ WARN value captured by `c` is never read c = 1; //~ WARN value captured by `c` is never read
println!("{}", c); println!("{}", c);
}; };
@ -37,7 +37,7 @@ pub fn f() {
}; };
// Read and written to, but never actually used. // Read and written to, but never actually used.
move || { let _ = move || {
c += 1; //~ WARN unused variable: `c` c += 1; //~ WARN unused variable: `c`
}; };
let _ = async move { let _ = async move {
@ -45,13 +45,13 @@ pub fn f() {
//~| WARN unused variable: `c` //~| WARN unused variable: `c`
}; };
move || { let _ = move || {
println!("{}", c); println!("{}", c);
// Value is read by closure itself on later invocations. // Value is read by closure itself on later invocations.
c += 1; c += 1;
}; };
let b = Box::new(42); let b = Box::new(42);
move || { let _ = move || {
println!("{}", c); println!("{}", c);
// Never read because this is FnOnce closure. // Never read because this is FnOnce closure.
c += 1; //~ WARN value assigned to `c` is never read c += 1; //~ WARN value assigned to `c` is never read
@ -67,12 +67,12 @@ pub fn f() {
pub fn nested() { pub fn nested() {
let mut d = None; let mut d = None;
let mut e = None; let mut e = None;
|| { let _ = || {
|| { let _ = || {
d = Some("d1"); //~ WARN value assigned to `d` is never read d = Some("d1"); //~ WARN value assigned to `d` is never read
d = Some("d2"); d = Some("d2");
}; };
move || { let _ = move || {
e = Some("e1"); //~ WARN value assigned to `e` is never read e = Some("e1"); //~ WARN value assigned to `e` is never read
//~| WARN unused variable: `e` //~| WARN unused variable: `e`
e = Some("e2"); //~ WARN value assigned to `e` is never read e = Some("e2"); //~ WARN value assigned to `e` is never read
@ -81,7 +81,7 @@ pub fn nested() {
} }
pub fn g<T: Default>(mut v: T) { pub fn g<T: Default>(mut v: T) {
|r| { let _ = |r| {
if r { if r {
v = T::default(); //~ WARN value assigned to `v` is never read v = T::default(); //~ WARN value assigned to `v` is never read
} else { } else {
@ -92,7 +92,7 @@ pub fn g<T: Default>(mut v: T) {
pub fn h<T: Copy + Default + std::fmt::Debug>() { pub fn h<T: Copy + Default + std::fmt::Debug>() {
let mut z = T::default(); let mut z = T::default();
move |b| { let _ = move |b| {
loop { loop {
if b { if b {
z = T::default(); //~ WARN value assigned to `z` is never read z = T::default(); //~ WARN value assigned to `z` is never read

View File

@ -8,7 +8,7 @@
pub fn mutable_upvar() { pub fn mutable_upvar() {
let x = &mut 0; let x = &mut 0;
//~^ ERROR //~^ ERROR
move || { let _ = move || {
*x = 1; *x = 1;
}; };
} }

View File

@ -8,7 +8,7 @@
pub fn mutable_upvar() { pub fn mutable_upvar() {
let mut x = &mut 0; let mut x = &mut 0;
//~^ ERROR //~^ ERROR
move || { let _ = move || {
*x = 1; *x = 1;
}; };
} }

View File

@ -12,7 +12,7 @@ fn drop(&mut self) {}
fn reborrow_from_generator(r: &mut ()) { fn reborrow_from_generator(r: &mut ()) {
let d = WithDrop; let d = WithDrop;
move || { d; yield; &mut *r }; move || { d; yield; &mut *r }; //~ WARN unused generator that must be used
} }
fn main() {} fn main() {}

View File

@ -0,0 +1,11 @@
warning: unused generator that must be used
--> $DIR/issue-48623-generator.rs:15:5
|
LL | move || { d; yield; &mut *r };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: generators are lazy and do nothing unless resumed
warning: 1 warning emitted

View File

@ -7,9 +7,9 @@
mod a { mod a {
fn b() { fn b() {
|| { (|| {
#[main] #[main]
fn c() { panic!(); } fn c() { panic!(); }
}; })();
} }
} }

View File

@ -2,6 +2,7 @@
// pretty-expanded FIXME #23616 // pretty-expanded FIXME #23616
#![deny(unused_mut)] #![deny(unused_mut)]
#![allow(unused_must_use)]
// Test that mutating a mutable upvar in a capture-by-value unboxed // Test that mutating a mutable upvar in a capture-by-value unboxed
// closure does not ice (issue #18238) and marks the upvar as used // closure does not ice (issue #18238) and marks the upvar as used

View File

@ -1,5 +1,5 @@
warning: unused variable: `x` warning: unused variable: `x`
--> $DIR/unboxed-closures-move-mutable.rs:16:17 --> $DIR/unboxed-closures-move-mutable.rs:17:17
| |
LL | move || x += 1; LL | move || x += 1;
| ^ | ^
@ -8,7 +8,7 @@ LL | move || x += 1;
= help: did you mean to capture by reference instead? = help: did you mean to capture by reference instead?
warning: unused variable: `x` warning: unused variable: `x`
--> $DIR/unboxed-closures-move-mutable.rs:20:17 --> $DIR/unboxed-closures-move-mutable.rs:21:17
| |
LL | move || x += 1; LL | move || x += 1;
| ^ | ^

View File

@ -0,0 +1,40 @@
// Test that closures and generators are "must use" types.
// edition:2018
#![feature(async_closure)]
#![feature(const_in_array_repeat_expressions)]
#![feature(generators)]
#![deny(unused_must_use)]
fn unused() {
|| { //~ ERROR unused closure that must be used
println!("Hello!");
};
async {}; //~ ERROR unused implementer of `std::future::Future` that must be used
|| async {}; //~ ERROR unused closure that must be used
async || {}; //~ ERROR unused closure that must be used
[Box::new([|| {}; 10]); 1]; //~ ERROR unused array of boxed arrays of closures that must be used
[|| { //~ ERROR unused array of generators that must be used
yield 42u32;
}; 42];
vec![|| "a"].pop().unwrap(); //~ ERROR unused closure that must be used
let b = false;
|| true; //~ ERROR unused closure that must be used
println!("{}", b);
}
fn ignored() {
let _ = || {};
let _ = || yield 42;
}
fn main() {
unused();
ignored();
}

View File

@ -0,0 +1,75 @@
error: unused closure that must be used
--> $DIR/unused-closure.rs:10:5
|
LL | / || {
LL | | println!("Hello!");
LL | | };
| |______^
|
note: the lint level is defined here
--> $DIR/unused-closure.rs:7:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
= note: closures are lazy and do nothing unless called
error: unused implementer of `std::future::Future` that must be used
--> $DIR/unused-closure.rs:14:5
|
LL | async {};
| ^^^^^^^^^
|
= note: futures do nothing unless you `.await` or poll them
error: unused closure that must be used
--> $DIR/unused-closure.rs:15:5
|
LL | || async {};
| ^^^^^^^^^^^^
|
= note: closures are lazy and do nothing unless called
error: unused closure that must be used
--> $DIR/unused-closure.rs:16:5
|
LL | async || {};
| ^^^^^^^^^^^^
|
= note: closures are lazy and do nothing unless called
error: unused array of boxed arrays of closures that must be used
--> $DIR/unused-closure.rs:19:5
|
LL | [Box::new([|| {}; 10]); 1];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: closures are lazy and do nothing unless called
error: unused array of generators that must be used
--> $DIR/unused-closure.rs:21:5
|
LL | / [|| {
LL | | yield 42u32;
LL | | }; 42];
| |___________^
|
= note: generators are lazy and do nothing unless resumed
error: unused closure that must be used
--> $DIR/unused-closure.rs:25:5
|
LL | vec![|| "a"].pop().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: closures are lazy and do nothing unless called
error: unused closure that must be used
--> $DIR/unused-closure.rs:28:9
|
LL | || true;
| ^^^^^^^^
|
= note: closures are lazy and do nothing unless called
error: aborting due to 8 previous errors

View File

@ -5,5 +5,5 @@
fn main() { fn main() {
let x = 1; let x = 1;
//~^ ERROR: variable does not need to be mutable //~^ ERROR: variable does not need to be mutable
move|| { println!("{}", x); }; (move|| { println!("{}", x); })();
} }

View File

@ -5,5 +5,5 @@
fn main() { fn main() {
let mut x = 1; let mut x = 1;
//~^ ERROR: variable does not need to be mutable //~^ ERROR: variable does not need to be mutable
move|| { println!("{}", x); }; (move|| { println!("{}", x); })();
} }

View File

@ -5,7 +5,7 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unreachable_code)] #![allow(unreachable_code)]
#![allow(unused_braces, unused_parens)] #![allow(unused_braces, unused_must_use, unused_parens)]
#![recursion_limit = "256"] #![recursion_limit = "256"]