-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathcfg_not_test.rs
More file actions
65 lines (60 loc) · 2.3 KB
/
cfg_not_test.rs
File metadata and controls
65 lines (60 loc) · 2.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::attr::data_structures::CfgEntry;
use rustc_ast::{AttrItemKind, EarlyParsedAttribute};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#[cfg(not(test))]`)
///
/// ### Why is this bad?
/// This may give the false impression that a codebase has 100% coverage, yet actually has untested code.
/// Enabling this also guards against excessive mockery as well, which is an anti-pattern.
///
/// ### Example
/// ```rust
/// # fn important_check() {}
/// #[cfg(not(test))]
/// important_check(); // I'm not actually tested, but not including me will falsely increase coverage!
/// ```
/// Use instead:
/// ```rust
/// # fn important_check() {}
/// important_check();
/// ```
#[clippy::version = "1.81.0"]
pub CFG_NOT_TEST,
restriction,
"enforce against excluding code from test builds"
}
declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]);
impl EarlyLintPass for CfgNotTest {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
if attr.has_name(sym::cfg_trace) {
let AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)) = &attr.get_normal_item().args else {
unreachable!()
};
if contains_not_test(cfg, false) {
span_lint_and_then(
cx,
CFG_NOT_TEST,
attr.span,
"code is excluded from test builds",
|diag| {
diag.help("consider not excluding any code from test builds");
diag.note_once("this could increase code coverage despite not actually being tested");
},
);
}
}
}
}
fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool {
match cfg {
CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| contains_not_test(item, not)),
CfgEntry::Not(sub, _) => contains_not_test(sub, !not),
CfgEntry::NameValue { name: sym::test, .. } => not,
_ => false,
}
}