Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions book/src/lint_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)]`

Expand Down
9 changes: 9 additions & 0 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = Vec::new(),
/// Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
#[lints(useless_vec)]
allow_useless_vec_in_tests: bool = false,
Expand Down
7 changes: 7 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4760,6 +4760,7 @@ pub struct Methods {
allow_unwrap_in_consts: bool,
allowed_dotfiles: FxHashSet<&'static str>,
format_args: FormatArgsStorage,
allow_unwrap_types: Vec<String>,
}

impl Methods {
Expand All @@ -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(),
}
}
}
Expand Down Expand Up @@ -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(..) => {
Expand Down Expand Up @@ -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);
Expand All @@ -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,
);
},
Expand All @@ -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,
);
},
Expand All @@ -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,
);
},
Expand Down
55 changes: 55 additions & 0 deletions clippy_lints/src/methods/unwrap_expect_used.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ 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<'_>,
recv: &Expr<'_>,
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();
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -116,6 +167,7 @@ pub(super) fn check_call(
false,
allow_unwrap_in_consts,
allow_unwrap_in_tests,
allow_unwrap_types,
Variant::Unwrap,
);
},
Expand All @@ -127,6 +179,7 @@ pub(super) fn check_call(
false,
allow_expect_in_consts,
allow_expect_in_tests,
allow_unwrap_types,
Variant::Expect,
);
},
Expand All @@ -138,6 +191,7 @@ pub(super) fn check_call(
true,
allow_unwrap_in_consts,
allow_unwrap_in_tests,
allow_unwrap_types,
Variant::Unwrap,
);
},
Expand All @@ -149,6 +203,7 @@ pub(super) fn check_call(
true,
allow_expect_in_consts,
allow_expect_in_tests,
allow_unwrap_types,
Variant::Expect,
);
},
Expand Down
3 changes: 3 additions & 0 deletions tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/ui-toml/unwrap_used_allowed/clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
allow-unwrap-types = ["std::sync::LockResult"]
20 changes: 20 additions & 0 deletions tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs
Original file line number Diff line number Diff line change
@@ -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<i32> = Some(1);
// This is not allowed!
let _opt = optional.unwrap();
//~^ ERROR: used `unwrap()` on an `Option` value

let result: Result<i32, ()> = Ok(1);
// This is not allowed!
let _res = result.unwrap();
//~^ ERROR: used `unwrap()` on a `Result` value
}
22 changes: 22 additions & 0 deletions tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr
Original file line number Diff line number Diff line change
@@ -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