mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-14 17:07:17 +00:00
458 lines
11 KiB
Rust
458 lines
11 KiB
Rust
#![feature(try_blocks)]
|
|
#![allow(unused_braces, unused_variables, dead_code)]
|
|
#![allow(
|
|
clippy::collapsible_else_if,
|
|
clippy::unused_unit,
|
|
clippy::let_unit_value,
|
|
clippy::match_single_binding,
|
|
clippy::never_loop,
|
|
clippy::needless_if,
|
|
clippy::diverging_sub_expression,
|
|
clippy::single_match
|
|
)]
|
|
#![warn(clippy::manual_let_else)]
|
|
//@no-rustfix
|
|
enum Variant {
|
|
A(usize, usize),
|
|
B(usize),
|
|
C,
|
|
}
|
|
|
|
fn g() -> Option<()> {
|
|
None
|
|
}
|
|
|
|
fn main() {}
|
|
|
|
fn fire() {
|
|
let v = if let Some(v_some) = g() { v_some } else { return };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let v = if let Some(v) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
// Blocks around the identity should have no impact
|
|
{ { v } }
|
|
} else {
|
|
// Some computation should still make it fire
|
|
g();
|
|
return;
|
|
};
|
|
|
|
// continue and break diverge
|
|
loop {
|
|
let v = if let Some(v_some) = g() { v_some } else { continue };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
let v = if let Some(v_some) = g() { v_some } else { break };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
}
|
|
|
|
// panic also diverges
|
|
let v = if let Some(v_some) = g() { v_some } else { panic!() };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
// abort also diverges
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
std::process::abort()
|
|
};
|
|
|
|
// If whose two branches diverge also diverges
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
if true { return } else { panic!() }
|
|
};
|
|
|
|
// Diverging after an if still makes the block diverge:
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
if true {}
|
|
panic!();
|
|
};
|
|
|
|
// The final expression will need to be turned into a statement.
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
panic!();
|
|
()
|
|
};
|
|
|
|
// Even if the result is buried multiple expressions deep.
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
panic!();
|
|
if true {
|
|
match 0 {
|
|
0 => (),
|
|
_ => (),
|
|
}
|
|
} else {
|
|
panic!()
|
|
}
|
|
};
|
|
|
|
// Or if a break gives the value.
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
loop {
|
|
panic!();
|
|
break ();
|
|
}
|
|
};
|
|
|
|
// Even if the break is in a weird position.
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
'a: loop {
|
|
panic!();
|
|
loop {
|
|
match 0 {
|
|
0 if (return break 'a ()) => {},
|
|
_ => {},
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// A match diverges if all branches diverge:
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
match 0 {
|
|
0 if true => panic!(),
|
|
_ => panic!(),
|
|
};
|
|
};
|
|
|
|
// An if's expression can cause divergence:
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
if panic!() {};
|
|
};
|
|
|
|
// An expression of a match can cause divergence:
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
match panic!() {
|
|
_ => {},
|
|
};
|
|
};
|
|
|
|
// Top level else if
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else if true {
|
|
return;
|
|
} else {
|
|
panic!("diverge");
|
|
};
|
|
|
|
// All match arms diverge
|
|
let v = if let Some(v_some) = g() {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
match (g(), g()) {
|
|
(Some(_), None) => return,
|
|
(None, Some(_)) => {
|
|
if true {
|
|
return;
|
|
} else {
|
|
panic!();
|
|
}
|
|
},
|
|
_ => return,
|
|
}
|
|
};
|
|
|
|
// Tuples supported for the declared variables
|
|
let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
v_some
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
// Tuples supported with multiple bindings
|
|
let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
(w_some, v_some)
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
// entirely inside macro lints
|
|
macro_rules! create_binding_if_some {
|
|
($n:ident, $e:expr) => {
|
|
let $n = if let Some(v) = $e { v } else { return };
|
|
};
|
|
}
|
|
create_binding_if_some!(w, g());
|
|
|
|
fn e() -> Variant {
|
|
Variant::A(0, 0)
|
|
}
|
|
|
|
let v = if let Variant::A(a, 0) = e() { a } else { return };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
// `mut v` is inserted into the pattern
|
|
let mut v = if let Variant::B(b) = e() { b } else { return };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
// Nesting works
|
|
let nested = Ok(Some(e()));
|
|
let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
b
|
|
} else {
|
|
return;
|
|
};
|
|
// dot dot works
|
|
let v = if let Variant::A(.., a) = e() { a } else { return };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
// () is preserved: a bit of an edge case but make sure it stays around
|
|
let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
|
|
// Tuple structs work
|
|
let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
x
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
// Field init shorthand is suggested
|
|
let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
x
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
// Multi-field structs also work
|
|
let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
(x, v, w)
|
|
} else {
|
|
return;
|
|
};
|
|
}
|
|
|
|
fn not_fire() {
|
|
let v = if let Some(v_some) = g() {
|
|
// Nothing returned. Should not fire.
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let w = 0;
|
|
let v = if let Some(v_some) = g() {
|
|
// Different variable than v_some. Should not fire.
|
|
w
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
// Computation in then clause. Should not fire.
|
|
g();
|
|
v_some
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
v_some
|
|
} else {
|
|
if false {
|
|
return;
|
|
}
|
|
// This doesn't diverge. Should not fire.
|
|
()
|
|
};
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
v_some
|
|
} else {
|
|
// There is one match arm that doesn't diverge. Should not fire.
|
|
match (g(), g()) {
|
|
(Some(_), None) => return,
|
|
(None, Some(_)) => return,
|
|
(Some(_), Some(_)) => (),
|
|
_ => return,
|
|
}
|
|
};
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
v_some
|
|
} else {
|
|
// loop with a break statement inside does not diverge.
|
|
loop {
|
|
break;
|
|
}
|
|
};
|
|
|
|
enum Uninhabited {}
|
|
fn un() -> Uninhabited {
|
|
panic!()
|
|
}
|
|
let v = if let Some(v_some) = None {
|
|
v_some
|
|
} else {
|
|
// Don't lint if the type is uninhabited but not !
|
|
un()
|
|
};
|
|
|
|
fn question_mark() -> Option<()> {
|
|
let v = if let Some(v) = g() {
|
|
v
|
|
} else {
|
|
// Question mark does not diverge
|
|
g()?
|
|
};
|
|
Some(v)
|
|
}
|
|
|
|
// Macro boundary inside let
|
|
macro_rules! some_or_return {
|
|
($e:expr) => {
|
|
if let Some(v) = $e { v } else { return }
|
|
};
|
|
}
|
|
let v = some_or_return!(g());
|
|
|
|
// Also macro boundary inside let, but inside a macro
|
|
macro_rules! create_binding_if_some_nf {
|
|
($n:ident, $e:expr) => {
|
|
let $n = some_or_return!($e);
|
|
};
|
|
}
|
|
create_binding_if_some_nf!(v, g());
|
|
|
|
// Already a let-else
|
|
let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else {
|
|
panic!()
|
|
};
|
|
|
|
// If a type annotation is present, don't lint as
|
|
// expressing the type might be too hard
|
|
let v: () = if let Some(v_some) = g() { v_some } else { panic!() };
|
|
|
|
// Issue 9940
|
|
// Suggestion should not expand macros
|
|
macro_rules! macro_call {
|
|
() => {
|
|
return ()
|
|
};
|
|
}
|
|
|
|
let ff = Some(1);
|
|
let _ = match ff {
|
|
//~^ ERROR: this could be rewritten as `let...else`
|
|
Some(value) => value,
|
|
_ => macro_call!(),
|
|
};
|
|
|
|
// Issue 10296
|
|
// The let/else block in the else part is not divergent despite the presence of return
|
|
let _x = if let Some(x) = Some(1) {
|
|
x
|
|
} else {
|
|
let Some(_z) = Some(3) else { return };
|
|
1
|
|
};
|
|
|
|
// This would require creation of a suggestion of the form
|
|
// let v @ (Some(_), _) = (...) else { return };
|
|
// Which is too advanced for our code, so we just bail.
|
|
let v = if let (Some(v_some), w_some) = (g(), 0) {
|
|
(w_some, v_some)
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
// A break that skips the divergent statement will cause the expression to be non-divergent.
|
|
let _x = if let Some(x) = Some(0) {
|
|
x
|
|
} else {
|
|
'foo: loop {
|
|
break 'foo 0;
|
|
panic!();
|
|
}
|
|
};
|
|
|
|
// Even in inner loops.
|
|
let _x = if let Some(x) = Some(0) {
|
|
x
|
|
} else {
|
|
'foo: {
|
|
loop {
|
|
break 'foo 0;
|
|
}
|
|
panic!();
|
|
}
|
|
};
|
|
|
|
// But a break that can't ever be reached still affects divergence checking.
|
|
let _x = if let Some(x) = g() {
|
|
x
|
|
} else {
|
|
'foo: {
|
|
'bar: loop {
|
|
loop {
|
|
break 'bar ();
|
|
}
|
|
break 'foo ();
|
|
}
|
|
panic!();
|
|
};
|
|
};
|
|
}
|
|
|
|
struct S<T> {
|
|
v: T,
|
|
}
|
|
|
|
struct U<T> {
|
|
v: T,
|
|
w: T,
|
|
x: T,
|
|
}
|
|
|
|
fn issue12337() {
|
|
// We want to generally silence question_mark lints within try blocks, since `?` has different
|
|
// behavior to `return`, and question_mark calls into manual_let_else logic, so make sure that
|
|
// we still emit a lint for manual_let_else
|
|
let _: Option<()> = try {
|
|
let v = if let Some(v_some) = g() { v_some } else { return };
|
|
};
|
|
}
|