mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): access parent of ast nodes
This commit is contained in:
parent
f72c96270e
commit
f3ca02f596
7 changed files with 37 additions and 18 deletions
|
|
@ -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<Semantic<'a>>,
|
||||
|
||||
|
|
@ -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<T: Into<Error>>(&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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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<T: Into<NodeId>>(&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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue