Skip to content

Commit 476efe9

Browse files
committed
Auto merge of #10672 - Centri3:excessive-width-lints, r=Alexendoo
Add `excessive_nesting` lint changelog: new lint [`excessive_nesting`]
2 parents 05de787 + 6afb355 commit 476efe9

File tree

12 files changed

+1193
-1
lines changed

12 files changed

+1193
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4759,6 +4759,7 @@ Released 2018-09-13
47594759
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
47604760
[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
47614761
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
4762+
[`excessive_nesting`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting
47624763
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
47634764
[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
47644765
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs

book/src/lint_configuration.md

+10
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ The maximum cognitive complexity a function can have
158158
* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
159159

160160

161+
## `excessive-nesting-threshold`
162+
The maximum amount of nesting a block can reside in
163+
164+
**Default Value:** `0` (`u64`)
165+
166+
---
167+
**Affected lints:**
168+
* [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting)
169+
170+
161171
## `disallowed-names`
162172
The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
163173
`".."` can be used as part of the list to indicate, that the configured values should be appended to the

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
160160
crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
161161
crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO,
162162
crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO,
163+
crate::excessive_nesting::EXCESSIVE_NESTING_INFO,
163164
crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO,
164165
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
165166
crate::exit::EXIT_INFO,

clippy_lints/src/excessive_nesting.rs

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use clippy_utils::{diagnostics::span_lint_and_help, source::snippet};
2+
use rustc_ast::{
3+
node_id::NodeSet,
4+
visit::{walk_block, walk_item, Visitor},
5+
Block, Crate, Inline, Item, ItemKind, ModKind, NodeId,
6+
};
7+
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
8+
use rustc_middle::lint::in_external_macro;
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::Span;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks for blocks which are nested beyond a certain threshold.
15+
///
16+
/// Note: Even though this lint is warn-by-default, it will only trigger if a maximum nesting level is defined in the clippy.toml file.
17+
///
18+
/// ### Why is this bad?
19+
/// It can severely hinder readability.
20+
///
21+
/// ### Example
22+
/// An example clippy.toml configuration:
23+
/// ```toml
24+
/// # clippy.toml
25+
/// excessive-nesting-threshold = 3
26+
/// ```
27+
/// lib.rs:
28+
/// ```rust,ignore
29+
/// pub mod a {
30+
/// pub struct X;
31+
/// impl X {
32+
/// pub fn run(&self) {
33+
/// if true {
34+
/// // etc...
35+
/// }
36+
/// }
37+
/// }
38+
/// }
39+
/// Use instead:
40+
/// a.rs:
41+
/// ```rust,ignore
42+
/// fn private_run(x: &X) {
43+
/// if true {
44+
/// // etc...
45+
/// }
46+
/// }
47+
///
48+
/// pub struct X;
49+
/// impl X {
50+
/// pub fn run(&self) {
51+
/// private_run(self);
52+
/// }
53+
/// }
54+
/// ```
55+
/// lib.rs:
56+
/// ```rust,ignore
57+
/// pub mod a;
58+
/// ```
59+
#[clippy::version = "1.70.0"]
60+
pub EXCESSIVE_NESTING,
61+
complexity,
62+
"checks for blocks nested beyond a certain threshold"
63+
}
64+
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
65+
66+
#[derive(Clone)]
67+
pub struct ExcessiveNesting {
68+
pub excessive_nesting_threshold: u64,
69+
pub nodes: NodeSet,
70+
}
71+
72+
impl ExcessiveNesting {
73+
pub fn check_node_id(&self, cx: &EarlyContext<'_>, span: Span, node_id: NodeId) {
74+
if self.nodes.contains(&node_id) {
75+
span_lint_and_help(
76+
cx,
77+
EXCESSIVE_NESTING,
78+
span,
79+
"this block is too nested",
80+
None,
81+
"try refactoring your code to minimize nesting",
82+
);
83+
}
84+
}
85+
}
86+
87+
impl EarlyLintPass for ExcessiveNesting {
88+
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
89+
if self.excessive_nesting_threshold == 0 {
90+
return;
91+
}
92+
93+
let mut visitor = NestingVisitor {
94+
conf: self,
95+
cx,
96+
nest_level: 0,
97+
};
98+
99+
for item in &krate.items {
100+
visitor.visit_item(item);
101+
}
102+
}
103+
104+
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
105+
self.check_node_id(cx, block.span, block.id);
106+
}
107+
108+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
109+
self.check_node_id(cx, item.span, item.id);
110+
}
111+
}
112+
113+
struct NestingVisitor<'conf, 'cx> {
114+
conf: &'conf mut ExcessiveNesting,
115+
cx: &'cx EarlyContext<'cx>,
116+
nest_level: u64,
117+
}
118+
119+
impl NestingVisitor<'_, '_> {
120+
fn check_indent(&mut self, span: Span, id: NodeId) -> bool {
121+
if self.nest_level > self.conf.excessive_nesting_threshold && !in_external_macro(self.cx.sess(), span) {
122+
self.conf.nodes.insert(id);
123+
124+
return true;
125+
}
126+
127+
false
128+
}
129+
}
130+
131+
impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> {
132+
fn visit_block(&mut self, block: &Block) {
133+
if block.span.from_expansion() {
134+
return;
135+
}
136+
137+
// TODO: This should be rewritten using `LateLintPass` so we can use `is_from_proc_macro` instead,
138+
// but for now, this is fine.
139+
let snippet = snippet(self.cx, block.span, "{}").trim().to_owned();
140+
if !snippet.starts_with('{') || !snippet.ends_with('}') {
141+
return;
142+
}
143+
144+
self.nest_level += 1;
145+
146+
if !self.check_indent(block.span, block.id) {
147+
walk_block(self, block);
148+
}
149+
150+
self.nest_level -= 1;
151+
}
152+
153+
fn visit_item(&mut self, item: &Item) {
154+
if item.span.from_expansion() {
155+
return;
156+
}
157+
158+
match &item.kind {
159+
ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
160+
self.nest_level += 1;
161+
162+
if !self.check_indent(item.span, item.id) {
163+
walk_item(self, item);
164+
}
165+
166+
self.nest_level -= 1;
167+
},
168+
// Reset nesting level for non-inline modules (since these are in another file)
169+
ItemKind::Mod(..) => walk_item(
170+
&mut NestingVisitor {
171+
conf: self.conf,
172+
cx: self.cx,
173+
nest_level: 0,
174+
},
175+
item,
176+
),
177+
_ => walk_item(self, item),
178+
}
179+
}
180+
}

clippy_lints/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ mod equatable_if_let;
123123
mod escape;
124124
mod eta_reduction;
125125
mod excessive_bools;
126+
mod excessive_nesting;
126127
mod exhaustive_items;
127128
mod exit;
128129
mod explicit_write;
@@ -1013,6 +1014,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10131014
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
10141015
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
10151016
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
1017+
let excessive_nesting_threshold = conf.excessive_nesting_threshold;
1018+
store.register_early_pass(move || {
1019+
Box::new(excessive_nesting::ExcessiveNesting {
1020+
excessive_nesting_threshold,
1021+
nodes: rustc_ast::node_id::NodeSet::new(),
1022+
})
1023+
});
10161024
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
10171025
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
10181026
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));

clippy_lints/src/utils/conf.rs

+4
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ define_Conf! {
305305
///
306306
/// The maximum cognitive complexity a function can have
307307
(cognitive_complexity_threshold: u64 = 25),
308+
/// Lint: EXCESSIVE_NESTING.
309+
///
310+
/// The maximum amount of nesting a block can reside in
311+
(excessive_nesting_threshold: u64 = 0),
308312
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
309313
///
310314
/// Use the Cognitive Complexity lint instead.

0 commit comments

Comments
 (0)