diff --git a/CHANGELOG.md b/CHANGELOG.md index ec2e317dd746..3175b36dc479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7234,6 +7234,7 @@ Released 2018-09-13 [`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-consts [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests +[`allow-unwrap-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-types [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 6cd3e4d195a1..c87f8e9a68de 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -214,6 +214,23 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` * [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) +## `allow-unwrap-types` +List of types to allow `unwrap()` and `expect()` on. + +#### Example + +```toml +allow-unwrap-types = [ "std::sync::LockResult" ] +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) +* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) + + ## `allow-useless-vec-in-tests` Whether `useless_vec` should ignore test functions or `#[cfg(test)]` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 0c12c3cdcbc6..41099f94b044 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -413,6 +413,15 @@ define_Conf! { /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` #[lints(unwrap_used)] allow_unwrap_in_tests: bool = false, + /// List of types to allow `unwrap()` and `expect()` on. + /// + /// #### Example + /// + /// ```toml + /// allow-unwrap-types = [ "std::sync::LockResult" ] + /// ``` + #[lints(expect_used, unwrap_used)] + allow_unwrap_types: Vec = Vec::new(), /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` #[lints(useless_vec)] allow_useless_vec_in_tests: bool = false, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ff534abe5353..c1b71a61ccb2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4760,6 +4760,7 @@ pub struct Methods { allow_unwrap_in_consts: bool, allowed_dotfiles: FxHashSet<&'static str>, format_args: FormatArgsStorage, + allow_unwrap_types: Vec, } impl Methods { @@ -4776,6 +4777,7 @@ impl Methods { allow_unwrap_in_consts: conf.allow_unwrap_in_consts, allowed_dotfiles, format_args, + allow_unwrap_types: conf.allow_unwrap_types.clone(), } } } @@ -4974,6 +4976,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self.allow_expect_in_tests, self.allow_unwrap_in_consts, self.allow_expect_in_consts, + &self.allow_unwrap_types, ); }, ExprKind::MethodCall(..) => { @@ -5722,6 +5725,7 @@ impl Methods { false, self.allow_expect_in_consts, self.allow_expect_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Expect, ); expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); @@ -5734,6 +5738,7 @@ impl Methods { true, self.allow_expect_in_consts, self.allow_expect_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Expect, ); }, @@ -5754,6 +5759,7 @@ impl Methods { false, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Unwrap, ); }, @@ -5765,6 +5771,7 @@ impl Methods { true, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Unwrap, ); }, diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 4effab3a5e63..6d8777cbc23f 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -36,6 +36,7 @@ impl Variant { /// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their /// `expect` counterparts). +#[allow(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -43,6 +44,7 @@ pub(super) fn check( is_err: bool, allow_unwrap_in_consts: bool, allow_unwrap_in_tests: bool, + allow_unwrap_types: &[String], variant: Variant, ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); @@ -64,6 +66,54 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; + let ty_name = ty.to_string(); + if allow_unwrap_types + .iter() + .any(|allowed_type| ty_name.starts_with(allowed_type) || ty_name == *allowed_type) + { + return; + } + + for s in allow_unwrap_types { + let def_ids = clippy_utils::paths::lookup_path_str(cx.tcx, clippy_utils::paths::PathNS::Type, s); + for def_id in def_ids { + if let ty::Adt(adt, _) = ty.kind() + && adt.did() == def_id + { + return; + } + if cx.tcx.def_kind(def_id) == DefKind::TyAlias { + let alias_ty = cx.tcx.type_of(def_id).instantiate_identity(); + if let (ty::Adt(adt, substs), ty::Adt(alias_adt, alias_substs)) = (ty.kind(), alias_ty.kind()) + && adt.did() == alias_adt.did() + { + let mut all_match = true; + for (arg, alias_arg) in substs.iter().zip(alias_substs.iter()) { + if let (Some(arg_ty), Some(alias_arg_ty)) = (arg.as_type(), alias_arg.as_type()) { + if matches!(alias_arg_ty.kind(), ty::Param(_)) { + continue; + } + if let (ty::Adt(arg_adt, _), ty::Adt(alias_arg_adt, _)) = + (arg_ty.peel_refs().kind(), alias_arg_ty.peel_refs().kind()) + { + if arg_adt.did() != alias_arg_adt.did() { + all_match = false; + break; + } + } else if arg_ty != alias_arg_ty { + all_match = false; + break; + } + } + } + if all_match { + return; + } + } + } + } + } + if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; } @@ -99,6 +149,7 @@ pub(super) fn check_call( allow_unwrap_in_tests: bool, allow_expect_in_consts: bool, allow_expect_in_tests: bool, + allow_unwrap_types: &[String], ) { let Some(recv) = args.first() else { return; @@ -116,6 +167,7 @@ pub(super) fn check_call( false, allow_unwrap_in_consts, allow_unwrap_in_tests, + allow_unwrap_types, Variant::Unwrap, ); }, @@ -127,6 +179,7 @@ pub(super) fn check_call( false, allow_expect_in_consts, allow_expect_in_tests, + allow_unwrap_types, Variant::Expect, ); }, @@ -138,6 +191,7 @@ pub(super) fn check_call( true, allow_unwrap_in_consts, allow_unwrap_in_tests, + allow_unwrap_types, Variant::Unwrap, ); }, @@ -149,6 +203,7 @@ pub(super) fn check_call( true, allow_expect_in_consts, allow_expect_in_tests, + allow_unwrap_types, Variant::Expect, ); }, diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index eea1f4f00e11..6bb3db8db67f 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -18,6 +18,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates @@ -118,6 +119,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates @@ -218,6 +220,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates diff --git a/tests/ui-toml/unwrap_used_allowed/clippy.toml b/tests/ui-toml/unwrap_used_allowed/clippy.toml new file mode 100644 index 000000000000..cceb303c126f --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/clippy.toml @@ -0,0 +1 @@ +allow-unwrap-types = ["std::sync::LockResult"] \ No newline at end of file diff --git a/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs new file mode 100644 index 000000000000..7905674e21e7 --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs @@ -0,0 +1,20 @@ +#![warn(clippy::unwrap_used)] +#![allow(clippy::unnecessary_literal_unwrap)] +#![allow(unused_variables)] +use std::sync::Mutex; + +fn main() { + let m = Mutex::new(0); + // This is allowed because `LockResult` is configured! + let _guard = m.lock().unwrap(); + + let optional: Option = Some(1); + // This is not allowed! + let _opt = optional.unwrap(); + //~^ ERROR: used `unwrap()` on an `Option` value + + let result: Result = Ok(1); + // This is not allowed! + let _res = result.unwrap(); + //~^ ERROR: used `unwrap()` on a `Result` value +} diff --git a/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr new file mode 100644 index 000000000000..1df89bf2eb92 --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr @@ -0,0 +1,22 @@ +error: used `unwrap()` on an `Option` value + --> tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs:13:16 + | +LL | let _opt = optional.unwrap(); + | ^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` + +error: used `unwrap()` on a `Result` value + --> tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs:18:16 + | +LL | let _res = result.unwrap(); + | ^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + = help: consider using `expect()` to provide a better panic message + +error: aborting due to 2 previous errors +