react_auditor/rules/quality/
no_deep_nesting.rs1use oxc_ast::ast::Program;
2use oxc_ast_visit::Visit;
3use oxc_ast_visit::walk::walk_statement;
4use oxc_semantic::Semantic;
5use oxc_span::GetSpan;
6
7use crate::rules::{Rule, RuleFinding, RuleMeta, Severity};
8
9pub struct NoDeepNesting;
10
11const RULE_META: RuleMeta = RuleMeta {
12 id: "no-deep-nesting",
13 default_severity: Severity::Warning,
14 category: "quality",
15 description: "Avoid nesting deeper than 4 levels",
16};
17
18const MAX_DEPTH: usize = 4;
19
20impl Rule for NoDeepNesting {
21 fn meta(&self) -> &RuleMeta {
22 &RULE_META
23 }
24
25 fn run(&self, program: &Program, _semantic: &Semantic, source_text: &str) -> Vec<RuleFinding> {
26 let mut collector = NestingCollector {
27 findings: Vec::new(),
28 source: source_text,
29 depth: 0,
30 };
31 collector.visit_program(program);
32 collector.findings
33 }
34}
35
36struct NestingCollector<'a> {
37 findings: Vec<RuleFinding>,
38 source: &'a str,
39 depth: usize,
40}
41
42impl<'a> Visit<'a> for NestingCollector<'a> {
43 fn visit_statement(&mut self, stmt: &oxc_ast::ast::Statement<'a>) {
44 let is_nesting = matches!(
45 stmt,
46 oxc_ast::ast::Statement::IfStatement(_)
47 | oxc_ast::ast::Statement::ForStatement(_)
48 | oxc_ast::ast::Statement::ForInStatement(_)
49 | oxc_ast::ast::Statement::ForOfStatement(_)
50 | oxc_ast::ast::Statement::WhileStatement(_)
51 | oxc_ast::ast::Statement::DoWhileStatement(_)
52 | oxc_ast::ast::Statement::SwitchStatement(_)
53 );
54
55 if is_nesting {
56 self.depth += 1;
57 if self.depth > MAX_DEPTH {
58 let start = stmt.span().start as usize;
59 let line = self.source[..start].lines().count().max(1);
60 let col = start - self.source[..start].rfind('\n').map(|i| i + 1).unwrap_or(0);
61
62 self.findings.push(RuleFinding {
63 line,
64 column: col + 1,
65 message: format!(
66 "Nesting depth {depth} exceeds max {MAX_DEPTH}",
67 depth = self.depth
68 ),
69 });
70 }
71 walk_statement(self, stmt);
72 self.depth -= 1;
73 } else {
74 walk_statement(self, stmt);
75 }
76 }
77}