blob: 381b83409e9d2c1b837d25eafd63ba41a5cc3385 [file] [log] [blame]
#![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
)]
#![warn(clippy::manual_let_else)]
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 };
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;
};
// Tuples supported with multiple bindings
let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
(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 };
// `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 };
// () 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;
};
}
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 {
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;
};
}
struct S<T> {
v: T,
}
struct U<T> {
v: T,
w: T,
x: T,
}