summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs')
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
new file mode 100644
index 000000000..0a0a77082
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -0,0 +1,71 @@
+use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function};
+use rustc_hir::{intravisit::FnKind, Body, FnDecl};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{def_id::LocalDefId, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
+ /// (marked with `#[cfg(test)]`).
+ /// ### Why is this bad?
+ /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
+ /// having test functions outside of this module is confusing and may lead to them being "hidden".
+ /// ### Example
+ /// ```rust
+ /// #[test]
+ /// fn my_cool_test() {
+ /// // [...]
+ /// }
+ ///
+ /// #[cfg(test)]
+ /// mod tests {
+ /// // [...]
+ /// }
+ ///
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[cfg(test)]
+ /// mod tests {
+ /// #[test]
+ /// fn my_cool_test() {
+ /// // [...]
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub TESTS_OUTSIDE_TEST_MODULE,
+ restriction,
+ "A test function is outside the testing module."
+}
+
+declare_lint_pass!(TestsOutsideTestModule => [TESTS_OUTSIDE_TEST_MODULE]);
+
+impl LateLintPass<'_> for TestsOutsideTestModule {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'_>,
+ kind: FnKind<'_>,
+ _: &FnDecl<'_>,
+ body: &Body<'_>,
+ sp: Span,
+ _: LocalDefId,
+ ) {
+ if_chain! {
+ if !matches!(kind, FnKind::Closure);
+ if is_in_test_function(cx.tcx, body.id().hir_id);
+ if !is_in_cfg_test(cx.tcx, body.id().hir_id);
+ then {
+ span_lint_and_note(
+ cx,
+ TESTS_OUTSIDE_TEST_MODULE,
+ sp,
+ "this function marked with #[test] is outside a #[cfg(test)] module",
+ None,
+ "move it to a testing module marked with #[cfg(test)]",
+ );
+ }
+ }
+ }
+}