Skip to content

Commit 1ab9ae6

Browse files
committed
feat: implement test_without_fail case lint
1 parent 5678531 commit 1ab9ae6

20 files changed

+710
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6004,6 +6004,7 @@ Released 2018-09-13
60046004
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
60056005
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
60066006
[`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest
6007+
[`test_without_fail_case`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_without_fail_case
60076008
[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
60086009
[`thread_local_initializer_can_be_made_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#thread_local_initializer_can_be_made_const
60096010
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
@@ -6215,6 +6216,9 @@ Released 2018-09-13
62156216
[`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
62166217
[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold
62176218
[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
6219+
[`test-without-fail-case-fallible-paths`]: https://doc.rust-lang.org/clippy/lint_configuration.html#test-without-fail-case-fallible-paths
6220+
[`test-without-fail-case-include-indexing-as-fallible`]: https://doc.rust-lang.org/clippy/lint_configuration.html#test-without-fail-case-include-indexing-as-fallible
6221+
[`test-without-fail-case-non-fallible-paths`]: https://doc.rust-lang.org/clippy/lint_configuration.html#test-without-fail-case-non-fallible-paths
62186222
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
62196223
[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
62206224
[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
1111
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.

book/src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
A collection of lints to catch common mistakes and improve your
77
[Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
Lints are divided into categories, each with a default [lint
1212
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how

book/src/lint_configuration.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,52 @@ if no suggestion can be made.
831831
* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
832832

833833

834+
## `test-without-fail-case-fallible-paths`
835+
List of full paths of macros and functions, that can fail. If a test, or a function
836+
that the test calls contains a call to any one of these, lint will mark the test fallible.
837+
838+
**Default Value:** `["core::panic", "core::assert", "core::assert_eq", "core::assert_ne"]`
839+
840+
---
841+
**Affected lints:**
842+
* [`test_without_fail_case`](https://rust-lang.github.io/rust-clippy/master/index.html#test_without_fail_case)
843+
844+
845+
## `test-without-fail-case-include-indexing-as-fallible`
846+
Whether to consider indexing as a fallible operation while assesing if a test can fail.
847+
Indexing is fallible, and thus the a test that is doing that can fail but it is likely
848+
that tests that fail this way were not intended.
849+
850+
If set true, the lint will consider indexing into a slice a failable case
851+
and won't lint tests that has some sort of indexing. This analysis still done
852+
in a interprocedural manner. Meaning that any indexing opeartion done inside of
853+
a function that the test calls will still result the test getting marked fallible.
854+
855+
By default this is set to `false`. That is because from a usability perspective,
856+
indexing an array is not the intended way to fail a test. So setting this `true`
857+
reduces false positives but makes the analysis more focused on possible byproducts
858+
of a test. That is the set of operations to get the point we assert something rather
859+
than the existance of asserting that thing.
860+
861+
**Default Value:** `false`
862+
863+
---
864+
**Affected lints:**
865+
* [`test_without_fail_case`](https://rust-lang.github.io/rust-clippy/master/index.html#test_without_fail_case)
866+
867+
868+
## `test-without-fail-case-non-fallible-paths`
869+
List of full paths of macros and functions, that we want to mark as "not going to fail".
870+
This allows us to make the lint more focused on actual short comings of our test suite
871+
by marking common routines non-fallible, even though they are fallible.
872+
873+
**Default Value:** `["std::print", "std::println", "std::dbg", "std::eprint", "std::eprintln"]`
874+
875+
---
876+
**Affected lints:**
877+
* [`test_without_fail_case`](https://rust-lang.github.io/rust-clippy/master/index.html#test_without_fail_case)
878+
879+
834880
## `too-large-for-stack`
835881
The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
836882

clippy_config/src/conf.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z
4646
const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
4747
const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
4848
&["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
49+
/// Default paths considered as fallible for `test_without_fail_case` lint.
50+
pub(crate) const DEFAULT_FALLIBLE_PATHS: &[&str] =
51+
&["core::panic", "core::assert", "core::assert_eq", "core::assert_ne"];
52+
/// Default paths considered as non-fallible for `test_without_fail_case` lint.
53+
pub(crate) const DEFAULT_NONFALLIBLE_PATHS: &[&str] =
54+
&["std::print", "std::println", "std::dbg", "std::eprint", "std::eprintln"];
4955

5056
/// Conf with parse errors
5157
#[derive(Default)]
@@ -628,6 +634,33 @@ define_Conf! {
628634
/// if no suggestion can be made.
629635
#[lints(indexing_slicing)]
630636
suppress_restriction_lint_in_const: bool = false,
637+
/// List of full paths of macros and functions, that can fail. If a test, or a function
638+
/// that the test calls contains a call to any one of these, lint will mark the test fallible.
639+
#[lints(test_without_fail_case)]
640+
test_without_fail_case_fallible_paths: Vec<String> =
641+
DEFAULT_FALLIBLE_PATHS.iter().map(ToString::to_string).collect(),
642+
/// Whether to consider indexing as a fallible operation while assesing if a test can fail.
643+
/// Indexing is fallible, and thus the a test that is doing that can fail but it is likely
644+
/// that tests that fail this way were not intended.
645+
///
646+
/// If set true, the lint will consider indexing into a slice a failable case
647+
/// and won't lint tests that has some sort of indexing. This analysis still done
648+
/// in a interprocedural manner. Meaning that any indexing opeartion done inside of
649+
/// a function that the test calls will still result the test getting marked fallible.
650+
///
651+
/// By default this is set to `false`. That is because from a usability perspective,
652+
/// indexing an array is not the intended way to fail a test. So setting this `true`
653+
/// reduces false positives but makes the analysis more focused on possible byproducts
654+
/// of a test. That is the set of operations to get the point we assert something rather
655+
/// than the existance of asserting that thing.
656+
#[lints(test_without_fail_case)]
657+
test_without_fail_case_include_indexing_as_fallible: bool = false,
658+
/// List of full paths of macros and functions, that we want to mark as "not going to fail".
659+
/// This allows us to make the lint more focused on actual short comings of our test suite
660+
/// by marking common routines non-fallible, even though they are fallible.
661+
#[lints(test_without_fail_case)]
662+
test_without_fail_case_non_fallible_paths: Vec<String> =
663+
DEFAULT_NONFALLIBLE_PATHS.iter().map(ToString::to_string).collect(),
631664
/// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
632665
#[lints(boxed_local, useless_vec)]
633666
too_large_for_stack: u64 = 200,
@@ -724,6 +757,14 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
724757
fn deserialize(file: &SourceFile) -> TryConf {
725758
match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) {
726759
Ok(mut conf) => {
760+
extend_vec_if_indicator_present(
761+
&mut conf.conf.test_without_fail_case_fallible_paths,
762+
DEFAULT_FALLIBLE_PATHS,
763+
);
764+
extend_vec_if_indicator_present(
765+
&mut conf.conf.test_without_fail_case_non_fallible_paths,
766+
DEFAULT_NONFALLIBLE_PATHS,
767+
);
727768
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
728769
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
729770
extend_vec_if_indicator_present(

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
691691
crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
692692
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
693693
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
694+
crate::test_without_fail_case::TEST_WITHOUT_FAIL_CASE_INFO,
694695
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
695696
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
696697
crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ mod swap;
346346
mod swap_ptr_to_ref;
347347
mod tabs_in_doc_comments;
348348
mod temporary_assignment;
349+
mod test_without_fail_case;
349350
mod tests_outside_test_module;
350351
mod to_digit_is_some;
351352
mod to_string_trait_impl;
@@ -949,5 +950,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
949950
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
950951
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
951952
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
953+
store.register_late_pass(|_| Box::new(test_without_fail_case::TestWithoutFailCase::new(conf)));
952954
// add lints here, do not remove this comment, it's used in `new_lint`
953955
}

0 commit comments

Comments
 (0)