mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(semantic): add ancestor_kinds iterator function (#7217)
for convenience, I've added a new function called `ancestor_kinds` which loops overall `ancestors` and gets their `AstKind`. this is a common pattern in a couple of places. I also did some somewhat related refactoring to remove places where we were manually calling `AstNode::kind` instead of using `ancestor_kinds` or calling `parent_kind`.
This commit is contained in:
parent
abf1602f33
commit
c5485ae7ee
14 changed files with 37 additions and 34 deletions
|
|
@ -259,10 +259,10 @@ pub fn nth_outermost_paren_parent<'a, 'b>(
|
|||
pub fn iter_outer_expressions<'a, 's>(
|
||||
semantic: &'s Semantic<'a>,
|
||||
node_id: NodeId,
|
||||
) -> impl Iterator<Item = &'s AstNode<'a>> + 's {
|
||||
semantic.nodes().ancestors(node_id).skip(1).filter(|parent| {
|
||||
) -> impl Iterator<Item = AstKind<'a>> + 's {
|
||||
semantic.nodes().ancestor_kinds(node_id).skip(1).filter(|parent| {
|
||||
!matches!(
|
||||
parent.kind(),
|
||||
parent,
|
||||
AstKind::ParenthesizedExpression(_)
|
||||
| AstKind::TSAsExpression(_)
|
||||
| AstKind::TSSatisfiesExpression(_)
|
||||
|
|
|
|||
|
|
@ -479,7 +479,7 @@ impl Rule for FuncNames {
|
|||
}
|
||||
|
||||
fn guess_function_name<'a>(ctx: &LintContext<'a>, parent_id: NodeId) -> Option<Cow<'a, str>> {
|
||||
for parent_kind in ctx.nodes().ancestors(parent_id).map(AstNode::kind) {
|
||||
for parent_kind in ctx.nodes().ancestor_kinds(parent_id) {
|
||||
match parent_kind {
|
||||
AstKind::ParenthesizedExpression(_)
|
||||
| AstKind::TSAsExpression(_)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ fn get_function_name_and_kind<'a>(
|
|||
node: &AstNode<'a>,
|
||||
ctx: &LintContext<'a>,
|
||||
) -> (&'static str, Option<Cow<'a, str>>) {
|
||||
for parent in ctx.nodes().ancestors(node.id()).skip(1).map(AstNode::kind) {
|
||||
for parent in ctx.nodes().ancestor_kinds(node.id()).skip(1) {
|
||||
match parent {
|
||||
AstKind::Function(f) => {
|
||||
if let Some(name) = f.name() {
|
||||
|
|
|
|||
|
|
@ -62,16 +62,15 @@ impl Rule for NoNewFunc {
|
|||
Some((obj_id, call_expr.span))
|
||||
}
|
||||
AstKind::MemberExpression(mem_expr) => {
|
||||
let parent: Option<&AstNode<'a>> =
|
||||
ctx.nodes().ancestors(node.id()).skip(1).find(|node| {
|
||||
let parent: Option<AstKind<'a>> =
|
||||
ctx.nodes().ancestor_kinds(node.id()).skip(1).find(|node| {
|
||||
!matches!(
|
||||
node.kind(),
|
||||
node,
|
||||
AstKind::ChainExpression(_) | AstKind::ParenthesizedExpression(_)
|
||||
)
|
||||
});
|
||||
|
||||
let Some(AstKind::CallExpression(parent_call_expr)) = parent.map(AstNode::kind)
|
||||
else {
|
||||
let Some(AstKind::CallExpression(parent_call_expr)) = parent else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! This module checks if an unused variable is allowed. Note that this does not
|
||||
//! consider variables ignored by name pattern, but by where they are declared.
|
||||
use oxc_ast::{ast::*, AstKind};
|
||||
use oxc_semantic::{AstNode, NodeId, Semantic};
|
||||
use oxc_semantic::{NodeId, Semantic};
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
use super::{options::ArgsOption, NoUnusedVars, Symbol};
|
||||
|
|
@ -255,7 +255,7 @@ impl NoUnusedVars {
|
|||
param: &FormalParameter<'a>,
|
||||
params_id: NodeId,
|
||||
) -> bool {
|
||||
let mut parents_iter = semantic.nodes().ancestors(params_id).skip(1).map(AstNode::kind);
|
||||
let mut parents_iter = semantic.nodes().ancestor_kinds(params_id).skip(1);
|
||||
|
||||
// in function declarations, the parent immediately before the
|
||||
// FormalParameters is a TSDeclareBlock
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use oxc_ast::{
|
|||
ast::{Expression, VariableDeclarator},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_semantic::{AstNode, NodeId};
|
||||
use oxc_semantic::NodeId;
|
||||
use oxc_span::CompactStr;
|
||||
|
||||
use super::{count_whitespace_or_commas, BindingInfo, NoUnusedVars, Symbol};
|
||||
|
|
@ -36,7 +36,7 @@ impl NoUnusedVars {
|
|||
return fixer.noop();
|
||||
}
|
||||
|
||||
let Some(parent) = symbol.nodes().parent_node(decl_id).map(AstNode::kind) else {
|
||||
let Some(parent) = symbol.nodes().parent_kind(decl_id) else {
|
||||
#[cfg(debug_assertions)]
|
||||
panic!("VariableDeclarator nodes should always have a parent node");
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
|
|
|||
|
|
@ -124,8 +124,7 @@ impl<'s, 'a> Symbol<'s, 'a> {
|
|||
{
|
||||
let parents_iter = self
|
||||
.nodes()
|
||||
.ancestors(node_id)
|
||||
.map(AstNode::kind)
|
||||
.ancestor_kinds(node_id)
|
||||
// no skip
|
||||
.filter(|kind| Self::is_relevant_kind(*kind));
|
||||
|
||||
|
|
|
|||
|
|
@ -187,9 +187,7 @@ impl Rule for AltText {
|
|||
// <object>
|
||||
if let Some(custom_tags) = &self.object {
|
||||
if name == "object" || custom_tags.iter().any(|i| i == name) {
|
||||
let maybe_parent =
|
||||
ctx.nodes().parent_node(node.id()).map(oxc_semantic::AstNode::kind);
|
||||
if let Some(AstKind::JSXElement(parent)) = maybe_parent {
|
||||
if let Some(AstKind::JSXElement(parent)) = ctx.nodes().parent_kind(node.id()) {
|
||||
object_rule(jsx_el, parent, ctx);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,8 +102,7 @@ impl Rule for HeadingHasContent {
|
|||
return;
|
||||
}
|
||||
|
||||
let maybe_parent = ctx.nodes().parent_node(node.id()).map(oxc_semantic::AstNode::kind);
|
||||
if let Some(AstKind::JSXElement(parent)) = maybe_parent {
|
||||
if let Some(AstKind::JSXElement(parent)) = ctx.nodes().parent_kind(node.id()) {
|
||||
if object_has_accessible_child(ctx, parent) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ impl Rule for NoExportsAssign {
|
|||
};
|
||||
}
|
||||
|
||||
let parent = ctx.nodes().parent_node(node.id());
|
||||
if let Some(AstKind::AssignmentExpression(assign_expr)) = parent.map(AstNode::kind) {
|
||||
if let Some(AstKind::AssignmentExpression(assign_expr)) = ctx.nodes().parent_kind(node.id())
|
||||
{
|
||||
if is_module_exports(assign_expr.left.as_member_expression(), ctx) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,9 +269,7 @@ impl Rule for ExplicitLengthCheck {
|
|||
self.report(ctx, ancestor, is_negative, static_member_expr, true);
|
||||
return;
|
||||
}
|
||||
let parent = ctx.nodes().parent_node(node.id());
|
||||
let kind = parent.map(AstNode::kind);
|
||||
match kind {
|
||||
match ctx.nodes().parent_kind(node.id()) {
|
||||
Some(AstKind::LogicalExpression(LogicalExpression {
|
||||
operator, right, ..
|
||||
})) if *operator == LogicalOperator::And
|
||||
|
|
|
|||
|
|
@ -180,14 +180,14 @@ impl Rule for NoNull {
|
|||
};
|
||||
|
||||
let mut parents = iter_outer_expressions(ctx, node.id());
|
||||
let Some(parent_kind) = parents.next().map(AstNode::kind) else {
|
||||
let Some(parent_kind) = parents.next() else {
|
||||
ctx.diagnostic_with_fix(no_null_diagnostic(null_literal.span), |fixer| {
|
||||
fix_null(fixer, null_literal)
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
let grandparent_kind = parents.next().map(AstNode::kind);
|
||||
let grandparent_kind = parents.next();
|
||||
match (parent_kind, grandparent_kind) {
|
||||
(AstKind::Argument(_), Some(AstKind::CallExpression(call_expr)))
|
||||
if match_call_expression_pass_case(null_literal, call_expr) =>
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ impl Rule for PreferDomNodeTextContent {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut ancestor_kinds =
|
||||
ctx.nodes().ancestors(node.id()).skip(1).map(AstNode::kind);
|
||||
let mut ancestor_kinds = ctx.nodes().ancestor_kinds(node.id()).skip(1);
|
||||
let (Some(parent_node_kind), Some(grand_parent_node_kind)) =
|
||||
(ancestor_kinds.next(), ancestor_kinds.next())
|
||||
else {
|
||||
|
|
@ -87,8 +86,7 @@ impl Rule for PreferDomNodeTextContent {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut ancestor_kinds =
|
||||
ctx.nodes().ancestors(node.id()).skip(1).map(AstNode::kind);
|
||||
let mut ancestor_kinds = ctx.nodes().ancestor_kinds(node.id()).skip(1);
|
||||
let (Some(parent_node_kind), Some(grand_parent_node_kind)) =
|
||||
(ancestor_kinds.next(), ancestor_kinds.next())
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl<'a> AstNodes<'a> {
|
|||
self.nodes.is_empty()
|
||||
}
|
||||
|
||||
/// Walk up the AST, iterating over each parent node.
|
||||
/// Walk up the AST, iterating over each parent [`AstNode`].
|
||||
///
|
||||
/// The first node produced by this iterator is the first parent of the node
|
||||
/// pointed to by `node_id`. The last node will usually be a `Program`.
|
||||
|
|
@ -136,6 +136,18 @@ impl<'a> AstNodes<'a> {
|
|||
AstNodeParentIter { current_node_id: Some(node_id), nodes: self }
|
||||
}
|
||||
|
||||
/// Walk up the AST, iterating over each parent [`AstKind`].
|
||||
///
|
||||
/// The first node produced by this iterator is the first parent of the node
|
||||
/// pointed to by `node_id`. The last node will is a [`AstKind::Program`].
|
||||
#[inline]
|
||||
pub fn ancestor_kinds(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> impl Iterator<Item = AstKind<'a>> + Clone + '_ {
|
||||
self.ancestors(node_id).map(AstNode::kind)
|
||||
}
|
||||
|
||||
/// Access the underlying struct from [`oxc_ast`].
|
||||
///
|
||||
/// ## Example
|
||||
|
|
@ -214,7 +226,7 @@ impl<'a> AstNodes<'a> {
|
|||
self.root().map(|id| self.get_node_mut(id))
|
||||
}
|
||||
|
||||
/// Walk up the AST, iterating over each parent node.
|
||||
/// Walk up the AST, iterating over each parent [`NodeId`].
|
||||
///
|
||||
/// The first node produced by this iterator is the first parent of the node
|
||||
/// pointed to by `node_id`. The last node will always be a [`Program`].
|
||||
|
|
|
|||
Loading…
Reference in a new issue