| #![feature(try_blocks)] | |
| #![warn(clippy::question_mark)] | |
| #![allow( | |
| clippy::needless_return, | |
| clippy::no_effect, | |
| clippy::toplevel_ref_arg, | |
| clippy::unnecessary_wraps | |
| )] | |
| use std::sync::MutexGuard; | |
| fn some_func(a: Option<u32>) -> Option<u32> { | |
| a?; | |
| a | |
| } | |
| fn some_other_func(a: Option<u32>) -> Option<u32> { | |
| if a.is_none() { | |
| return None; | |
| } else { | |
| return Some(0); | |
| } | |
| unreachable!() | |
| } | |
| pub enum SeemsOption<T> { | |
| Some(T), | |
| None, | |
| } | |
| impl<T> SeemsOption<T> { | |
| pub fn is_none(&self) -> bool { | |
| match *self { | |
| SeemsOption::None => true, | |
| SeemsOption::Some(_) => false, | |
| } | |
| } | |
| } | |
| fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> { | |
| if a.is_none() { | |
| return SeemsOption::None; | |
| } | |
| a | |
| } | |
| pub struct CopyStruct { | |
| pub opt: Option<u32>, | |
| } | |
| impl CopyStruct { | |
| #[rustfmt::skip] | |
| pub fn func(&self) -> Option<u32> { | |
| (self.opt)?; | |
| self.opt?; | |
| let _ = Some(self.opt?); | |
| let _ = self.opt?; | |
| self.opt | |
| } | |
| } | |
| #[derive(Clone)] | |
| pub struct MoveStruct { | |
| pub opt: Option<Vec<u32>>, | |
| } | |
| impl MoveStruct { | |
| pub fn ref_func(&self) -> Option<Vec<u32>> { | |
| self.opt.as_ref()?; | |
| self.opt.clone() | |
| } | |
| pub fn mov_func_reuse(self) -> Option<Vec<u32>> { | |
| self.opt.as_ref()?; | |
| self.opt | |
| } | |
| pub fn mov_func_no_use(self) -> Option<Vec<u32>> { | |
| self.opt.as_ref()?; | |
| Some(Vec::new()) | |
| } | |
| pub fn if_let_ref_func(self) -> Option<Vec<u32>> { | |
| let v: &Vec<_> = self.opt.as_ref()?; | |
| Some(v.clone()) | |
| } | |
| pub fn if_let_mov_func(self) -> Option<Vec<u32>> { | |
| let v = self.opt?; | |
| Some(v) | |
| } | |
| } | |
| fn func() -> Option<i32> { | |
| macro_rules! opt_none { | |
| () => { | |
| None | |
| }; | |
| } | |
| fn f() -> Option<String> { | |
| Some(String::new()) | |
| } | |
| f()?; | |
| let _val = f()?; | |
| let s: &str = &Some(String::new())?; | |
| f()?; | |
| opt_none!()?; | |
| match f() { | |
| Some(x) => x, | |
| None => return opt_none!(), | |
| }; | |
| { | |
| let val = f()?; | |
| println!("{val}"); | |
| val | |
| }; | |
| Some(0) | |
| } | |
| fn func_returning_result() -> Result<i32, i32> { | |
| Ok(1) | |
| } | |
| fn result_func(x: Result<i32, i32>) -> Result<i32, i32> { | |
| let _ = x?; | |
| //~^ question_mark | |
| x?; | |
| let _val = func_returning_result()?; | |
| func_returning_result()?; | |
| // No warning | |
| let y = if let Ok(x) = x { | |
| x | |
| } else { | |
| return Err(0); | |
| }; | |
| // issue #7859 | |
| // no warning | |
| let _ = if let Ok(x) = func_returning_result() { | |
| x | |
| } else { | |
| return Err(0); | |
| }; | |
| // no warning | |
| if func_returning_result().is_err() { | |
| return func_returning_result(); | |
| } | |
| // no warning | |
| let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; | |
| // issue #11283 | |
| // no warning | |
| #[warn(clippy::question_mark_used)] | |
| { | |
| if let Err(err) = Ok(()) { | |
| return Err(err); | |
| } | |
| if Err::<i32, _>(0).is_err() { | |
| return Err(0); | |
| } else { | |
| return Ok(0); | |
| } | |
| unreachable!() | |
| } | |
| Ok(y) | |
| } | |
| fn infer_check() { | |
| let closure = |x: Result<u8, ()>| { | |
| // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. | |
| let _val = match x { | |
| Ok(val) => val, | |
| Err(val) => return Err(val), | |
| }; | |
| Ok(()) | |
| }; | |
| let closure = |x: Result<u8, ()>| -> Result<(), _> { | |
| // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. | |
| let _val = match x { | |
| Ok(val) => val, | |
| Err(val) => return Err(val), | |
| }; | |
| Ok(()) | |
| }; | |
| } | |
| // see issue #8019 | |
| pub enum NotOption { | |
| None, | |
| First, | |
| AfterFirst, | |
| } | |
| fn obj(_: i32) -> Result<(), NotOption> { | |
| Err(NotOption::First) | |
| } | |
| fn f() -> NotOption { | |
| if obj(2).is_err() { | |
| return NotOption::None; | |
| } | |
| NotOption::First | |
| } | |
| fn do_something() {} | |
| fn err_immediate_return() -> Result<i32, i32> { | |
| func_returning_result()?; | |
| Ok(1) | |
| } | |
| fn err_immediate_return_and_do_something() -> Result<i32, i32> { | |
| func_returning_result()?; | |
| do_something(); | |
| Ok(1) | |
| } | |
| // No warning | |
| fn no_immediate_return() -> Result<i32, i32> { | |
| if let Err(err) = func_returning_result() { | |
| do_something(); | |
| return Err(err); | |
| } | |
| Ok(1) | |
| } | |
| // No warning | |
| fn mixed_result_and_option() -> Option<i32> { | |
| if let Err(err) = func_returning_result() { | |
| return Some(err); | |
| } | |
| None | |
| } | |
| // No warning | |
| fn else_if_check() -> Result<i32, i32> { | |
| if true { | |
| Ok(1) | |
| } else if let Err(e) = func_returning_result() { | |
| Err(e) | |
| } else { | |
| Err(-1) | |
| } | |
| } | |
| // No warning | |
| #[allow(clippy::manual_map)] | |
| #[rustfmt::skip] | |
| fn option_map() -> Option<bool> { | |
| if let Some(a) = Some(false) { | |
| Some(!a) | |
| } else { | |
| None | |
| } | |
| } | |
| pub struct PatternedError { | |
| flag: bool, | |
| } | |
| // No warning | |
| fn pattern() -> Result<(), PatternedError> { | |
| let res = Ok(()); | |
| if let Err(err @ PatternedError { flag: true }) = res { | |
| return Err(err); | |
| } | |
| res | |
| } | |
| fn expect_expr(a: Option<usize>) -> Option<usize> { | |
| #[expect(clippy::needless_question_mark)] | |
| Some(a?) | |
| } | |
| fn main() {} | |
| // `?` is not the same as `return None;` if inside of a try block | |
| fn issue8628(a: Option<u32>) -> Option<u32> { | |
| let b: Option<u32> = try { | |
| if a.is_none() { | |
| return None; | |
| } | |
| 32 | |
| }; | |
| b.or(Some(128)) | |
| } | |
| fn issue6828_nested_body() -> Option<u32> { | |
| try { | |
| fn f2(a: Option<i32>) -> Option<i32> { | |
| a?; | |
| Some(32) | |
| } | |
| 123 | |
| } | |
| } | |
| // should not lint, `?` operator not available in const context | |
| const fn issue9175(option: Option<()>) -> Option<()> { | |
| if option.is_none() { | |
| return None; | |
| } | |
| //stuff | |
| Some(()) | |
| } | |
| fn issue12337() -> Option<i32> { | |
| let _: Option<i32> = try { | |
| let Some(_) = Some(42) else { | |
| return None; | |
| }; | |
| 123 | |
| }; | |
| Some(42) | |
| } | |
| fn issue11983(option: &Option<String>) -> Option<()> { | |
| // Don't lint, `&Option` dose not impl `Try`. | |
| let Some(v) = option else { return None }; | |
| let opt = Some(String::new()); | |
| // Don't lint, `branch` method in `Try` takes ownership of `opt`, | |
| // and `(&opt)?` also doesn't work since it's `&Option`. | |
| let Some(v) = &opt else { return None }; | |
| let mov = opt; | |
| Some(()) | |
| } | |
| struct Foo { | |
| owned: Option<String>, | |
| } | |
| struct Bar { | |
| foo: Foo, | |
| } | |
| #[allow(clippy::disallowed_names)] | |
| fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { | |
| // Don't lint, `owned` is behind a shared reference. | |
| let Some(v) = &foo.owned else { | |
| return None; | |
| }; | |
| // Don't lint, `owned` is behind a shared reference. | |
| let Some(v) = &bar.foo.owned else { | |
| return None; | |
| }; | |
| // lint | |
| let v = bar.foo.owned.clone()?; | |
| //~^^^ question_mark | |
| Some(()) | |
| } | |
| struct StructWithOptionString { | |
| opt_x: Option<String>, | |
| } | |
| struct WrapperStructWithString(String); | |
| #[allow(clippy::disallowed_names)] | |
| fn issue_13417(foo: &mut StructWithOptionString) -> Option<String> { | |
| let x = foo.opt_x.as_ref()?; | |
| //~^^^ question_mark | |
| let opt_y = Some(x.clone()); | |
| std::mem::replace(&mut foo.opt_x, opt_y) | |
| } | |
| #[allow(clippy::disallowed_names)] | |
| fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option<String> { | |
| let x = foo.opt_x.as_mut()?; | |
| //~^^^ question_mark | |
| let opt_y = Some(x.clone()); | |
| std::mem::replace(&mut foo.opt_x, opt_y) | |
| } | |
| #[allow(clippy::disallowed_names)] | |
| fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option<WrapperStructWithString>) -> Option<()> { | |
| let x @ y = foo.opt_x.as_ref()?; | |
| //~^^^ question_mark | |
| let x @ &WrapperStructWithString(_) = bar.as_ref()?; | |
| //~^^^ question_mark | |
| let x @ &mut WrapperStructWithString(_) = bar.as_mut()?; | |
| //~^^^ question_mark | |
| Some(()) | |
| } | |
| #[clippy::msrv = "1.12"] | |
| fn msrv_1_12(arg: Option<i32>) -> Option<i32> { | |
| if arg.is_none() { | |
| return None; | |
| } | |
| let val = match arg { | |
| Some(val) => val, | |
| None => return None, | |
| }; | |
| println!("{}", val); | |
| Some(val) | |
| } | |
| #[clippy::msrv = "1.13"] | |
| fn msrv_1_13(arg: Option<i32>) -> Option<i32> { | |
| arg?; | |
| let val = arg?; | |
| println!("{}", val); | |
| Some(val) | |
| } | |
| fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> { | |
| let a = (*a)?; | |
| //~^^^ question_mark | |
| Some(format!("{a}")) | |
| } | |
| fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> { | |
| const N: i32 = 0; | |
| let Some((x, N)) = x else { | |
| return None; | |
| }; | |
| None | |
| } | |
| fn issue_13642(x: Option<i32>) -> Option<()> { | |
| let Some(x) = x else { | |
| #[cfg(false)] | |
| panic!(); | |
| #[cfg(true)] | |
| return None; | |
| }; | |
| None | |
| } | |
| fn issue_15679() -> Result<i32, String> { | |
| let some_result: Result<i32, &'static str> = todo!(); | |
| some_result?; | |
| some_result?; | |
| some_result?; | |
| Ok(0) | |
| } | |
| mod issue14894 { | |
| fn use_after_question_mark(do_something_else: impl Fn() -> Result<String, ()>) -> Result<(), ()> { | |
| let result = do_something_else(); | |
| if let Err(reason) = result { | |
| return Err(reason); | |
| } | |
| drop(result); | |
| let result = do_something_else(); | |
| let x = result?; | |
| drop(x); | |
| Ok(()) | |
| } | |
| #[expect(dropping_copy_types)] | |
| fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result<i32, ()>) -> Result<(), ()> { | |
| let result = do_something_else(); | |
| result?; | |
| drop(result); | |
| Ok(()) | |
| } | |
| } | |
| fn wrongly_unmangled_macros() -> Option<i32> { | |
| macro_rules! test_expr { | |
| ($val:expr) => { | |
| Some($val) | |
| }; | |
| } | |
| let x = test_expr!(42)?; | |
| //~^^^ question_mark | |
| Some(x); | |
| test_expr!(42)?; | |
| test_expr!(42) | |
| } | |
| fn issue16429(b: i32) -> Option<i32> { | |
| let a = Some(5); | |
| let _ = if b == 1 { | |
| b | |
| } else { a? }; | |
| Some(0) | |
| } | |
| fn issue16654() -> Result<(), i32> { | |
| let result = func_returning_result(); | |
| #[allow(clippy::collapsible_if)] | |
| if true { | |
| result?; | |
| } | |
| _ = [{ result?; }]; | |
| Ok(()) | |
| } | |
| #[rustfmt::skip] | |
| fn issue16751(mut v: Option<usize>) -> Option<usize> { | |
| let _ = { | |
| let n = &v?; | |
| println!("{n}"); | |
| Some(42) | |
| }; | |
| let _ = { | |
| let ref mut n = v?; | |
| println!("{n}"); | |
| Some(42) | |
| }; | |
| let _ = { | |
| let ref mut n = v?; | |
| //~^ question_mark | |
| println!("{n}"); | |
| 42 | |
| }; | |
| { | |
| let n = v?; | |
| if n > 10 { Some(42) } else { None } | |
| } | |
| } | |
| fn issue_destructuring_assignment() -> Option<(i32, i32)> { | |
| let mut a = 0i32; | |
| let mut b = 0i32; | |
| let opt: Option<(i32, i32)> = Some((1, 2)); | |
| { | |
| let x = opt?; | |
| (a, b) = x | |
| } | |
| Some((a, b)) | |
| } |