Skip to content

Commit 0884d16

Browse files
committed
Auto merge of #10578 - blyxyas:items_after_test_module, r=dswij
Add `items_after_test_module` lint Resolves task *3* of #10506, alongside *1* resolved at #10543 in an effort to help standarize a little bit more testing modules. --- changelog:[`items_after_test_module`]: Added the lint.
2 parents 9283497 + 0c6da4c commit 0884d16

File tree

6 files changed

+127
-0
lines changed

6 files changed

+127
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4730,6 +4730,7 @@ Released 2018-09-13
47304730
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
47314731
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
47324732
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
4733+
[`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module
47334734
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
47344735
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
47354736
[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
215215
crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
216216
crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO,
217217
crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
218+
crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO,
218219
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
219220
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
220221
crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use clippy_utils::{diagnostics::span_lint_and_help, is_in_cfg_test};
2+
use rustc_hir::{HirId, ItemId, ItemKind, Mod};
3+
use rustc_lint::{LateContext, LateLintPass, LintContext};
4+
use rustc_middle::lint::in_external_macro;
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_span::{sym, Span};
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Triggers if an item is declared after the testing module marked with `#[cfg(test)]`.
11+
/// ### Why is this bad?
12+
/// Having items declared after the testing module is confusing and may lead to bad test coverage.
13+
/// ### Example
14+
/// ```rust
15+
/// #[cfg(test)]
16+
/// mod tests {
17+
/// // [...]
18+
/// }
19+
///
20+
/// fn my_function() {
21+
/// // [...]
22+
/// }
23+
/// ```
24+
/// Use instead:
25+
/// ```rust
26+
/// fn my_function() {
27+
/// // [...]
28+
/// }
29+
///
30+
/// #[cfg(test)]
31+
/// mod tests {
32+
/// // [...]
33+
/// }
34+
/// ```
35+
#[clippy::version = "1.70.0"]
36+
pub ITEMS_AFTER_TEST_MODULE,
37+
style,
38+
"An item was found after the testing module `tests`"
39+
}
40+
41+
declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]);
42+
43+
impl LateLintPass<'_> for ItemsAfterTestModule {
44+
fn check_mod(&mut self, cx: &LateContext<'_>, _: &Mod<'_>, _: HirId) {
45+
let mut was_test_mod_visited = false;
46+
let mut test_mod_span: Option<Span> = None;
47+
48+
let hir = cx.tcx.hir();
49+
let items = hir.items().collect::<Vec<ItemId>>();
50+
51+
for (i, itid) in items.iter().enumerate() {
52+
let item = hir.item(*itid);
53+
54+
if_chain! {
55+
if was_test_mod_visited;
56+
if i == (items.len() - 3 /* Weird magic number (HIR-translation behaviour) */);
57+
if cx.sess().source_map().lookup_char_pos(item.span.lo()).file.name_hash
58+
== cx.sess().source_map().lookup_char_pos(test_mod_span.unwrap().lo()).file.name_hash; // Will never fail
59+
if !matches!(item.kind, ItemKind::Mod(_));
60+
if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself
61+
if !in_external_macro(cx.sess(), item.span);
62+
63+
then {
64+
span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined");
65+
}};
66+
67+
if matches!(item.kind, ItemKind::Mod(_)) {
68+
for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) {
69+
if_chain! {
70+
if attr.has_name(sym::cfg);
71+
if let Some(mitems) = attr.meta_item_list();
72+
if let [mitem] = &*mitems;
73+
if mitem.has_name(sym::test);
74+
then {
75+
was_test_mod_visited = true;
76+
test_mod_span = Some(item.span);
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ mod int_plus_one;
158158
mod invalid_upcast_comparisons;
159159
mod invalid_utf8_in_unchecked;
160160
mod items_after_statements;
161+
mod items_after_test_module;
161162
mod iter_not_returning_iterator;
162163
mod large_const_arrays;
163164
mod large_enum_variant;
@@ -961,6 +962,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
961962
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
962963
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
963964
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
965+
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
964966
// add lints here, do not remove this comment, it's used in `new_lint`
965967
}
966968

tests/ui/items_after_test_module.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// compile-flags: --test
2+
#![allow(unused)]
3+
#![warn(clippy::items_after_test_module)]
4+
5+
fn main() {}
6+
7+
fn should_not_lint() {}
8+
9+
#[allow(dead_code)]
10+
#[allow(unused)] // Some attributes to check that span replacement is good enough
11+
#[allow(clippy::allow_attributes)]
12+
#[cfg(test)]
13+
mod tests {
14+
#[test]
15+
fn hi() {}
16+
}
17+
18+
fn should_lint() {}
19+
20+
const SHOULD_ALSO_LINT: usize = 1;
21+
macro_rules! should_not_lint {
22+
() => {};
23+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: items were found after the testing module
2+
--> $DIR/items_after_test_module.rs:13:1
3+
|
4+
LL | / mod tests {
5+
LL | | #[test]
6+
LL | | fn hi() {}
7+
LL | | }
8+
... |
9+
LL | | () => {};
10+
LL | | }
11+
| |_^
12+
|
13+
= help: move the items to before the testing module was defined
14+
= note: `-D clippy::items-after-test-module` implied by `-D warnings`
15+
16+
error: aborting due to previous error
17+

0 commit comments

Comments
 (0)