diff --git a/crates/oxc_linter/src/context.rs b/crates/oxc_linter/src/context.rs index 2412392a1..f89ace8a5 100644 --- a/crates/oxc_linter/src/context.rs +++ b/crates/oxc_linter/src/context.rs @@ -1,8 +1,11 @@ use std::{cell::RefCell, rc::Rc}; +use oxc_ast::AstKind; use oxc_diagnostics::Error; use oxc_semantic::Semantic; +use crate::AstNode; + pub struct LintContext<'a> { semantic: Rc>, @@ -18,7 +21,6 @@ impl<'a> LintContext<'a> { self.diagnostics.into_inner() } - #[allow(unused)] pub fn semantic(&self) -> &Semantic<'a> { &self.semantic } @@ -26,4 +28,8 @@ impl<'a> LintContext<'a> { pub fn diagnostic>(&self, diagnostic: T) { self.diagnostics.borrow_mut().push(diagnostic.into()); } + + pub fn parent_kind(&self, node: &AstNode<'a>) -> AstKind<'a> { + self.semantic().nodes().parent_kind(node) + } } diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index dc15ea997..36abaa084 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -8,6 +8,7 @@ mod rules; use std::rc::Rc; use oxc_diagnostics::Error; +pub(crate) use oxc_semantic::AstNode; use oxc_semantic::Semantic; use crate::{context::LintContext, rules::RuleEnum}; @@ -36,7 +37,7 @@ impl Linter { for node in semantic.nodes().iter() { for rule in &self.rules { - rule.run(node.get().kind(), &ctx); + rule.run(node, &ctx); } } diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs index da4211ffa..3a2af05d5 100644 --- a/crates/oxc_linter/src/rule.rs +++ b/crates/oxc_linter/src/rule.rs @@ -1,9 +1,7 @@ use std::fmt::Debug; -use oxc_ast::AstKind; - -use crate::context::LintContext; +use crate::{context::LintContext, AstNode}; pub trait Rule: Sized + Default + Debug { - fn run<'a>(&self, kind: AstKind<'a>, _ctx: &LintContext<'a>); + fn run<'a>(&self, node: &AstNode<'a>, _ctx: &LintContext<'a>); } diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index e1083f21d..cf219fc07 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -3,9 +3,8 @@ mod no_empty; pub use no_debugger::NoDebugger; pub use no_empty::NoEmpty; -use oxc_ast::AstKind; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, rule::Rule, AstNode}; #[derive(Debug, Clone)] pub enum RuleEnum { @@ -14,10 +13,10 @@ pub enum RuleEnum { } impl RuleEnum { - pub fn run<'a>(&self, kind: AstKind<'a>, ctx: &LintContext<'a>) { + pub fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match self { - Self::NoDebugger(rule) => rule.run(kind, ctx), - Self::NoEmpty(rule) => rule.run(kind, ctx), + Self::NoDebugger(rule) => rule.run(node, ctx), + Self::NoEmpty(rule) => rule.run(node, ctx), } } } diff --git a/crates/oxc_linter/src/rules/no_debugger.rs b/crates/oxc_linter/src/rules/no_debugger.rs index beea9b11c..c80b84809 100644 --- a/crates/oxc_linter/src/rules/no_debugger.rs +++ b/crates/oxc_linter/src/rules/no_debugger.rs @@ -4,7 +4,7 @@ use oxc_diagnostics::{ thiserror::Error, }; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, rule::Rule, AstNode}; #[derive(Debug, Error, Diagnostic)] #[error("eslint(no-debugger): `debugger` statement is not allowed")] @@ -15,8 +15,8 @@ struct NoDebuggerDiagnostic(#[label] pub Span); pub struct NoDebugger; impl Rule for NoDebugger { - fn run<'a>(&self, kind: AstKind<'a>, ctx: &LintContext<'a>) { - if let AstKind::DebuggerStatement(stmt) = kind { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::DebuggerStatement(stmt) = node.get().kind() { ctx.diagnostic(NoDebuggerDiagnostic(stmt.span)); } } diff --git a/crates/oxc_linter/src/rules/no_empty.rs b/crates/oxc_linter/src/rules/no_empty.rs index 8080e6c06..86bc475a7 100644 --- a/crates/oxc_linter/src/rules/no_empty.rs +++ b/crates/oxc_linter/src/rules/no_empty.rs @@ -4,7 +4,7 @@ use oxc_diagnostics::{ thiserror::Error, }; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, rule::Rule, AstNode}; #[derive(Debug, Error, Diagnostic)] #[error("eslint(no-empty): Disallow empty block statements")] @@ -15,11 +15,13 @@ struct NoEmptyDiagnostic(&'static str, #[label("Empty {0} statement")] pub Span) pub struct NoEmpty; impl Rule for NoEmpty { - fn run<'a>(&self, kind: AstKind<'a>, ctx: &LintContext<'a>) { - match kind { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.get().kind() { AstKind::BlockStatement(block) if block.body.is_empty() => { // TODO: check comment - ctx.diagnostic(NoEmptyDiagnostic("block", block.span)); + if !matches!(ctx.parent_kind(node), AstKind::CatchClause(_)) { + ctx.diagnostic(NoEmptyDiagnostic("block", block.span)); + } } AstKind::SwitchStatement(switch) if switch.cases.is_empty() => { ctx.diagnostic(NoEmptyDiagnostic("switch", switch.span)); diff --git a/crates/oxc_semantic/src/node/tree.rs b/crates/oxc_semantic/src/node/tree.rs index 55d8982e4..bb79920a4 100644 --- a/crates/oxc_semantic/src/node/tree.rs +++ b/crates/oxc_semantic/src/node/tree.rs @@ -1,6 +1,7 @@ use std::ops::{Deref, DerefMut, Index, IndexMut}; use indextree::{Arena, NodeId}; +use oxc_ast::AstKind; use super::{AstNode, AstNodeId, SemanticNode}; @@ -54,3 +55,15 @@ impl<'a> DerefMut for AstNodes<'a> { &mut self.nodes } } + +impl<'a> AstNodes<'a> { + #[must_use] + pub fn kind>(&self, id: T) -> AstKind<'a> { + self.nodes[id.into()].get().kind + } + + #[must_use] + pub fn parent_kind(&self, node: &AstNode<'a>) -> AstKind<'a> { + node.parent().map_or(AstKind::Root, |node_id| self.kind(node_id)) + } +}