blob: c6476a7507a1db10484ff6d686c58650e7e99018 [file] [log] [blame] [edit]
//@no-rustfix: has placeholders
#![warn(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
#![expect(
clippy::if_same_then_else,
clippy::branches_sharing_code,
clippy::unnecessary_literal_unwrap,
clippy::self_assignment
)]
macro_rules! m {
($a:expr) => {
if $a.is_some() {
$a.unwrap();
//~^ unnecessary_unwrap
}
};
}
macro_rules! checks_in_param {
($a:expr, $b:expr) => {
if $a {
$b;
}
};
}
macro_rules! checks_unwrap {
($a:expr, $b:expr) => {
if $a.is_some() {
$b;
}
};
}
macro_rules! checks_some {
($a:expr, $b:expr) => {
if $a {
$b.unwrap();
}
};
}
fn main() {
let x = Some(());
if x.is_some() {
x.unwrap();
//~^ unnecessary_unwrap
x.expect("an error message");
//~^ unnecessary_unwrap
} else {
x.unwrap();
//~^ panicking_unwrap
x.expect("an error message");
//~^ panicking_unwrap
}
if x.is_none() {
x.unwrap();
//~^ panicking_unwrap
} else {
x.unwrap();
//~^ unnecessary_unwrap
}
m!(x);
checks_in_param!(x.is_some(), x.unwrap());
checks_unwrap!(x, x.unwrap());
checks_some!(x.is_some(), x);
let mut x: Result<(), ()> = Ok(());
if x.is_ok() {
x.unwrap();
//~^ unnecessary_unwrap
x.expect("an error message");
//~^ unnecessary_unwrap
x.unwrap_err();
//~^ panicking_unwrap
} else {
x.unwrap();
//~^ panicking_unwrap
x.expect("an error message");
//~^ panicking_unwrap
x.unwrap_err();
//~^ unnecessary_unwrap
}
if x.is_err() {
x.unwrap();
//~^ panicking_unwrap
x.unwrap_err();
//~^ unnecessary_unwrap
} else {
x.unwrap();
//~^ unnecessary_unwrap
x.unwrap_err();
//~^ panicking_unwrap
}
if x.is_ok() {
x = Err(());
// not unnecessary because of mutation of `x`
// it will always panic but the lint is not smart enough to see this (it only
// checks if conditions).
x.unwrap();
} else {
x = Ok(());
// not unnecessary because of mutation of `x`
// it will always panic but the lint is not smart enough to see this (it
// only checks if conditions).
x.unwrap_err();
}
// ok, it's a common test pattern
assert!(x.is_ok(), "{:?}", x.unwrap_err());
}
fn issue11371() {
let option = Some(());
if option.is_some() {
option.as_ref().unwrap();
//~^ unnecessary_unwrap
} else {
option.as_ref().unwrap();
//~^ panicking_unwrap
}
let result = Ok::<(), ()>(());
if result.is_ok() {
result.as_ref().unwrap();
//~^ unnecessary_unwrap
} else {
result.as_ref().unwrap();
//~^ panicking_unwrap
}
let mut option = Some(());
if option.is_some() {
option.as_mut().unwrap();
//~^ unnecessary_unwrap
} else {
option.as_mut().unwrap();
//~^ panicking_unwrap
}
let mut result = Ok::<(), ()>(());
if result.is_ok() {
result.as_mut().unwrap();
//~^ unnecessary_unwrap
} else {
result.as_mut().unwrap();
//~^ panicking_unwrap
}
// This should not lint and suggest `if let Some(..) = X {}`, as `X` is being mutated
static mut X: Option<i32> = Some(123);
unsafe {
#[expect(static_mut_refs)]
if X.is_some() {
X = None;
X.unwrap();
}
}
}
fn gen_option() -> Option<()> {
Some(())
// Or None
}
fn gen_result() -> Result<(), ()> {
Ok(())
// Or Err(())
}
fn issue14725() {
let option = Some(());
if option.is_some() {
let _ = option.as_ref().unwrap();
//~^ unnecessary_unwrap
} else {
let _ = option.as_ref().unwrap();
//~^ panicking_unwrap
}
let result = Ok::<(), ()>(());
if result.is_ok() {
let _y = 1;
result.as_ref().unwrap();
//~^ unnecessary_unwrap
} else {
let _y = 1;
result.as_ref().unwrap();
//~^ panicking_unwrap
}
let mut option = Some(());
if option.is_some() {
option = gen_option();
option.as_mut().unwrap();
} else {
option = gen_option();
option.as_mut().unwrap();
}
let mut result = Ok::<(), ()>(());
if result.is_ok() {
result = gen_result();
result.as_mut().unwrap();
} else {
result = gen_result();
result.as_mut().unwrap();
}
}
fn issue14763(x: Option<String>, r: Result<(), ()>) {
_ = || {
if x.is_some() {
_ = x.unwrap();
//~^ unnecessary_unwrap
} else {
_ = x.unwrap();
//~^ panicking_unwrap
}
};
_ = || {
if r.is_ok() {
_ = r.as_ref().unwrap();
//~^ unnecessary_unwrap
} else {
_ = r.as_ref().unwrap();
//~^ panicking_unwrap
}
};
}
const ISSUE14763: fn(Option<String>) = |x| {
_ = || {
if x.is_some() {
_ = x.unwrap();
//~^ unnecessary_unwrap
} else {
_ = x.unwrap();
//~^ panicking_unwrap
}
}
};
fn issue12295() {
let option = Some(());
if option.is_some() {
println!("{:?}", option.unwrap());
//~^ unnecessary_unwrap
} else {
println!("{:?}", option.unwrap());
//~^ panicking_unwrap
}
let result = Ok::<(), ()>(());
if result.is_ok() {
println!("{:?}", result.unwrap());
//~^ unnecessary_unwrap
} else {
println!("{:?}", result.unwrap());
//~^ panicking_unwrap
}
}
fn check_expect() {
let x = Some(());
if x.is_some() {
#[expect(clippy::unnecessary_unwrap)]
x.unwrap();
#[expect(clippy::unnecessary_unwrap)]
x.expect("an error message");
} else {
#[expect(clippy::panicking_unwrap)]
x.unwrap();
#[expect(clippy::panicking_unwrap)]
x.expect("an error message");
}
}
fn partial_moves() {
fn borrow_option(_: &Option<()>) {}
let x = Some(());
// Using `if let Some(o) = x` won't work here, as `borrow_option` will try to borrow a moved value
if x.is_some() {
borrow_option(&x);
x.unwrap();
//~^ unnecessary_unwrap
}
// This is fine though, as `if let Some(o) = &x` won't move `x`
if x.is_some() {
borrow_option(&x);
x.as_ref().unwrap();
//~^ unnecessary_unwrap
}
}
fn issue15321() {
struct Soption {
option: Option<bool>,
other: bool,
}
let mut sopt = Soption {
option: Some(true),
other: true,
};
// Lint: nothing was mutated
let _res = if sopt.option.is_some() {
sopt.option.unwrap()
//~^ unnecessary_unwrap
} else {
sopt.option.unwrap()
//~^ panicking_unwrap
};
// Lint: an unrelated field was mutated
let _res = if sopt.option.is_some() {
sopt.other = false;
sopt.option.unwrap()
//~^ unnecessary_unwrap
} else {
sopt.other = false;
sopt.option.unwrap()
//~^ panicking_unwrap
};
// No lint: the whole local was mutated
let _res = if sopt.option.is_some() {
sopt = sopt;
sopt.option.unwrap()
} else {
sopt.option = None;
sopt.option.unwrap()
};
// No lint: the field we're looking at was mutated
let _res = if sopt.option.is_some() {
sopt = sopt;
sopt.option.unwrap()
} else {
sopt.option = None;
sopt.option.unwrap()
};
struct Toption(Option<bool>, bool);
let mut topt = Toption(Some(true), true);
// Lint: nothing was mutated
let _res = if topt.0.is_some() {
topt.0.unwrap()
//~^ unnecessary_unwrap
} else {
topt.0.unwrap()
//~^ panicking_unwrap
};
// Lint: an unrelated field was mutated
let _res = if topt.0.is_some() {
topt.1 = false;
topt.0.unwrap()
//~^ unnecessary_unwrap
} else {
topt.1 = false;
topt.0.unwrap()
//~^ panicking_unwrap
};
// No lint: the whole local was mutated
let _res = if topt.0.is_some() {
topt = topt;
topt.0.unwrap()
} else {
topt = topt;
topt.0.unwrap()
};
// No lint: the field we're looking at was mutated
let _res = if topt.0.is_some() {
topt.0 = None;
topt.0.unwrap()
} else {
topt.0 = None;
topt.0.unwrap()
};
// Nested field accesses get linted as well
struct Soption2 {
other: bool,
option: Soption,
}
let mut sopt2 = Soption2 {
other: true,
option: Soption {
option: Some(true),
other: true,
},
};
// Lint: no fields were mutated
let _res = if sopt2.option.option.is_some() {
sopt2.option.option.unwrap()
//~^ unnecessary_unwrap
} else {
sopt2.option.option.unwrap()
//~^ panicking_unwrap
};
// Lint: an unrelated outer field was mutated -- don't get confused by `Soption2.other` having the
// same `FieldIdx` of 1 as `Soption.option`
let _res = if sopt2.option.option.is_some() {
sopt2.other = false;
sopt2.option.option.unwrap()
//~^ unnecessary_unwrap
} else {
sopt2.other = false;
sopt2.option.option.unwrap()
//~^ panicking_unwrap
};
// Lint: an unrelated inner field was mutated
let _res = if sopt2.option.option.is_some() {
sopt2.option.other = false;
sopt2.option.option.unwrap()
//~^ unnecessary_unwrap
} else {
sopt2.option.other = false;
sopt2.option.option.unwrap()
//~^ panicking_unwrap
};
// Don't lint: the whole local was mutated
let _res = if sopt2.option.option.is_some() {
sopt2 = sopt2;
sopt2.option.option.unwrap()
} else {
sopt2 = sopt2;
sopt2.option.option.unwrap()
};
// Don't lint: a parent field of the field we're looking at was mutated, and with that the
// field we're looking at
let _res = if sopt2.option.option.is_some() {
sopt2.option = sopt;
sopt2.option.option.unwrap()
} else {
sopt2.option = sopt;
sopt2.option.option.unwrap()
};
// Don't lint: the field we're looking at was mutated directly
let _res = if sopt2.option.option.is_some() {
sopt2.option.option = None;
sopt2.option.option.unwrap()
} else {
sopt2.option.option = None;
sopt2.option.option.unwrap()
};
// Partial moves
fn borrow_toption(_: &Toption) {}
// Using `if let Some(o) = topt.0` won't work here, as `borrow_toption` will try to borrow a
// partially moved value
if topt.0.is_some() {
borrow_toption(&topt);
topt.0.unwrap();
//~^ unnecessary_unwrap
}
// This is fine though, as `if let Some(o) = &topt.0` won't (partially) move `topt`
if topt.0.is_some() {
borrow_toption(&topt);
topt.0.as_ref().unwrap();
//~^ unnecessary_unwrap
}
}