diff options
Diffstat (limited to '')
-rw-r--r-- | src/tools/clippy/tests/ui/manual_let_else.rs | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs new file mode 100644 index 000000000..48a162c13 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -0,0 +1,251 @@ +#![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 +)] +#![warn(clippy::manual_let_else)] + +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 for the identity block and pattern + let v = if let (Some(v_some), w_some) = (g(), 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 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!(), + }; +} |