2022-11-21 13:34:47 -06:00
|
|
|
#![allow(unused_braces, unused_variables, dead_code)]
|
|
|
|
#![allow(
|
|
|
|
clippy::collapsible_else_if,
|
|
|
|
clippy::unused_unit,
|
|
|
|
clippy::let_unit_value,
|
|
|
|
clippy::match_single_binding,
|
2023-07-02 07:35:19 -05:00
|
|
|
clippy::never_loop,
|
|
|
|
clippy::needless_if
|
2022-11-21 13:34:47 -06:00
|
|
|
)]
|
|
|
|
#![warn(clippy::manual_let_else)]
|
|
|
|
|
2023-05-20 08:39:26 -05:00
|
|
|
enum Variant {
|
|
|
|
A(usize, usize),
|
|
|
|
B(usize),
|
|
|
|
C,
|
|
|
|
}
|
|
|
|
|
2022-11-21 13:34:47 -06:00
|
|
|
fn g() -> Option<()> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {}
|
|
|
|
|
|
|
|
fn fire() {
|
|
|
|
let v = if let Some(v_some) = g() { v_some } else { return };
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
let v = if let Some(v) = g() {
|
|
|
|
// 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 };
|
|
|
|
let v = if let Some(v_some) = g() { v_some } else { break };
|
|
|
|
}
|
|
|
|
|
|
|
|
// panic also diverges
|
|
|
|
let v = if let Some(v_some) = g() { v_some } else { panic!() };
|
|
|
|
|
|
|
|
// abort also diverges
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
std::process::abort()
|
|
|
|
};
|
|
|
|
|
|
|
|
// If whose two branches diverge also diverges
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
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() {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
if true {}
|
|
|
|
panic!();
|
|
|
|
};
|
|
|
|
|
|
|
|
// A match diverges if all branches diverge:
|
|
|
|
// Note: the corresponding let-else requires a ; at the end of the match
|
|
|
|
// as otherwise the type checker does not turn it into a ! type.
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
match () {
|
|
|
|
_ if panic!() => {},
|
|
|
|
_ => panic!(),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// An if's expression can cause divergence:
|
|
|
|
let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
|
|
|
|
|
|
|
|
// An expression of a match can cause divergence:
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
match panic!() {
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Top level else if
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
v_some
|
|
|
|
} else if true {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
panic!("diverge");
|
|
|
|
};
|
|
|
|
|
|
|
|
// All match arms diverge
|
|
|
|
let v = if let Some(v_some) = g() {
|
|
|
|
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)) {
|
|
|
|
v_some
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
2023-07-02 07:35:19 -05:00
|
|
|
// Tuples supported with multiple bindings
|
|
|
|
let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
|
2022-11-21 13:34:47 -06:00
|
|
|
(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());
|
2023-05-20 08:39:26 -05:00
|
|
|
|
|
|
|
fn e() -> Variant {
|
|
|
|
Variant::A(0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
let v = if let Variant::A(a, 0) = e() { a } else { return };
|
2023-06-02 04:41:57 -05:00
|
|
|
|
|
|
|
// `mut v` is inserted into the pattern
|
|
|
|
let mut v = if let Variant::B(b) = e() { b } else { return };
|
|
|
|
|
|
|
|
// Nesting works
|
|
|
|
let nested = Ok(Some(e()));
|
|
|
|
let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
|
|
|
|
b
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
// dot dot works
|
|
|
|
let v = if let Variant::A(.., a) = e() { a } else { return };
|
2023-07-02 07:35:19 -05:00
|
|
|
|
|
|
|
// () is preserved: a bit of an edge case but make sure it stays around
|
|
|
|
let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
|
|
|
|
|
|
|
|
// Tuple structs work
|
|
|
|
let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
|
|
|
x
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Field init shorthand is suggested
|
|
|
|
let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
|
|
|
x
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Multi-field structs also work
|
|
|
|
let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
|
|
|
|
(x, v, w)
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2022-11-21 13:34:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2023-07-14 06:27:56 -05:00
|
|
|
let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else {
|
|
|
|
panic!()
|
|
|
|
};
|
2022-11-21 13:34:47 -06:00
|
|
|
|
|
|
|
// 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!() };
|
2022-12-01 11:29:38 -06:00
|
|
|
|
|
|
|
// Issue 9940
|
|
|
|
// Suggestion should not expand macros
|
|
|
|
macro_rules! macro_call {
|
|
|
|
() => {
|
|
|
|
return ()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let ff = Some(1);
|
|
|
|
let _ = match ff {
|
|
|
|
Some(value) => value,
|
|
|
|
_ => macro_call!(),
|
|
|
|
};
|
2023-02-25 18:08:29 -06:00
|
|
|
|
|
|
|
// 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 {
|
2023-07-14 06:27:56 -05:00
|
|
|
let Some(_z) = Some(3) else { return };
|
2023-02-25 18:08:29 -06:00
|
|
|
1
|
|
|
|
};
|
2023-07-02 07:35:19 -05:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
struct S<T> {
|
|
|
|
v: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct U<T> {
|
|
|
|
v: T,
|
|
|
|
w: T,
|
|
|
|
x: T,
|
2022-11-21 13:34:47 -06:00
|
|
|
}
|