From f966fefe3a6cadc34294212d6fd43110a29f9782 Mon Sep 17 00:00:00 2001 From: Ulrich Stark Date: Wed, 9 Jul 2025 21:10:43 +0200 Subject: [PATCH] perf(semantic,linter): simplify implementation and uses of ancestors iterators --- crates/oxc_linter/src/ast_util.rs | 6 ++-- .../oxc_linter/src/rules/eslint/func_names.rs | 8 ++--- .../oxc_linter/src/rules/eslint/func_style.rs | 1 - .../src/rules/eslint/init_declarations.rs | 2 +- .../oxc_linter/src/rules/eslint/max_depth.rs | 2 +- .../src/rules/eslint/max_nested_callbacks.rs | 4 +-- .../src/rules/eslint/no_cond_assign.rs | 2 +- .../oxc_linter/src/rules/eslint/no_console.rs | 2 +- .../src/rules/eslint/no_constructor_return.rs | 2 +- .../src/rules/eslint/no_debugger.rs | 1 - .../src/rules/eslint/no_empty_function.rs | 2 +- crates/oxc_linter/src/rules/eslint/no_eval.rs | 2 +- .../src/rules/eslint/no_extend_native.rs | 4 +-- .../src/rules/eslint/no_extra_boolean_cast.rs | 22 +++++--------- .../oxc_linter/src/rules/eslint/no_labels.rs | 3 +- crates/oxc_linter/src/rules/eslint/no_new.rs | 14 ++++----- .../src/rules/eslint/no_new_func.rs | 13 ++++---- .../eslint/no_unused_private_class_members.rs | 5 ++-- .../rules/eslint/no_unused_vars/allowed.rs | 2 +- .../src/rules/eslint/no_unused_vars/symbol.rs | 20 ++++++------- .../src/rules/eslint/no_unused_vars/usage.rs | 4 +-- .../rules/eslint/no_useless_constructor.rs | 3 +- .../src/rules/import/no_commonjs.rs | 2 +- .../src/rules/jest/no_duplicate_hooks.rs | 13 ++++---- .../oxc_linter/src/rules/jest/prefer_each.rs | 2 +- .../src/rules/nextjs/no_img_element.rs | 3 +- .../src/rules/oxc/bad_comparison_sequence.rs | 12 ++++---- .../oxc_linter/src/rules/oxc/missing_throw.rs | 2 +- .../rules/promise/no_callback_in_promise.rs | 9 ++---- .../src/rules/promise/no_nesting.rs | 12 ++++---- .../src/rules/promise/no_return_wrap.rs | 2 +- .../promise/prefer_await_to_callbacks.rs | 2 +- .../src/rules/promise/prefer_await_to_then.rs | 6 +--- crates/oxc_linter/src/rules/react/jsx_key.rs | 2 +- .../src/rules/react/no_array_index_key.rs | 2 +- .../src/rules/react/no_is_mounted.rs | 7 ++--- .../src/rules/react/rules_of_hooks.rs | 15 +++------- .../explicit_function_return_type.rs | 2 +- .../src/rules/typescript/no_namespace.rs | 6 ++-- .../rules/unicorn/no_accessor_recursion.rs | 2 +- .../oxc_linter/src/rules/unicorn/no_null.rs | 2 +- .../no_single_promise_in_promise_methods.rs | 3 +- .../src/rules/unicorn/prefer_array_find.rs | 2 +- .../unicorn/prefer_dom_node_text_content.rs | 2 +- .../src/rules/unicorn/prefer_global_this.rs | 2 +- crates/oxc_linter/src/utils/react.rs | 8 +---- crates/oxc_mangler/src/keep_names.rs | 2 +- crates/oxc_semantic/src/binder.rs | 2 +- crates/oxc_semantic/src/checker/javascript.rs | 30 +++++++++---------- crates/oxc_semantic/src/checker/typescript.rs | 2 +- crates/oxc_semantic/src/node.rs | 21 +++++++------ 51 files changed, 125 insertions(+), 176 deletions(-) diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index b236896cf7ba7..5605a92427f89 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -241,7 +241,6 @@ pub fn outermost_paren_parent<'a, 'b>( semantic .nodes() .ancestors(node.id()) - .skip(1) .find(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_))) } @@ -253,7 +252,6 @@ pub fn nth_outermost_paren_parent<'a, 'b>( semantic .nodes() .ancestors(node.id()) - .skip(1) .filter(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_))) .nth(n) } @@ -264,7 +262,7 @@ pub fn iter_outer_expressions<'a, 's>( semantic: &'s Semantic<'a>, node_id: NodeId, ) -> impl Iterator> + 's { - semantic.nodes().ancestor_kinds(node_id).skip(1).filter(|parent| { + semantic.nodes().ancestor_kinds(node_id).filter(|parent| { !matches!( parent, AstKind::ParenthesizedExpression(_) @@ -661,7 +659,7 @@ pub fn is_default_this_binding<'a>( current_node = parent; } AstKind::ReturnStatement(_) => { - let upper_func = semantic.nodes().ancestors(parent.id()).skip(1).find(|node| { + let upper_func = semantic.nodes().ancestors(parent.id()).find(|node| { matches!( node.kind(), AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) diff --git a/crates/oxc_linter/src/rules/eslint/func_names.rs b/crates/oxc_linter/src/rules/eslint/func_names.rs index f4975f51c2e3e..0a13446d0c2c1 100644 --- a/crates/oxc_linter/src/rules/eslint/func_names.rs +++ b/crates/oxc_linter/src/rules/eslint/func_names.rs @@ -272,7 +272,7 @@ impl Rule for FuncNames { // a function which is calling itself inside is always valid let ast_span = - ctx.nodes().ancestors(node.id()).skip(1).find_map(|p| match p.kind() { + ctx.nodes().ancestors(node.id()).find_map(|p| match p.kind() { AstKind::Function(func) => { let func_name = func.name()?; @@ -314,7 +314,7 @@ impl Rule for FuncNames { ctx.diagnostic_with_fix( unnamed_diagnostic(&func_name_complete, report_span), |fixer| { - guess_function_name(ctx, parent_node.id()).map_or_else( + guess_function_name(ctx, node.id()).map_or_else( || fixer.noop(), |name| { // if this name shadows a variable in the outer scope **and** that name is referenced @@ -346,8 +346,8 @@ impl Rule for FuncNames { } } -fn guess_function_name<'a>(ctx: &LintContext<'a>, parent_id: NodeId) -> Option> { - for parent_kind in ctx.nodes().ancestor_kinds(parent_id) { +fn guess_function_name<'a>(ctx: &LintContext<'a>, node_id: NodeId) -> Option> { + for parent_kind in ctx.nodes().ancestor_kinds(node_id) { match parent_kind { AstKind::ParenthesizedExpression(_) | AstKind::TSAsExpression(_) diff --git a/crates/oxc_linter/src/rules/eslint/func_style.rs b/crates/oxc_linter/src/rules/eslint/func_style.rs index bfe61967618f2..df767ef6781d7 100644 --- a/crates/oxc_linter/src/rules/eslint/func_style.rs +++ b/crates/oxc_linter/src/rules/eslint/func_style.rs @@ -297,7 +297,6 @@ impl Rule for FuncStyle { let arrow_func_ancestor = semantic .nodes() .ancestors(node.id()) - .skip(1) .find(|v| matches!(v.kind(), AstKind::FunctionBody(_))) .map(|el| semantic.nodes().parent_node(el.id())); if let Some(ret) = arrow_func_ancestor { diff --git a/crates/oxc_linter/src/rules/eslint/init_declarations.rs b/crates/oxc_linter/src/rules/eslint/init_declarations.rs index 632bdf2bfd5ab..e75d822583142 100644 --- a/crates/oxc_linter/src/rules/eslint/init_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/init_declarations.rs @@ -135,7 +135,7 @@ impl Rule for InitDeclarations { return; } let decl_ancestor = - ctx.nodes().ancestor_kinds(node.id()).skip(1).find(|el| { + ctx.nodes().ancestor_kinds(node.id()).find(|el| { matches!(el, AstKind::TSModuleDeclaration(ts_module_decl) if ts_module_decl.declare) }); if decl_ancestor.is_some() { diff --git a/crates/oxc_linter/src/rules/eslint/max_depth.rs b/crates/oxc_linter/src/rules/eslint/max_depth.rs index d84fb8a345bd5..1b3a72ab20baf 100644 --- a/crates/oxc_linter/src/rules/eslint/max_depth.rs +++ b/crates/oxc_linter/src/rules/eslint/max_depth.rs @@ -115,7 +115,7 @@ declare_oxc_lint!( impl Rule for MaxDepth { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if should_count(node, ctx.nodes()) { - let depth = ctx + let depth = 1 + ctx .nodes() .ancestors(node.id()) .take_while(|node| !should_stop(node)) diff --git a/crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs b/crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs index f6bcc46342bb1..fb9d4edc41611 100644 --- a/crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs +++ b/crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs @@ -108,8 +108,8 @@ declare_oxc_lint!( impl Rule for MaxNestedCallbacks { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if is_function_node(node) { - let depth = ctx + if is_callback(node, ctx) { + let depth = 1 + ctx .semantic() .nodes() .ancestors(node.id()) diff --git a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs index 7142fa0816fe9..42a0d7b543087 100644 --- a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs @@ -89,7 +89,7 @@ impl Rule for NoCondAssign { } AstKind::AssignmentExpression(expr) if self.config == NoCondAssignConfig::Always => { let mut spans = vec![]; - for ancestor in ctx.nodes().ancestors(node.id()).skip(1) { + for ancestor in ctx.nodes().ancestors(node.id()) { match ancestor.kind() { AstKind::IfStatement(if_stmt) => { spans.push(if_stmt.test.span()); diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index ab34f5835e925..9ccc1bd415c50 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -158,7 +158,7 @@ fn remove_console<'c, 'a: 'c>( node: &AstNode<'a>, ) -> RuleFix<'a> { let mut node_to_delete = node; - for parent in ctx.nodes().ancestors(node.id()).skip(1) { + for parent in ctx.nodes().ancestors(node.id()) { match parent.kind() { AstKind::ParenthesizedExpression(_) | AstKind::ExpressionStatement(_) diff --git a/crates/oxc_linter/src/rules/eslint/no_constructor_return.rs b/crates/oxc_linter/src/rules/eslint/no_constructor_return.rs index 105d0d39e1606..b9cbca5524541 100644 --- a/crates/oxc_linter/src/rules/eslint/no_constructor_return.rs +++ b/crates/oxc_linter/src/rules/eslint/no_constructor_return.rs @@ -67,7 +67,7 @@ fn is_constructor(node: &AstNode<'_>) -> bool { } fn is_definitely_in_constructor(ctx: &LintContext, node_id: NodeId) -> bool { - for ancestor_id in ctx.nodes().ancestor_ids(node_id).skip(1) { + for ancestor_id in ctx.nodes().ancestor_ids(node_id) { match ctx.nodes().kind(ancestor_id) { AstKind::Function(_) => { return is_constructor(ctx.nodes().parent_node(ancestor_id)); diff --git a/crates/oxc_linter/src/rules/eslint/no_debugger.rs b/crates/oxc_linter/src/rules/eslint/no_debugger.rs index 7499951f0ecb7..9afbee247e8b8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_debugger.rs +++ b/crates/oxc_linter/src/rules/eslint/no_debugger.rs @@ -55,7 +55,6 @@ impl Rule for NoDebugger { let Some(parent) = ctx .nodes() .ancestors(node.id()) - .skip(1) .find(|p| !matches!(p.kind(), AstKind::ParenthesizedExpression(_))) else { return fixer.delete(&stmt.span).with_message(REMOVE_DEBUGGER); diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_function.rs b/crates/oxc_linter/src/rules/eslint/no_empty_function.rs index 654fc644ace22..f1c9f8b1742b0 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_function.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_function.rs @@ -225,7 +225,7 @@ impl NoEmptyFunction { node: &AstNode<'a>, ctx: &LintContext<'a>, ) -> ViolationInfo<'a> { - for parent in ctx.nodes().ancestor_kinds(node.id()).skip(1) { + for parent in ctx.nodes().ancestor_kinds(node.id()) { match parent { AstKind::Function(f) => { if let Some(name) = f.name() { diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index 90a9d014a92b7..d93fb4741baa1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -243,7 +243,7 @@ impl NoEval { node: &'a AstNode<'b>, semantic: &'a LintContext<'b>, ) -> Option<&'a AstNode<'b>> { - semantic.nodes().ancestors(node.id()).skip(1).find(|parent| { + semantic.nodes().ancestors(node.id()).find(|parent| { !matches!( parent.kind(), AstKind::ParenthesizedExpression(_) | AstKind::ChainExpression(_) diff --git a/crates/oxc_linter/src/rules/eslint/no_extend_native.rs b/crates/oxc_linter/src/rules/eslint/no_extend_native.rs index 18e457eef4b41..00dc2028d0dab 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extend_native.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extend_native.rs @@ -143,7 +143,7 @@ fn get_define_property_call<'a>( ctx: &'a LintContext, node: &AstNode<'a>, ) -> Option<&'a AstNode<'a>> { - for parent in ctx.nodes().ancestors(node.id()).skip(1) { + for parent in ctx.nodes().ancestors(node.id()) { if let AstKind::CallExpression(call_expr) = parent.kind() { if is_define_property_call(call_expr) { return Some(parent); @@ -180,7 +180,7 @@ fn get_property_assignment<'a>( ctx: &'a LintContext, node: &AstNode<'a>, ) -> Option<&'a AstNode<'a>> { - for parent in ctx.nodes().ancestors(node.id()).skip(1) { + for parent in ctx.nodes().ancestors(node.id()) { match parent.kind() { AstKind::AssignmentExpression(assignment_expr) if assignment_expr.left.span().contains_inclusive(node.span()) => diff --git a/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs b/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs index b5875a7cb466e..fe989806f59d0 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs @@ -1,4 +1,3 @@ -use itertools::Itertools; use oxc_ast::{ AstKind, ast::{CallExpression, Expression, NewExpression}, @@ -210,19 +209,14 @@ fn is_unary_negation(node: &AstNode) -> bool { } fn get_real_parent<'a, 'b>(node: &AstNode, ctx: &'a LintContext<'b>) -> Option<&'a AstNode<'b>> { - for (_, parent) in - ctx.nodes().ancestors(node.id()).tuple_windows::<(&AstNode<'b>, &AstNode<'b>)>() - { - if let AstKind::Argument(_) - | AstKind::ParenthesizedExpression(_) - | AstKind::ChainExpression(_) = parent.kind() - { - continue; - } - - return Some(parent); - } - None + ctx.nodes().ancestors(node.id()).find(|parent| { + !matches!( + parent.kind(), + AstKind::Argument(_) + | AstKind::ParenthesizedExpression(_) + | AstKind::ChainExpression(_) + ) + }) } /// Remove `!!` from `expr` if present. diff --git a/crates/oxc_linter/src/rules/eslint/no_labels.rs b/crates/oxc_linter/src/rules/eslint/no_labels.rs index b24e7e55f42ab..80f290065bd5b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_labels.rs +++ b/crates/oxc_linter/src/rules/eslint/no_labels.rs @@ -191,8 +191,7 @@ impl NoLabels { stmt_node_id: NodeId, ctx: &LintContext<'a>, ) -> bool { - let nodes = ctx.nodes(); - for ancestor_kind in nodes.ancestor_kinds(stmt_node_id) { + for ancestor_kind in ctx.nodes().ancestor_kinds(stmt_node_id) { if let AstKind::LabeledStatement(labeled_stmt) = ancestor_kind { if label.name == labeled_stmt.label.name { return self.is_allowed(&labeled_stmt.body); diff --git a/crates/oxc_linter/src/rules/eslint/no_new.rs b/crates/oxc_linter/src/rules/eslint/no_new.rs index 437c22042afd0..e357a23bf3bf5 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new.rs @@ -52,17 +52,13 @@ impl Rule for NoNew { let mut ancestors = ctx .nodes() .ancestors(node.id()) - .filter(|a| !matches!(a.kind(), AstKind::ParenthesizedExpression(_))) - .map(AstNode::id) - .skip(1); - let Some(node_id) = ancestors.next() else { return }; + .filter(|a| !matches!(a.kind(), AstKind::ParenthesizedExpression(_))); + let Some(node) = ancestors.next() else { return }; - let kind = ctx.nodes().kind(node_id); - if matches!(kind, AstKind::ExpressionStatement(_)) { + if matches!(node.kind(), AstKind::ExpressionStatement(_)) { ancestors.next(); // skip `FunctionBody` - if let Some(node_id) = ancestors.next() { - let kind = ctx.nodes().kind(node_id); - if matches!(kind, AstKind::ArrowFunctionExpression(e) if e.expression) { + if let Some(node) = ancestors.next() { + if matches!(node.kind(), AstKind::ArrowFunctionExpression(e) if e.expression) { return; } } diff --git a/crates/oxc_linter/src/rules/eslint/no_new_func.rs b/crates/oxc_linter/src/rules/eslint/no_new_func.rs index 19a904f48bb34..da4e17e995166 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new_func.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new_func.rs @@ -67,13 +67,12 @@ impl Rule for NoNewFunc { return; }; - let parent: Option> = - ctx.nodes().ancestor_kinds(node.id()).skip(1).find(|node| { - !matches!( - node, - AstKind::ChainExpression(_) | AstKind::ParenthesizedExpression(_) - ) - }); + let parent = ctx.nodes().ancestor_kinds(node.id()).find(|node| { + !matches!( + node, + AstKind::ChainExpression(_) | AstKind::ParenthesizedExpression(_) + ) + }); let Some(AstKind::CallExpression(parent_call_expr)) = parent else { return; diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs index 1000cc61e7fda..857345e2b9d6e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs @@ -113,9 +113,8 @@ impl Rule for NoUnusedPrivateClassMembers { } fn is_read(current_node_id: NodeId, nodes: &AstNodes) -> bool { - for (curr, parent) in nodes - .ancestors(nodes.parent_id(current_node_id)) - .tuple_windows::<(&AstNode<'_>, &AstNode<'_>)>() + for (curr, parent) in + nodes.ancestors(current_node_id).tuple_windows::<(&AstNode<'_>, &AstNode<'_>)>() { match (curr.kind(), parent.kind()) { (member_expr, AstKind::AssignmentTarget(_) | AstKind::SimpleAssignmentTarget(_)) diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs index 650b69567ca8a..9e7311745d141 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs @@ -204,7 +204,7 @@ impl NoUnusedVars { param: &FormalParameter<'a>, params_id: NodeId, ) -> bool { - let mut parents_iter = semantic.nodes().ancestor_kinds(params_id).skip(1); + let mut parents_iter = semantic.nodes().ancestor_kinds(params_id); // in function declarations, the parent immediately before the // FormalParameters is a TSDeclareBlock diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs index 8d0de227fc7fc..e462c01aa637b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs @@ -1,4 +1,4 @@ -use std::{cell::OnceCell, fmt}; +use std::{cell::OnceCell, fmt, iter}; use oxc_ast::{ AstKind, @@ -99,18 +99,20 @@ impl<'s, 'a> Symbol<'s, 'a> { #[inline] pub fn iter_parents(&self) -> impl Iterator> + '_ { - self.iter_self_and_parents().skip(1) + self.nodes().ancestors(self.declaration_id()) } pub fn iter_self_and_parents(&self) -> impl Iterator> + '_ { - self.nodes().ancestors(self.declaration_id()) + let node_id = self.declaration_id(); + let node = self.nodes().get_node(node_id); + iter::once(node).chain(self.nodes().ancestors(node_id)) } pub fn iter_relevant_parents_of( &self, node_id: NodeId, ) -> impl Iterator> + Clone + '_ { - self.nodes().ancestors(node_id).skip(1).filter(|n| Self::is_relevant_kind(n.kind())) + self.nodes().ancestors(node_id).filter(|n| Self::is_relevant_kind(n.kind())) } pub fn iter_relevant_parent_and_grandparent_kinds( @@ -118,11 +120,9 @@ impl<'s, 'a> Symbol<'s, 'a> { node_id: NodeId, ) -> impl Iterator, /* grandparent */ AstKind<'a>)> + Clone + '_ { - let parents_iter = self - .nodes() - .ancestor_kinds(node_id) - // no skip - .filter(|kind| Self::is_relevant_kind(*kind)); + let parents_iter = iter::once(self.nodes().kind(node_id)).chain( + self.nodes().ancestor_kinds(node_id).filter(|kind| Self::is_relevant_kind(*kind)), + ); let grandparents_iter = parents_iter.clone().skip(1); @@ -184,7 +184,7 @@ impl<'a> Symbol<'_, 'a> { /// We need to do this due to limitations of [`Semantic`]. fn in_export_node(&self) -> bool { - for parent in self.nodes().ancestors(self.declaration_id()).skip(1) { + for parent in self.nodes().ancestors(self.declaration_id()) { match parent.kind() { AstKind::ExportNamedDeclaration(_) | AstKind::ExportDefaultDeclaration(_) => { return true; diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs index 45ac111e9f7ea..f086280525c53 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs @@ -245,7 +245,7 @@ impl<'a> Symbol<'_, 'a> { return false; } - for parent in self.nodes().ancestors(reference.node_id()).map(AstNode::kind) { + for parent in self.nodes().ancestor_kinds(reference.node_id()) { match parent { AstKind::IdentifierReference(_) | AstKind::SimpleAssignmentTarget(_) @@ -405,7 +405,7 @@ impl<'a> Symbol<'_, 'a> { let name = self.name(); let ref_span = self.get_ref_span(reference); - for node in self.nodes().ancestors(reference.node_id()).skip(1) { + for node in self.nodes().ancestors(reference.node_id()) { match node.kind() { // references used in declaration of another variable are definitely // used by others diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs index a7a0eb5e34608..14d62062a9a5f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs @@ -116,8 +116,7 @@ impl Rule for NoUselessConstructor { return; } - let class = - ctx.nodes().ancestors(node.id()).skip(1).find_map(|parent| parent.kind().as_class()); + let class = ctx.nodes().ancestors(node.id()).find_map(|parent| parent.kind().as_class()); debug_assert!(class.is_some(), "Found a constructor outside of a class definition"); let Some(class) = class else { return; diff --git a/crates/oxc_linter/src/rules/import/no_commonjs.rs b/crates/oxc_linter/src/rules/import/no_commonjs.rs index 57817689531f0..05f5d712d92d7 100644 --- a/crates/oxc_linter/src/rules/import/no_commonjs.rs +++ b/crates/oxc_linter/src/rules/import/no_commonjs.rs @@ -162,7 +162,7 @@ impl Rule for NoCommonjs { let object = member_expr_kind.object(); if object.is_specific_id("module") && property_name == "exports" { - let Some(parent_node) = ctx.nodes().ancestors(node.id()).nth(3) else { + let Some(parent_node) = ctx.nodes().ancestors(node.id()).nth(2) else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs b/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs index eb617f8b7d658..2d4909c54b30e 100644 --- a/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs +++ b/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs @@ -143,14 +143,11 @@ impl NoDuplicateHooks { } let hook_name = jest_fn_call.name.to_string(); - let parent_node_id = - match ctx.nodes().ancestor_ids(node.id()).find(|n| hook_contexts.contains_key(n)) { - Some(n) => Some(n), - _ => Some(root_node_id), - }; - let Some(parent_id) = parent_node_id else { - return; - }; + let parent_id = ctx + .nodes() + .ancestor_ids(node.id()) + .find(|n| hook_contexts.contains_key(n)) + .unwrap_or(root_node_id); let Some(contexts) = hook_contexts.get_mut(&parent_id) else { return; diff --git a/crates/oxc_linter/src/rules/jest/prefer_each.rs b/crates/oxc_linter/src/rules/jest/prefer_each.rs index ce6ae510a3e51..045280dd35d21 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_each.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_each.rs @@ -98,7 +98,7 @@ impl PreferEach { return; } - for parent_node in ctx.nodes().ancestors(node.id()).skip(1) { + for parent_node in ctx.nodes().ancestors(node.id()) { match parent_node.kind() { AstKind::CallExpression(_) => return, AstKind::ForStatement(_) diff --git a/crates/oxc_linter/src/rules/nextjs/no_img_element.rs b/crates/oxc_linter/src/rules/nextjs/no_img_element.rs index 868d59b7852ec..a511009c29f42 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_img_element.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_img_element.rs @@ -79,8 +79,7 @@ impl Rule for NoImgElement { return; } - // first two are self, parent. third is grandparent - let Some(grandparent) = ctx.nodes().ancestor_kinds(node.id()).nth(2) else { + let Some(grandparent) = ctx.nodes().ancestor_kinds(node.id()).nth(1) else { return; }; diff --git a/crates/oxc_linter/src/rules/oxc/bad_comparison_sequence.rs b/crates/oxc_linter/src/rules/oxc/bad_comparison_sequence.rs index 903f4ed007105..137e64c1f4b48 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_comparison_sequence.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_comparison_sequence.rs @@ -60,19 +60,17 @@ fn has_no_bad_comparison_in_parents<'a, 'b>( node: &'b AstNode<'a>, ctx: &'b LintContext<'a>, ) -> bool { - for node_id in ctx.nodes().ancestor_ids(node.id()).skip(1) { - let kind = ctx.nodes().kind(node_id); - + for ancestor_kind in ctx.nodes().ancestor_kinds(node.id()) { // `a === b === c === d === e` only produce one error, since `(a === b === c) === d === e` will produce two errors. // So we should treat Parenthesized Expression as a boundary. - if matches!(kind, AstKind::ParenthesizedExpression(_)) - || kind.is_declaration() - || kind.is_statement() + if matches!(ancestor_kind, AstKind::ParenthesizedExpression(_)) + || ancestor_kind.is_declaration() + || ancestor_kind.is_statement() { return true; } - if matches!(kind, AstKind::BinaryExpression(expr) if is_bad_comparison(expr)) { + if matches!(ancestor_kind, AstKind::BinaryExpression(expr) if is_bad_comparison(expr)) { return false; } } diff --git a/crates/oxc_linter/src/rules/oxc/missing_throw.rs b/crates/oxc_linter/src/rules/oxc/missing_throw.rs index 3273677e1e1cf..ddd44a1af206d 100644 --- a/crates/oxc_linter/src/rules/oxc/missing_throw.rs +++ b/crates/oxc_linter/src/rules/oxc/missing_throw.rs @@ -57,7 +57,7 @@ impl Rule for MissingThrow { impl MissingThrow { fn has_missing_throw<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { - let mut node_ancestors = ctx.nodes().ancestor_ids(node.id()).skip(1); + let mut node_ancestors = ctx.nodes().ancestor_ids(node.id()); let Some(node_id) = node_ancestors.next() else { return false; diff --git a/crates/oxc_linter/src/rules/promise/no_callback_in_promise.rs b/crates/oxc_linter/src/rules/promise/no_callback_in_promise.rs index 2b657e8aa7589..50acd0cf4ed31 100644 --- a/crates/oxc_linter/src/rules/promise/no_callback_in_promise.rs +++ b/crates/oxc_linter/src/rules/promise/no_callback_in_promise.rs @@ -115,12 +115,7 @@ impl Rule for NoCallbackInPromise { ctx.diagnostic(no_callback_in_promise_diagnostic(id.span)); } } - } else if ctx - .nodes() - .ancestors(node.id()) - .skip(1) - .any(|node| Self::is_inside_promise(node, ctx)) - { + } else if ctx.nodes().ancestors(node.id()).any(|node| Self::is_inside_promise(node, ctx)) { ctx.diagnostic(no_callback_in_promise_diagnostic(node.span())); } } @@ -134,7 +129,7 @@ impl NoCallbackInPromise { return false; } - ctx.nodes().ancestors(node.id()).nth(2).is_some_and(|node| { + ctx.nodes().ancestors(node.id()).nth(1).is_some_and(|node| { node.kind().as_call_expression().is_some_and(Self::has_promise_callback) }) } diff --git a/crates/oxc_linter/src/rules/promise/no_nesting.rs b/crates/oxc_linter/src/rules/promise/no_nesting.rs index 87a304ce861d9..468db8c7947e1 100644 --- a/crates/oxc_linter/src/rules/promise/no_nesting.rs +++ b/crates/oxc_linter/src/rules/promise/no_nesting.rs @@ -76,7 +76,7 @@ fn is_inside_promise(node: &AstNode, ctx: &LintContext) -> bool { return false; } - ctx.nodes().ancestors(node.id()).nth(2).is_some_and(|node| { + ctx.nodes().ancestors(node.id()).nth(1).is_some_and(|node| { node.kind().as_call_expression().is_some_and(|a| { is_promise(a).is_some_and(|prop_name| prop_name == "then" || prop_name == "catch") }) @@ -88,14 +88,12 @@ fn closest_promise_cb<'a, 'b>( node: &'a AstNode<'b>, ctx: &'a LintContext<'b>, ) -> Option<&'a CallExpression<'b>> { - ctx.nodes() - .ancestors(node.id()) - .filter_map(|node| node.kind().as_call_expression()) - .filter(|ancestor| { + ctx.nodes().ancestors(node.id()).filter_map(|node| node.kind().as_call_expression()).find( + |ancestor| { is_promise(ancestor) .is_some_and(|prop_name| prop_name == "then" || prop_name == "catch") - }) - .nth(1) + }, + ) } /// Checks if we can safely unnest the promise callback. diff --git a/crates/oxc_linter/src/rules/promise/no_return_wrap.rs b/crates/oxc_linter/src/rules/promise/no_return_wrap.rs index 37b8e0c463d9f..64e68835227b3 100644 --- a/crates/oxc_linter/src/rules/promise/no_return_wrap.rs +++ b/crates/oxc_linter/src/rules/promise/no_return_wrap.rs @@ -151,7 +151,7 @@ impl Rule for NoReturnWrap { return; }; - if !inside_promise_cb(node, ctx) { + if is_promise(call_expr).is_none() && !inside_promise_cb(node, ctx) { return; } diff --git a/crates/oxc_linter/src/rules/promise/prefer_await_to_callbacks.rs b/crates/oxc_linter/src/rules/promise/prefer_await_to_callbacks.rs index 915a67f1c0ec5..c5df21215d543 100644 --- a/crates/oxc_linter/src/rules/promise/prefer_await_to_callbacks.rs +++ b/crates/oxc_linter/src/rules/promise/prefer_await_to_callbacks.rs @@ -133,7 +133,7 @@ impl PreferAwaitToCallbacks { } fn is_inside_yield_or_await(id: NodeId, ctx: &LintContext) -> bool { - ctx.nodes().ancestors(id).skip(1).any(|parent| { + ctx.nodes().ancestors(id).any(|parent| { matches!(parent.kind(), AstKind::AwaitExpression(_) | AstKind::YieldExpression(_)) }) } diff --git a/crates/oxc_linter/src/rules/promise/prefer_await_to_then.rs b/crates/oxc_linter/src/rules/promise/prefer_await_to_then.rs index 611f9df1b7375..806a44f233d8c 100644 --- a/crates/oxc_linter/src/rules/promise/prefer_await_to_then.rs +++ b/crates/oxc_linter/src/rules/promise/prefer_await_to_then.rs @@ -84,11 +84,7 @@ impl Rule for PreferAwaitToThen { if !self.strict { // Already inside a yield or await - if ctx - .nodes() - .ancestor_ids(node.id()) - .any(|node_id| is_inside_yield_or_await(ctx.nodes().get_node(node_id))) - { + if ctx.nodes().ancestors(node.id()).any(is_inside_yield_or_await) { return; } } diff --git a/crates/oxc_linter/src/rules/react/jsx_key.rs b/crates/oxc_linter/src/rules/react/jsx_key.rs index 4d9d308d8a971..e24674750519d 100644 --- a/crates/oxc_linter/src/rules/react/jsx_key.rs +++ b/crates/oxc_linter/src/rules/react/jsx_key.rs @@ -139,7 +139,7 @@ pub fn is_children<'a, 'b>(call: &'b CallExpression<'a>, ctx: &'b LintContext<'a is_import(ctx, ident.name.as_str(), REACT, REACT) && local_name == CHILDREN } fn is_within_children_to_array<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { - let parents_iter = ctx.nodes().ancestors(node.id()).skip(2); + let parents_iter = ctx.nodes().ancestors(node.id()).skip(1); parents_iter .filter_map(|parent_node| parent_node.kind().as_call_expression()) .any(|parent_call| is_children(parent_call, ctx) && is_to_array(parent_call)) diff --git a/crates/oxc_linter/src/rules/react/no_array_index_key.rs b/crates/oxc_linter/src/rules/react/no_array_index_key.rs index 0e082a898f3a6..d5bd3d262fefa 100644 --- a/crates/oxc_linter/src/rules/react/no_array_index_key.rs +++ b/crates/oxc_linter/src/rules/react/no_array_index_key.rs @@ -124,7 +124,7 @@ fn check_react_clone_element<'a>( } fn find_index_param_name<'a>(node: &'a AstNode, ctx: &'a LintContext) -> Option<&'a str> { - for ancestor in ctx.nodes().ancestors(node.id()).skip(1) { + for ancestor in ctx.nodes().ancestors(node.id()) { if let AstKind::CallExpression(call_expr) = ancestor.kind() { let Expression::StaticMemberExpression(expr) = &call_expr.callee else { return None; diff --git a/crates/oxc_linter/src/rules/react/no_is_mounted.rs b/crates/oxc_linter/src/rules/react/no_is_mounted.rs index 348eb3d74fc86..9b11bf0c4e592 100644 --- a/crates/oxc_linter/src/rules/react/no_is_mounted.rs +++ b/crates/oxc_linter/src/rules/react/no_is_mounted.rs @@ -63,11 +63,8 @@ impl Rule for NoIsMounted { return; } - for ancestor in ctx.nodes().ancestor_ids(node.id()).skip(1) { - if matches!( - ctx.nodes().kind(ancestor), - AstKind::ObjectProperty(_) | AstKind::MethodDefinition(_) - ) { + for ancestor_kind in ctx.nodes().ancestor_kinds(node.id()) { + if matches!(ancestor_kind, AstKind::ObjectProperty(_) | AstKind::MethodDefinition(_)) { ctx.diagnostic(no_is_mounted_diagnostic(call_expr.span)); break; } diff --git a/crates/oxc_linter/src/rules/react/rules_of_hooks.rs b/crates/oxc_linter/src/rules/react/rules_of_hooks.rs index 327da3ca0a1df..4f47d8b33bc8d 100644 --- a/crates/oxc_linter/src/rules/react/rules_of_hooks.rs +++ b/crates/oxc_linter/src/rules/react/rules_of_hooks.rs @@ -324,10 +324,7 @@ fn has_conditional_path_accept_throw( } fn parent_func<'a>(nodes: &'a AstNodes<'a>, node: &AstNode) -> Option<&'a AstNode<'a>> { - nodes - .ancestor_ids(node.id()) - .map(|id| nodes.get_node(id)) - .find(|it| it.kind().is_function_like()) + nodes.ancestors(node.id()).find(|node| node.kind().is_function_like()) } /// Checks if the `node_id` is a callback argument, @@ -349,8 +346,7 @@ fn is_non_react_func_arg(nodes: &AstNodes, node_id: NodeId) -> bool { fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: NodeId) -> bool { nodes - .ancestor_ids(node_id) - .map(|id| nodes.get_node(id)) + .ancestors(node_id) .filter(|node| node.kind().is_function_like()) .map(|node| { ( @@ -386,10 +382,7 @@ fn get_declaration_identifier<'a>( Some(Cow::Borrowed(id.name.as_str())) } AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => { - let parent = - nodes.ancestor_ids(node_id).skip(1).map(|node| nodes.get_node(node)).next()?; - - match parent.kind() { + match nodes.parent_kind(node_id) { AstKind::VariableDeclarator(decl) => { decl.id.get_identifier_name().map(|id| Cow::Borrowed(id.as_str())) } @@ -417,7 +410,7 @@ fn get_declaration_identifier<'a>( /// # Panics /// `node_id` should always point to a valid `Function`. fn is_memo_or_forward_ref_callback(nodes: &AstNodes, node_id: NodeId) -> bool { - nodes.ancestor_ids(node_id).map(|id| nodes.get_node(id)).any(|node| { + nodes.ancestors(node_id).any(|node| { if let AstKind::CallExpression(call) = node.kind() { call.callee_name().is_some_and(|name| matches!(name, "forwardRef" | "memo")) } else { diff --git a/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs b/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs index af3d940dc83d0..00e3251969173 100644 --- a/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs +++ b/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs @@ -623,7 +623,7 @@ fn ancestor_has_return_type<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bo } } - for ancestor in ctx.nodes().ancestors(node.id()).skip(1) { + for ancestor in ctx.nodes().ancestors(node.id()) { match ancestor.kind() { AstKind::ArrowFunctionExpression(func) => { if func.return_type.is_some() { diff --git a/crates/oxc_linter/src/rules/typescript/no_namespace.rs b/crates/oxc_linter/src/rules/typescript/no_namespace.rs index 0c9a374a672dd..70ba019b7f177 100644 --- a/crates/oxc_linter/src/rules/typescript/no_namespace.rs +++ b/crates/oxc_linter/src/rules/typescript/no_namespace.rs @@ -157,7 +157,9 @@ impl Rule for NoNamespace { return; } - if self.allow_declarations && is_declaration(node, ctx) { + if self.allow_declarations + && (declaration.declare || is_any_ancestor_declaration(node, ctx)) + { return; } @@ -185,7 +187,7 @@ impl Rule for NoNamespace { } } -fn is_declaration(node: &AstNode, ctx: &LintContext) -> bool { +fn is_any_ancestor_declaration(node: &AstNode, ctx: &LintContext) -> bool { ctx.nodes() .ancestors(node.id()) .any(|node| node.kind().as_ts_module_declaration().is_some_and(|decl| decl.declare)) diff --git a/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs b/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs index 9d819f9586232..b0190fdf455cc 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs @@ -65,7 +65,7 @@ impl Rule for NoAccessorRecursion { let AstKind::ThisExpression(this_expr) = node.kind() else { return; }; - let Some(target) = ctx.nodes().ancestors(node.id()).skip(1).find(|n| match n.kind() { + let Some(target) = ctx.nodes().ancestors(node.id()).find(|n| match n.kind() { member_expr if member_expr.is_member_expression_kind() => { let Some(member_expr) = member_expr.as_member_expression_kind() else { return false; diff --git a/crates/oxc_linter/src/rules/unicorn/no_null.rs b/crates/oxc_linter/src/rules/unicorn/no_null.rs index e59a7584edf4e..d47aa33b311cd 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_null.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_null.rs @@ -205,7 +205,7 @@ impl Rule for NoNull { ctx.diagnostic_with_fix(no_null_diagnostic(null_literal.span), |fixer| { let mut null_span = null_literal.span; // Find the last parent that is a TSAsExpression (`null as any`) or TSNonNullExpression (`null!`) - for parent in ctx.nodes().ancestors(node.id()).skip(1) { + for parent in ctx.nodes().ancestors(node.id()) { let parent = parent.kind(); if matches!( parent, diff --git a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs index 1ca5499b12970..8a59482c63d0f 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs @@ -97,7 +97,6 @@ impl Rule for NoSinglePromiseInPromiseMethods { .nodes() // get first non-parenthesis parent node .ancestor_kinds(node.id()) - .skip(1) // first node is the call expr .find(|kind| !is_ignorable_kind(kind)) // check if it's an `await ...` expression .is_some_and(|kind| matches!(kind, AstKind::AwaitExpression(_))); @@ -128,7 +127,7 @@ fn is_promise_method_with_single_argument(call_expr: &CallExpression) -> bool { } fn is_fixable(call_node_id: NodeId, ctx: &LintContext<'_>) -> bool { - for parent in ctx.nodes().ancestors(call_node_id).skip(1) { + for parent in ctx.nodes().ancestors(call_node_id) { match parent.kind() { AstKind::CallExpression(_) | AstKind::VariableDeclarator(_) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs b/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs index f738cf7f268bc..5b6296fcba97c 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs @@ -123,7 +123,7 @@ impl Rule for PreferArrayFind { if let Some(Expression::CallExpression(call_expr)) = &var_decl.init { if is_filter_call(call_expr) && !matches!( - ctx.nodes().ancestor_kinds(node.id()).nth(2), + ctx.nodes().ancestor_kinds(node.id()).nth(1), Some( AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs index dc264093a09d0..9efcc1c16f0cd 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs @@ -84,7 +84,7 @@ impl Rule for PreferDomNodeTextContent { return; } - let mut ancestor_kinds = ctx.nodes().ancestor_kinds(node.id()).skip(1); + let mut ancestor_kinds = ctx.nodes().ancestor_kinds(node.id()); let Some(mut parent_node_kind) = ancestor_kinds.next() else { return }; if matches!(parent_node_kind, AstKind::AssignmentTargetPropertyIdentifier(_)) { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs index c8c1e741a764e..396ed8eb9cc17 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs @@ -85,7 +85,7 @@ impl Rule for PreferGlobalThis { "addEventListener" | "removeEventListener" | "dispatchEvent" ) { if let Some(AstKind::CallExpression(call_expr)) = - ctx.nodes().ancestor_kinds(node.id()).nth(2) + ctx.nodes().ancestor_kinds(node.id()).nth(1) { if let Some(Expression::StringLiteral(lit)) = call_expr.arguments.first().and_then(|arg| arg.as_expression()) diff --git a/crates/oxc_linter/src/utils/react.rs b/crates/oxc_linter/src/utils/react.rs index 6e5aab5e7edff..32a3e0ed1f3ac 100644 --- a/crates/oxc_linter/src/utils/react.rs +++ b/crates/oxc_linter/src/utils/react.rs @@ -196,13 +196,7 @@ pub fn get_parent_component<'a, 'b>( node: &'b AstNode<'a>, ctx: &'b LintContext<'a>, ) -> Option<&'b AstNode<'a>> { - for node_id in ctx.nodes().ancestor_ids(node.id()) { - let node = ctx.nodes().get_node(node_id); - if is_es5_component(node) || is_es6_component(node) { - return Some(node); - } - } - None + ctx.nodes().ancestors(node.id()).find(|node| is_es5_component(node) || is_es6_component(node)) } fn get_jsx_mem_expr_name<'a>(jsx_mem_expr: &JSXMemberExpression) -> Cow<'a, str> { diff --git a/crates/oxc_mangler/src/keep_names.rs b/crates/oxc_mangler/src/keep_names.rs index 16fbfc9afb0b4..cc362cf03d0d6 100644 --- a/crates/oxc_mangler/src/keep_names.rs +++ b/crates/oxc_mangler/src/keep_names.rs @@ -116,7 +116,7 @@ impl<'a, 'b: 'a> NameSymbolCollector<'a, 'b> { match parent_node.kind() { AstKind::SimpleAssignmentTarget(_) => { let Some((grand_parent_node_kind, grand_grand_parent_node_kind)) = - self.ast_nodes.ancestor_kinds(parent_node.id()).skip(1).take(2).collect_tuple() + self.ast_nodes.ancestor_kinds(parent_node.id()).take(2).collect_tuple() else { return false; }; diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 264c02c826e0f..2527c580d78dc 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -662,7 +662,7 @@ fn get_module_instance_state_for_alias_target<'a>( } } - let Some(node) = builder.nodes.ancestors(current_node_id).skip(1).find(|node| { + let Some(node) = builder.nodes.ancestors(current_node_id).find(|node| { matches!( node.kind(), AstKind::Program(_) | AstKind::TSModuleBlock(_) | AstKind::BlockStatement(_) diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 53e331d88e6d4..eba1ea275ce11 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -113,8 +113,8 @@ pub fn check_binding_identifier(ident: &BindingIdentifier, ctx: &SemanticBuilder // LexicalDeclaration : LetOrConst BindingList ; // * It is a Syntax Error if the BoundNames of BindingList contains "let". if !strict_mode && ident.name == "let" { - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::VariableDeclaration(decl) if decl.kind.is_lexical() => { return ctx.error(invalid_let_declaration(decl.kind.as_str(), ident.span)); } @@ -135,8 +135,8 @@ pub fn check_identifier_reference(ident: &IdentifierReference, ctx: &SemanticBui // Static Semantics: AssignmentTargetType // 1. If this IdentifierReference is contained in strict mode code and StringValue of Identifier is "eval" or "arguments", return invalid. if ctx.strict_mode() && matches!(ident.name.as_str(), "arguments" | "eval") { - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::AssignmentTarget(_) | AstKind::SimpleAssignmentTarget(_) => { return ctx.error(unexpected_identifier_assign(&ident.name, ident.span)); } @@ -152,8 +152,8 @@ pub fn check_identifier_reference(ident: &IdentifierReference, ctx: &SemanticBui // It is a Syntax Error if ContainsArguments of ClassStaticBlockStatementList is true. if ident.name == "arguments" { - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::Function(_) => break, AstKind::PropertyDefinition(_) => { return ctx.error(unexpected_arguments("class field initializer", ident.span)); @@ -431,7 +431,7 @@ pub fn check_function_declaration_in_labeled_statement<'a>( ctx.error(function_declaration_strict(decl.span)); } else { // skip(1) for `LabeledStatement` - for kind in ctx.nodes.ancestor_kinds(ctx.current_node_id).skip(1) { + for kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { match kind { // Nested labeled statement AstKind::LabeledStatement(_) => {} @@ -580,8 +580,8 @@ fn invalid_break(span: Span) -> OxcDiagnostic { pub fn check_break_statement(stmt: &BreakStatement, ctx: &SemanticBuilder<'_>) { // It is a Syntax Error if this BreakStatement is not nested, directly or indirectly (but not crossing function or static initialization block boundaries), within an IterationStatement or a SwitchStatement. - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::Program(_) => { return stmt.label.as_ref().map_or_else( || ctx.error(invalid_break(stmt.span)), @@ -622,8 +622,8 @@ fn invalid_continue(span: Span) -> OxcDiagnostic { pub fn check_continue_statement(stmt: &ContinueStatement, ctx: &SemanticBuilder<'_>) { // It is a Syntax Error if this ContinueStatement is not nested, directly or indirectly (but not crossing function or static initialization block boundaries), within an IterationStatement. - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::Program(_) => { return stmt.label.as_ref().map_or_else( || ctx.error(invalid_continue(stmt.span)), @@ -671,8 +671,8 @@ fn label_redeclaration(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { } pub fn check_labeled_statement(stmt: &LabeledStatement, ctx: &SemanticBuilder<'_>) { - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { // label cannot cross boundary on function or static block AstKind::Function(_) | AstKind::StaticBlock(_) | AstKind::Program(_) => break, // check label name redeclaration @@ -973,8 +973,8 @@ pub fn check_unary_expression(unary_expr: &UnaryExpression, ctx: &SemanticBuilde } fn is_in_formal_parameters(ctx: &SemanticBuilder<'_>) -> bool { - for node_id in ctx.nodes.ancestor_ids(ctx.current_node_id).skip(1) { - match ctx.nodes.kind(node_id) { + for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) { + match node_kind { AstKind::FormalParameter(_) => return true, AstKind::Program(_) | AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => { break; diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 51c8a9b08219e..c0325ce7ae4b7 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -139,7 +139,7 @@ fn not_allowed_namespace_declaration(span: Span) -> OxcDiagnostic { pub fn check_ts_module_declaration<'a>(decl: &TSModuleDeclaration<'a>, ctx: &SemanticBuilder<'a>) { // skip current node - for node in ctx.nodes.ancestors(ctx.current_node_id).skip(1) { + for node in ctx.nodes.ancestors(ctx.current_node_id) { match node.kind() { AstKind::Program(_) | AstKind::TSModuleBlock(_) | AstKind::TSModuleDeclaration(_) => { break; diff --git a/crates/oxc_semantic/src/node.rs b/crates/oxc_semantic/src/node.rs index 428e1a87252b7..0d29f219aba80 100644 --- a/crates/oxc_semantic/src/node.rs +++ b/crates/oxc_semantic/src/node.rs @@ -124,7 +124,7 @@ impl<'a> AstNodes<'a> { /// Walk up the AST, iterating over each parent [`NodeId`]. /// - /// The first node produced by this iterator is the node pointed to by `node_id`. + /// The first node produced by this iterator is the parent of `node_id`. /// The last node will always be [`AstKind::Program`]. #[inline] pub fn ancestor_ids(&self, node_id: NodeId) -> impl Iterator + Clone + '_ { @@ -133,7 +133,7 @@ impl<'a> AstNodes<'a> { /// Walk up the AST, iterating over each parent [`AstKind`]. /// - /// The first node produced by this iterator is the node pointed to by `node_id`. + /// The first node produced by this iterator is the parent of `node_id`. /// The last node will always be [`AstKind::Program`]. #[inline] pub fn ancestor_kinds( @@ -145,7 +145,7 @@ impl<'a> AstNodes<'a> { /// Walk up the AST, iterating over each parent [`AstNode`]. /// - /// The first node produced by this iterator is the node pointed to by `node_id`. + /// The first node produced by this iterator is the parent of `node_id`. /// The last node will always be [`AstKind::Program`]. #[inline] pub fn ancestors(&self, node_id: NodeId) -> impl Iterator> + Clone + '_ { @@ -257,13 +257,13 @@ impl<'a, 'n> IntoIterator for &'n AstNodes<'a> { /// Yields `NodeId` of each AST node. The last node yielded is `Program`. #[derive(Debug, Clone)] pub struct AstNodeIdAncestorsIter<'n> { - next_node_id: Option, + current_node_id: NodeId, parent_ids: &'n IndexSlice, } impl<'n> AstNodeIdAncestorsIter<'n> { fn new(node_id: NodeId, nodes: &'n AstNodes<'_>) -> Self { - Self { next_node_id: Some(node_id), parent_ids: nodes.parent_ids.as_slice() } + Self { current_node_id: node_id, parent_ids: nodes.parent_ids.as_slice() } } } @@ -271,14 +271,13 @@ impl Iterator for AstNodeIdAncestorsIter<'_> { type Item = NodeId; fn next(&mut self) -> Option { - if let Some(node_id) = self.next_node_id { + if self.current_node_id == NodeId::ROOT { // `Program`'s parent is itself, so next node is `None` if this node is `Program` - self.next_node_id = - if node_id == NodeId::ROOT { None } else { Some(self.parent_ids[node_id]) }; - Some(node_id) - } else { - None + return None; } + + self.current_node_id = self.parent_ids[self.current_node_id]; + Some(self.current_node_id) } }