rust/clippy_lints/src/excessive_nesting.rs

137 lines
3.6 KiB
Rust
Raw Normal View History

use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::{
2023-04-28 14:22:24 -05:00
visit::{walk_block, walk_item, Visitor},
Block, Crate, Inline, Item, ItemKind, ModKind,
};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
///
/// Checks for blocks which are indented beyond a certain threshold.
///
/// ### Why is this bad?
///
/// It can severely hinder readability. The default is very generous; if you
/// exceed this, it's a sign you should refactor.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// excessive-nesting-threshold = 3
/// ```
/// lib.rs:
/// ```rust,ignore
/// pub mod a {
/// pub struct X;
/// impl X {
/// pub fn run(&self) {
/// if true {
/// // etc...
/// }
/// }
/// }
/// }
/// Use instead:
/// a.rs:
/// ```rust,ignore
/// fn private_run(x: &X) {
/// if true {
/// // etc...
/// }
/// }
///
/// pub struct X;
/// impl X {
/// pub fn run(&self) {
/// private_run(self);
/// }
/// }
/// ```
#[clippy::version = "1.70.0"]
pub EXCESSIVE_NESTING,
restriction,
"checks for blocks nested beyond a certain threshold"
}
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
#[derive(Clone, Copy)]
pub struct ExcessiveNesting {
pub excessive_nesting_threshold: u64,
}
impl EarlyLintPass for ExcessiveNesting {
2023-04-28 14:22:24 -05:00
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
let conf = self;
2023-04-28 14:22:24 -05:00
let mut visitor = NestingVisitor {
conf,
cx,
nest_level: 0,
2023-04-28 14:22:24 -05:00
};
for item in &krate.items {
visitor.visit_item(item);
}
}
}
struct NestingVisitor<'conf, 'cx> {
conf: &'conf ExcessiveNesting,
cx: &'cx EarlyContext<'cx>,
nest_level: u64,
}
2023-04-28 14:22:24 -05:00
impl NestingVisitor<'_, '_> {
fn check_indent(&self, span: Span) -> bool {
if self.nest_level > self.conf.excessive_nesting_threshold && !in_external_macro(self.cx.sess(), span) {
span_lint_and_help(
self.cx,
EXCESSIVE_NESTING,
span,
"this block is too nested",
None,
"try refactoring your code to minimize nesting",
);
return true;
}
2023-04-28 14:22:24 -05:00
false
}
2023-04-28 14:22:24 -05:00
}
2023-04-28 14:22:24 -05:00
impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> {
fn visit_block(&mut self, block: &Block) {
self.nest_level += 1;
2023-04-28 14:22:24 -05:00
if !self.check_indent(block.span) {
walk_block(self, block);
}
self.nest_level -= 1;
}
fn visit_item(&mut self, item: &Item) {
match &item.kind {
2023-04-28 14:22:24 -05:00
ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
self.nest_level += 1;
2023-04-28 14:22:24 -05:00
if !self.check_indent(item.span) {
walk_item(self, item);
}
self.nest_level -= 1;
2023-04-28 14:22:24 -05:00
},
// Mod: Don't visit non-inline modules
// ForeignMod: I don't think this is necessary, but just incase let's not take any chances (don't want to
// cause any false positives)
ItemKind::Mod(..) | ItemKind::ForeignMod(..) => {},
_ => walk_item(self, item),
}
}
}