feat(linter): access parent of ast nodes

This commit is contained in:
Boshen 2023-02-26 11:06:04 +08:00
parent f72c96270e
commit f3ca02f596
7 changed files with 37 additions and 18 deletions

View file

@ -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)
}
}

View file

@ -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);
}
}

View file

@ -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>);
}

View file

@ -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),
}
}
}

View file

@ -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));
}
}

View file

@ -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));

View file

@ -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))
}
}