Skip to content

Commit 32aef11

Browse files
authored
{expect,unwrap}_used: add options to lint at compilation time (#14200)
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
2 parents ffa1caf + da6a059 commit 32aef11

File tree

13 files changed

+123
-4
lines changed

13 files changed

+123
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6268,6 +6268,7 @@ Released 2018-09-13
62686268
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
62696269
[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
62706270
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
6271+
[`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts
62716272
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
62726273
[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests
62736274
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
@@ -6276,6 +6277,7 @@ Released 2018-09-13
62766277
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
62776278
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
62786279
[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for
6280+
[`allow-unwrap-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-consts
62796281
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
62806282
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
62816283
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles

book/src/lint_configuration.md

+20
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
7171
* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
7272

7373

74+
## `allow-expect-in-consts`
75+
Whether `expect` should be allowed in code always evaluated at compile time
76+
77+
**Default Value:** `true`
78+
79+
---
80+
**Affected lints:**
81+
* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
82+
83+
7484
## `allow-expect-in-tests`
7585
Whether `expect` should be allowed in test functions or `#[cfg(test)]`
7686

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

166176

177+
## `allow-unwrap-in-consts`
178+
Whether `unwrap` should be allowed in code always evaluated at compile time
179+
180+
**Default Value:** `true`
181+
182+
---
183+
**Affected lints:**
184+
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
185+
186+
167187
## `allow-unwrap-in-tests`
168188
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
169189

clippy_config/src/conf.rs

+6
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ define_Conf! {
288288
/// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
289289
#[lints(dbg_macro)]
290290
allow_dbg_in_tests: bool = false,
291+
/// Whether `expect` should be allowed in code always evaluated at compile time
292+
#[lints(expect_used)]
293+
allow_expect_in_consts: bool = true,
291294
/// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
292295
#[lints(expect_used)]
293296
allow_expect_in_tests: bool = false,
@@ -325,6 +328,9 @@ define_Conf! {
325328
#[lints(renamed_function_params)]
326329
allow_renamed_params_for: Vec<String> =
327330
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(),
331+
/// Whether `unwrap` should be allowed in code always evaluated at compile time
332+
#[lints(unwrap_used)]
333+
allow_unwrap_in_consts: bool = true,
328334
/// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
329335
#[lints(unwrap_used)]
330336
allow_unwrap_in_tests: bool = false,

clippy_lints/src/methods/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -4406,11 +4406,14 @@ declare_clippy_lint! {
44064406
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
44074407
}
44084408

4409+
#[expect(clippy::struct_excessive_bools)]
44094410
pub struct Methods {
44104411
avoid_breaking_exported_api: bool,
44114412
msrv: Msrv,
44124413
allow_expect_in_tests: bool,
44134414
allow_unwrap_in_tests: bool,
4415+
allow_expect_in_consts: bool,
4416+
allow_unwrap_in_consts: bool,
44144417
allowed_dotfiles: FxHashSet<&'static str>,
44154418
format_args: FormatArgsStorage,
44164419
}
@@ -4425,6 +4428,8 @@ impl Methods {
44254428
msrv: conf.msrv.clone(),
44264429
allow_expect_in_tests: conf.allow_expect_in_tests,
44274430
allow_unwrap_in_tests: conf.allow_unwrap_in_tests,
4431+
allow_expect_in_consts: conf.allow_expect_in_consts,
4432+
allow_unwrap_in_consts: conf.allow_unwrap_in_consts,
44284433
allowed_dotfiles,
44294434
format_args,
44304435
}
@@ -4918,6 +4923,7 @@ impl Methods {
49184923
expr,
49194924
recv,
49204925
false,
4926+
self.allow_expect_in_consts,
49214927
self.allow_expect_in_tests,
49224928
unwrap_expect_used::Variant::Expect,
49234929
),
@@ -4931,6 +4937,7 @@ impl Methods {
49314937
expr,
49324938
recv,
49334939
true,
4940+
self.allow_expect_in_consts,
49344941
self.allow_expect_in_tests,
49354942
unwrap_expect_used::Variant::Expect,
49364943
);
@@ -5304,6 +5311,7 @@ impl Methods {
53045311
expr,
53055312
recv,
53065313
false,
5314+
self.allow_unwrap_in_consts,
53075315
self.allow_unwrap_in_tests,
53085316
unwrap_expect_used::Variant::Unwrap,
53095317
);
@@ -5315,6 +5323,7 @@ impl Methods {
53155323
expr,
53165324
recv,
53175325
true,
5326+
self.allow_unwrap_in_consts,
53185327
self.allow_unwrap_in_tests,
53195328
unwrap_expect_used::Variant::Unwrap,
53205329
);

clippy_lints/src/methods/unwrap_expect_used.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
3-
use clippy_utils::{is_in_test, is_lint_allowed};
3+
use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed};
44
use rustc_hir::Expr;
55
use rustc_lint::{LateContext, Lint};
66
use rustc_middle::ty;
@@ -39,6 +39,7 @@ pub(super) fn check(
3939
expr: &Expr<'_>,
4040
recv: &Expr<'_>,
4141
is_err: bool,
42+
allow_unwrap_in_consts: bool,
4243
allow_unwrap_in_tests: bool,
4344
variant: Variant,
4445
) {
@@ -65,6 +66,10 @@ pub(super) fn check(
6566
return;
6667
}
6768

69+
if allow_unwrap_in_consts && is_inside_always_const_context(cx.tcx, expr.hir_id) {
70+
return;
71+
}
72+
6873
span_lint_and_then(
6974
cx,
7075
variant.lint(),

tests/ui-toml/expect_used/clippy.toml

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
allow-expect-in-consts = false
12
allow-expect-in-tests = true

tests/ui-toml/expect_used/expect_used.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@compile-flags: --test
2+
//@no-rustfix
23
#![warn(clippy::expect_used)]
34
#![allow(clippy::unnecessary_literal_unwrap)]
45

@@ -15,6 +16,14 @@ fn expect_result() {
1516
fn main() {
1617
expect_option();
1718
expect_result();
19+
20+
const SOME: Option<i32> = Some(3);
21+
const UNWRAPPED: i32 = SOME.expect("Not three?");
22+
//~^ ERROR: used `expect()` on an `Option` value
23+
const {
24+
SOME.expect("Still not three?");
25+
//~^ ERROR: used `expect()` on an `Option` value
26+
}
1827
}
1928

2029
#[test]
+19-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: used `expect()` on an `Option` value
2-
--> tests/ui-toml/expect_used/expect_used.rs:7:13
2+
--> tests/ui-toml/expect_used/expect_used.rs:8:13
33
|
44
LL | let _ = opt.expect("");
55
| ^^^^^^^^^^^^^^
@@ -9,12 +9,28 @@ LL | let _ = opt.expect("");
99
= help: to override `-D warnings` add `#[allow(clippy::expect_used)]`
1010

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

19-
error: aborting due to 2 previous errors
19+
error: used `expect()` on an `Option` value
20+
--> tests/ui-toml/expect_used/expect_used.rs:21:28
21+
|
22+
LL | const UNWRAPPED: i32 = SOME.expect("Not three?");
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: if this value is `None`, it will panic
26+
27+
error: used `expect()` on an `Option` value
28+
--> tests/ui-toml/expect_used/expect_used.rs:24:9
29+
|
30+
LL | SOME.expect("Still not three?");
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= note: if this value is `None`, it will panic
34+
35+
error: aborting due to 4 previous errors
2036

tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
55
accept-comment-above-statement
66
allow-comparison-to-zero
77
allow-dbg-in-tests
8+
allow-expect-in-consts
89
allow-expect-in-tests
910
allow-indexing-slicing-in-tests
1011
allow-mixed-uninlined-format-args
@@ -13,6 +14,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
1314
allow-print-in-tests
1415
allow-private-module-inception
1516
allow-renamed-params-for
17+
allow-unwrap-in-consts
1618
allow-unwrap-in-tests
1719
allow-useless-vec-in-tests
1820
allowed-dotfiles
@@ -94,6 +96,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
9496
accept-comment-above-statement
9597
allow-comparison-to-zero
9698
allow-dbg-in-tests
99+
allow-expect-in-consts
97100
allow-expect-in-tests
98101
allow-indexing-slicing-in-tests
99102
allow-mixed-uninlined-format-args
@@ -102,6 +105,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
102105
allow-print-in-tests
103106
allow-private-module-inception
104107
allow-renamed-params-for
108+
allow-unwrap-in-consts
105109
allow-unwrap-in-tests
106110
allow-useless-vec-in-tests
107111
allowed-dotfiles
@@ -183,6 +187,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
183187
accept-comment-above-statement
184188
allow-comparison-to-zero
185189
allow-dbg-in-tests
190+
allow-expect-in-consts
186191
allow-expect-in-tests
187192
allow-indexing-slicing-in-tests
188193
allow-mixed-uninlined-format-args
@@ -191,6 +196,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
191196
allow-print-in-tests
192197
allow-private-module-inception
193198
allow-renamed-params-for
199+
allow-unwrap-in-consts
194200
allow-unwrap-in-tests
195201
allow-useless-vec-in-tests
196202
allowed-dotfiles

tests/ui-toml/unwrap_used/clippy.toml

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
allow-unwrap-in-consts = false
12
allow-unwrap-in-tests = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![warn(clippy::unwrap_used)]
2+
3+
fn main() {
4+
const SOME: Option<i32> = Some(3);
5+
const UNWRAPPED: i32 = SOME.unwrap();
6+
//~^ ERROR: used `unwrap()` on an `Option` value
7+
const {
8+
SOME.unwrap();
9+
//~^ ERROR: used `unwrap()` on an `Option` value
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: used `unwrap()` on an `Option` value
2+
--> tests/ui-toml/unwrap_used/unwrap_used_const.rs:5:28
3+
|
4+
LL | const UNWRAPPED: i32 = SOME.unwrap();
5+
| ^^^^^^^^^^^^^
6+
|
7+
= note: if this value is `None`, it will panic
8+
= help: consider using `expect()` to provide a better panic message
9+
= note: `-D clippy::unwrap-used` implied by `-D warnings`
10+
= help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]`
11+
12+
error: used `unwrap()` on an `Option` value
13+
--> tests/ui-toml/unwrap_used/unwrap_used_const.rs:8:9
14+
|
15+
LL | SOME.unwrap();
16+
| ^^^^^^^^^^^^^
17+
|
18+
= note: if this value is `None`, it will panic
19+
= help: consider using `expect()` to provide a better panic message
20+
21+
error: aborting due to 2 previous errors
22+

tests/ui/unwrap_expect_used.rs

+11
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,15 @@ fn main() {
5050
//~^ ERROR: used `unwrap_err()` on a `Result` value
5151
a.expect_err("Hello error!");
5252
//~^ ERROR: used `expect_err()` on a `Result` value
53+
54+
// Don't trigger in compile time contexts by default
55+
const SOME: Option<i32> = Some(3);
56+
const UNWRAPPED: i32 = SOME.unwrap();
57+
const EXPECTED: i32 = SOME.expect("Not three?");
58+
const {
59+
SOME.unwrap();
60+
}
61+
const {
62+
SOME.expect("Still not three?");
63+
}
5364
}

0 commit comments

Comments
 (0)