Skip to content

Commit 597f61b

Browse files
committed
Optionally allow expect and unwrap in tests
1 parent bdfea1c commit 597f61b

File tree

12 files changed

+543
-181
lines changed

12 files changed

+543
-181
lines changed

clippy_lints/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
582582
});
583583

584584
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
585+
let allow_expect_in_tests = conf.allow_expect_in_tests;
586+
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
585587
store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
586-
store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
588+
store.register_late_pass(move || {
589+
Box::new(methods::Methods::new(
590+
avoid_breaking_exported_api,
591+
msrv,
592+
allow_expect_in_tests,
593+
allow_unwrap_in_tests,
594+
))
595+
});
587596
store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
588597
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
589598
store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));

clippy_lints/src/methods/expect_used.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::is_in_test_function;
23
use clippy_utils::ty::is_type_diagnostic_item;
34
use rustc_hir as hir;
45
use rustc_lint::LateContext;
@@ -7,7 +8,7 @@ use rustc_span::sym;
78
use super::EXPECT_USED;
89

910
/// lint use of `expect()` for `Option`s and `Result`s
10-
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
11+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
1112
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
1213

1314
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
@@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
1819
None
1920
};
2021

22+
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
23+
return;
24+
}
25+
2126
if let Some((lint, kind, none_value)) = mess {
2227
span_lint_and_help(
2328
cx,

clippy_lints/src/methods/mod.rs

Lines changed: 191 additions & 177 deletions
Large diffs are not rendered by default.

clippy_lints/src/methods/unwrap_used.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::is_in_test_function;
23
use clippy_utils::ty::is_type_diagnostic_item;
34
use rustc_hir as hir;
45
use rustc_lint::LateContext;
@@ -7,7 +8,7 @@ use rustc_span::sym;
78
use super::UNWRAP_USED;
89

910
/// lint use of `unwrap()` for `Option`s and `Result`s
10-
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
11+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
1112
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
1213

1314
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
@@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
1819
None
1920
};
2021

22+
if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
23+
return;
24+
}
25+
2126
if let Some((lint, kind, none_value)) = mess {
2227
span_lint_and_help(
2328
cx,

clippy_lints/src/utils/conf.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,14 @@ define_Conf! {
316316
///
317317
/// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
318318
(max_include_file_size: u64 = 1_000_000),
319+
/// Lint: EXPECT_USED.
320+
///
321+
/// Whether `expect` should be allowed in test functions
322+
(allow_expect_in_tests: bool = false),
323+
/// Lint: UNWRAP_USED.
324+
///
325+
/// Whether `unwrap` should be allowed in test functions
326+
(allow_unwrap_in_tests: bool = false),
319327
}
320328

321329
/// Search for the configuration file.

tests/ui-toml/expect_used/clippy.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
allow-expect-in-tests = true
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// compile-flags: --test
2+
#![warn(clippy::expect_used)]
3+
4+
fn expect_option() {
5+
let opt = Some(0);
6+
let _ = opt.expect("");
7+
}
8+
9+
fn expect_result() {
10+
let res: Result<u8, ()> = Ok(0);
11+
let _ = res.expect("");
12+
}
13+
14+
fn main() {
15+
expect_option();
16+
expect_result();
17+
}
18+
19+
#[test]
20+
fn test_expect_option() {
21+
let opt = Some(0);
22+
let _ = opt.expect("");
23+
}
24+
25+
#[test]
26+
fn test_expect_result() {
27+
let res: Result<u8, ()> = Ok(0);
28+
let _ = res.expect("");
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: used `expect()` on `an Option` value
2+
--> $DIR/expect_used.rs:6:13
3+
|
4+
LL | let _ = opt.expect("");
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::expect-used` implied by `-D warnings`
8+
= help: if this value is an `None`, it will panic
9+
10+
error: used `expect()` on `a Result` value
11+
--> $DIR/expect_used.rs:11:13
12+
|
13+
LL | let _ = res.expect("");
14+
| ^^^^^^^^^^^^^^
15+
|
16+
= help: if this value is an `Err`, it will panic
17+
18+
error: aborting due to 2 previous errors
19+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1
1+
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `allow-expect-in-tests`, `allow-unwrap-in-tests`, `third-party` at line 5 column 1
22

33
error: aborting due to previous error
44

tests/ui-toml/unwrap_used/clippy.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
allow-unwrap-in-tests = true
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// compile-flags: --test
2+
3+
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
4+
#![warn(clippy::unwrap_used)]
5+
#![deny(clippy::get_unwrap)]
6+
7+
use std::collections::BTreeMap;
8+
use std::collections::HashMap;
9+
use std::collections::VecDeque;
10+
use std::iter::FromIterator;
11+
12+
struct GetFalsePositive {
13+
arr: [u32; 3],
14+
}
15+
16+
impl GetFalsePositive {
17+
fn get(&self, pos: usize) -> Option<&u32> {
18+
self.arr.get(pos)
19+
}
20+
fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
21+
self.arr.get_mut(pos)
22+
}
23+
}
24+
25+
fn main() {
26+
let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
27+
let mut some_slice = &mut [0, 1, 2, 3];
28+
let mut some_vec = vec![0, 1, 2, 3];
29+
let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
30+
let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
31+
let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
32+
let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
33+
34+
{
35+
// Test `get().unwrap()`
36+
let _ = boxed_slice.get(1).unwrap();
37+
let _ = some_slice.get(0).unwrap();
38+
let _ = some_vec.get(0).unwrap();
39+
let _ = some_vecdeque.get(0).unwrap();
40+
let _ = some_hashmap.get(&1).unwrap();
41+
let _ = some_btreemap.get(&1).unwrap();
42+
#[allow(clippy::unwrap_used)]
43+
let _ = false_positive.get(0).unwrap();
44+
// Test with deref
45+
let _: u8 = *boxed_slice.get(1).unwrap();
46+
}
47+
48+
{
49+
// Test `get_mut().unwrap()`
50+
*boxed_slice.get_mut(0).unwrap() = 1;
51+
*some_slice.get_mut(0).unwrap() = 1;
52+
*some_vec.get_mut(0).unwrap() = 1;
53+
*some_vecdeque.get_mut(0).unwrap() = 1;
54+
// Check false positives
55+
#[allow(clippy::unwrap_used)]
56+
{
57+
*some_hashmap.get_mut(&1).unwrap() = 'b';
58+
*some_btreemap.get_mut(&1).unwrap() = 'b';
59+
*false_positive.get_mut(0).unwrap() = 1;
60+
}
61+
}
62+
63+
{
64+
// Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
65+
let _ = some_vec.get(0..1).unwrap().to_vec();
66+
let _ = some_vec.get_mut(0..1).unwrap().to_vec();
67+
}
68+
}
69+
70+
#[test]
71+
fn test() {
72+
let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
73+
let _ = boxed_slice.get(1).unwrap();
74+
}

0 commit comments

Comments
 (0)