Skip to content

Commit

Permalink
{expect,unwrap}_used: add options to lint at compilation time (#14200)
Browse files Browse the repository at this point in the history
By default, do not lint `.unwrap()` and `.expect(…)` in always const
contexts, as a failure would be detected at compile time anyway.

New options `allow_expect_in_consts` and `allow_unwrap_in_consts`,
defaulting to `true`, can be turned unset to still lint in always const
contexts.

Close #14198

changelog: [`unwrap_used`, `expect_used`]: add new option to lint in
always constant contexts
  • Loading branch information
llogiq authored Feb 12, 2025
2 parents ffa1caf + da6a059 commit 32aef11
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6268,6 +6268,7 @@ Released 2018-09-13
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
[`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
Expand All @@ -6276,6 +6277,7 @@ Released 2018-09-13
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
[`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-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
Expand Down
20 changes: 20 additions & 0 deletions book/src/lint_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)


## `allow-expect-in-consts`
Whether `expect` should be allowed in code always evaluated at compile time

**Default Value:** `true`

---
**Affected lints:**
* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)


## `allow-expect-in-tests`
Whether `expect` should be allowed in test functions or `#[cfg(test)]`

Expand Down Expand Up @@ -164,6 +174,16 @@ default configuration of Clippy. By default, any configuration will replace the
* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params)


## `allow-unwrap-in-consts`
Whether `unwrap` should be allowed in code always evaluated at compile time

**Default Value:** `true`

---
**Affected lints:**
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)


## `allow-unwrap-in-tests`
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`

Expand Down
6 changes: 6 additions & 0 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ define_Conf! {
/// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
#[lints(dbg_macro)]
allow_dbg_in_tests: bool = false,
/// Whether `expect` should be allowed in code always evaluated at compile time
#[lints(expect_used)]
allow_expect_in_consts: bool = true,
/// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
#[lints(expect_used)]
allow_expect_in_tests: bool = false,
Expand Down Expand Up @@ -325,6 +328,9 @@ define_Conf! {
#[lints(renamed_function_params)]
allow_renamed_params_for: Vec<String> =
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(),
/// Whether `unwrap` should be allowed in code always evaluated at compile time
#[lints(unwrap_used)]
allow_unwrap_in_consts: bool = true,
/// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
#[lints(unwrap_used)]
allow_unwrap_in_tests: bool = false,
Expand Down
9 changes: 9 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4406,11 +4406,14 @@ declare_clippy_lint! {
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
}

#[expect(clippy::struct_excessive_bools)]
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
allow_expect_in_tests: bool,
allow_unwrap_in_tests: bool,
allow_expect_in_consts: bool,
allow_unwrap_in_consts: bool,
allowed_dotfiles: FxHashSet<&'static str>,
format_args: FormatArgsStorage,
}
Expand All @@ -4425,6 +4428,8 @@ impl Methods {
msrv: conf.msrv.clone(),
allow_expect_in_tests: conf.allow_expect_in_tests,
allow_unwrap_in_tests: conf.allow_unwrap_in_tests,
allow_expect_in_consts: conf.allow_expect_in_consts,
allow_unwrap_in_consts: conf.allow_unwrap_in_consts,
allowed_dotfiles,
format_args,
}
Expand Down Expand Up @@ -4918,6 +4923,7 @@ impl Methods {
expr,
recv,
false,
self.allow_expect_in_consts,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
),
Expand All @@ -4931,6 +4937,7 @@ impl Methods {
expr,
recv,
true,
self.allow_expect_in_consts,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
);
Expand Down Expand Up @@ -5304,6 +5311,7 @@ impl Methods {
expr,
recv,
false,
self.allow_unwrap_in_consts,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
Expand All @@ -5315,6 +5323,7 @@ impl Methods {
expr,
recv,
true,
self.allow_unwrap_in_consts,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
Expand Down
7 changes: 6 additions & 1 deletion clippy_lints/src/methods/unwrap_expect_used.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
use clippy_utils::{is_in_test, is_lint_allowed};
use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed};
use rustc_hir::Expr;
use rustc_lint::{LateContext, Lint};
use rustc_middle::ty;
Expand Down Expand Up @@ -39,6 +39,7 @@ pub(super) fn check(
expr: &Expr<'_>,
recv: &Expr<'_>,
is_err: bool,
allow_unwrap_in_consts: bool,
allow_unwrap_in_tests: bool,
variant: Variant,
) {
Expand All @@ -65,6 +66,10 @@ pub(super) fn check(
return;
}

if allow_unwrap_in_consts && is_inside_always_const_context(cx.tcx, expr.hir_id) {
return;
}

span_lint_and_then(
cx,
variant.lint(),
Expand Down
1 change: 1 addition & 0 deletions tests/ui-toml/expect_used/clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
allow-expect-in-consts = false
allow-expect-in-tests = true
9 changes: 9 additions & 0 deletions tests/ui-toml/expect_used/expect_used.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//@compile-flags: --test
//@no-rustfix
#![warn(clippy::expect_used)]
#![allow(clippy::unnecessary_literal_unwrap)]

Expand All @@ -15,6 +16,14 @@ fn expect_result() {
fn main() {
expect_option();
expect_result();

const SOME: Option<i32> = Some(3);
const UNWRAPPED: i32 = SOME.expect("Not three?");
//~^ ERROR: used `expect()` on an `Option` value
const {
SOME.expect("Still not three?");
//~^ ERROR: used `expect()` on an `Option` value
}
}

#[test]
Expand Down
22 changes: 19 additions & 3 deletions tests/ui-toml/expect_used/expect_used.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: used `expect()` on an `Option` value
--> tests/ui-toml/expect_used/expect_used.rs:7:13
--> tests/ui-toml/expect_used/expect_used.rs:8:13
|
LL | let _ = opt.expect("");
| ^^^^^^^^^^^^^^
Expand All @@ -9,12 +9,28 @@ LL | let _ = opt.expect("");
= help: to override `-D warnings` add `#[allow(clippy::expect_used)]`

error: used `expect()` on a `Result` value
--> tests/ui-toml/expect_used/expect_used.rs:12:13
--> tests/ui-toml/expect_used/expect_used.rs:13:13
|
LL | let _ = res.expect("");
| ^^^^^^^^^^^^^^
|
= note: if this value is an `Err`, it will panic

error: aborting due to 2 previous errors
error: used `expect()` on an `Option` value
--> tests/ui-toml/expect_used/expect_used.rs:21:28
|
LL | const UNWRAPPED: i32 = SOME.expect("Not three?");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: if this value is `None`, it will panic

error: used `expect()` on an `Option` value
--> tests/ui-toml/expect_used/expect_used.rs:24:9
|
LL | SOME.expect("Still not three?");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: if this value is `None`, it will panic

error: aborting due to 4 previous errors

6 changes: 6 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 @@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
accept-comment-above-statement
allow-comparison-to-zero
allow-dbg-in-tests
allow-expect-in-consts
allow-expect-in-tests
allow-indexing-slicing-in-tests
allow-mixed-uninlined-format-args
Expand All @@ -13,6 +14,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
allow-print-in-tests
allow-private-module-inception
allow-renamed-params-for
allow-unwrap-in-consts
allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles
Expand Down Expand Up @@ -94,6 +96,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
accept-comment-above-statement
allow-comparison-to-zero
allow-dbg-in-tests
allow-expect-in-consts
allow-expect-in-tests
allow-indexing-slicing-in-tests
allow-mixed-uninlined-format-args
Expand All @@ -102,6 +105,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
allow-print-in-tests
allow-private-module-inception
allow-renamed-params-for
allow-unwrap-in-consts
allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles
Expand Down Expand Up @@ -183,6 +187,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
accept-comment-above-statement
allow-comparison-to-zero
allow-dbg-in-tests
allow-expect-in-consts
allow-expect-in-tests
allow-indexing-slicing-in-tests
allow-mixed-uninlined-format-args
Expand All @@ -191,6 +196,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
allow-print-in-tests
allow-private-module-inception
allow-renamed-params-for
allow-unwrap-in-consts
allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles
Expand Down
1 change: 1 addition & 0 deletions tests/ui-toml/unwrap_used/clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
allow-unwrap-in-consts = false
allow-unwrap-in-tests = true
11 changes: 11 additions & 0 deletions tests/ui-toml/unwrap_used/unwrap_used_const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![warn(clippy::unwrap_used)]

fn main() {
const SOME: Option<i32> = Some(3);
const UNWRAPPED: i32 = SOME.unwrap();
//~^ ERROR: used `unwrap()` on an `Option` value
const {
SOME.unwrap();
//~^ ERROR: used `unwrap()` on an `Option` value
}
}
22 changes: 22 additions & 0 deletions tests/ui-toml/unwrap_used/unwrap_used_const.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/unwrap_used_const.rs:5:28
|
LL | const UNWRAPPED: i32 = SOME.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 an `Option` value
--> tests/ui-toml/unwrap_used/unwrap_used_const.rs:8:9
|
LL | SOME.unwrap();
| ^^^^^^^^^^^^^
|
= note: if this value is `None`, it will panic
= help: consider using `expect()` to provide a better panic message

error: aborting due to 2 previous errors

11 changes: 11 additions & 0 deletions tests/ui/unwrap_expect_used.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,15 @@ fn main() {
//~^ ERROR: used `unwrap_err()` on a `Result` value
a.expect_err("Hello error!");
//~^ ERROR: used `expect_err()` on a `Result` value

// Don't trigger in compile time contexts by default
const SOME: Option<i32> = Some(3);
const UNWRAPPED: i32 = SOME.unwrap();
const EXPECTED: i32 = SOME.expect("Not three?");
const {
SOME.unwrap();
}
const {
SOME.expect("Still not three?");
}
}

0 comments on commit 32aef11

Please sign in to comment.