diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index ddda691fcc81d..b236896cf7ba7 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -204,7 +204,7 @@ pub fn get_enclosing_function<'a, 'b>( { return Some(current_node); } - current_node = semantic.nodes().parent_node(current_node.id())?; + current_node = semantic.nodes().parent_node(current_node.id()); } } @@ -222,11 +222,10 @@ pub fn outermost_paren<'a, 'b>( let mut node = node; loop { - if let Some(parent) = semantic.nodes().parent_node(node.id()) { - if let AstKind::ParenthesizedExpression(_) = parent.kind() { - node = parent; - continue; - } + let parent = semantic.nodes().parent_node(node.id()); + if let AstKind::ParenthesizedExpression(_) = parent.kind() { + node = parent; + continue; } break; @@ -652,7 +651,7 @@ pub fn is_default_this_binding<'a>( let mut current_node = node; loop { - let parent = semantic.nodes().parent_node(current_node.id()).unwrap(); + let parent = semantic.nodes().parent_node(current_node.id()); let parent_kind = parent.kind(); match parent_kind { AstKind::ChainExpression(_) diff --git a/crates/oxc_linter/src/rules/eslint/array_callback_return/mod.rs b/crates/oxc_linter/src/rules/eslint/array_callback_return/mod.rs index be210e24cc2fb..cdcb702956883 100644 --- a/crates/oxc_linter/src/rules/eslint/array_callback_return/mod.rs +++ b/crates/oxc_linter/src/rules/eslint/array_callback_return/mod.rs @@ -153,7 +153,8 @@ impl Rule for ArrayCallbackReturn { /// to the target array methods we're interested in. pub fn get_array_method_name<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> Option<&'a str> { let mut current_node = node; - while let Some(parent) = ctx.nodes().parent_node(current_node.id()) { + loop { + let parent = ctx.nodes().parent_node(current_node.id()); match parent.kind() { // foo.every(nativeFoo || function foo() { ... }) AstKind::LogicalExpression(_) @@ -174,7 +175,7 @@ pub fn get_array_method_name<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> O let func_node = outermost_paren(func_node, ctx); // the node that calls func_node - let func_parent = ctx.nodes().parent_node(func_node.id()).unwrap(); + let func_parent = ctx.nodes().parent_node(func_node.id()); if let AstKind::CallExpression(call) = func_parent.kind() { let expected_callee = &call.callee; diff --git a/crates/oxc_linter/src/rules/eslint/curly.rs b/crates/oxc_linter/src/rules/eslint/curly.rs index a200d4109e7fa..f126e77db9031 100644 --- a/crates/oxc_linter/src/rules/eslint/curly.rs +++ b/crates/oxc_linter/src/rules/eslint/curly.rs @@ -293,10 +293,10 @@ fn report_if_needed<'a>( let fixed = format!("{{{}}}", ctx.source_range(body.span())); fixer.replace(body.span(), fixed) } else { - let needs_preceding_space = ctx - .nodes() - .parent_node(get_node_by_statement(body, ctx).id()) - .is_some_and(|parent| matches!(parent.kind(), AstKind::DoWhileStatement(_))); + let needs_preceding_space = matches!( + ctx.nodes().parent_kind(get_node_by_statement(body, ctx).id()), + AstKind::DoWhileStatement(_) + ); let mut fixed = ctx.source_range(body.span()).to_string(); if let Some(stripped) = fixed.strip_prefix(|c: char| c.is_whitespace() || c == '{') { @@ -329,10 +329,13 @@ fn is_collapsed_one_liner(node: &Statement, ctx: &LintContext) -> bool { let before_node_span = get_token_before(node, ctx).map_or_else( || { - ctx.nodes() - .parent_node(node.id()) - .filter(|parent| parent.span().start < span.start) - .map_or(Span::empty(0), |parent| Span::empty(parent.span().start)) + let parent = ctx.nodes().parent_node(node.id()); + + if parent.span().start < span.start { + Span::empty(parent.span().start) + } else { + Span::empty(0) + } }, oxc_span::GetSpan::span, ); diff --git a/crates/oxc_linter/src/rules/eslint/func_names.rs b/crates/oxc_linter/src/rules/eslint/func_names.rs index 32044894e7648..f4975f51c2e3e 100644 --- a/crates/oxc_linter/src/rules/eslint/func_names.rs +++ b/crates/oxc_linter/src/rules/eslint/func_names.rs @@ -248,9 +248,7 @@ impl Rule for FuncNames { match node.kind() { // check function if it invalid, do not report it because maybe later the function is calling itself AstKind::Function(func) => { - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - continue; - }; + let parent_node = ctx.nodes().parent_node(node.id()); let config = if func.generator { &self.generators_config } else { &self.default_config }; diff --git a/crates/oxc_linter/src/rules/eslint/func_style.rs b/crates/oxc_linter/src/rules/eslint/func_style.rs index 3a54f513df389..bfe61967618f2 100644 --- a/crates/oxc_linter/src/rules/eslint/func_style.rs +++ b/crates/oxc_linter/src/rules/eslint/func_style.rs @@ -232,9 +232,7 @@ impl Rule for FuncStyle { for node in semantic.nodes() { match node.kind() { AstKind::Function(func) => { - let Some(parent) = semantic.nodes().parent_node(node.id()) else { - continue; - }; + let parent = semantic.nodes().parent_node(node.id()); match func.r#type { FunctionType::FunctionDeclaration => { // There are two situations to diagnostic @@ -301,7 +299,7 @@ impl Rule for FuncStyle { .ancestors(node.id()) .skip(1) .find(|v| matches!(v.kind(), AstKind::FunctionBody(_))) - .map(|el| semantic.nodes().parent_node(el.id()).unwrap()); + .map(|el| semantic.nodes().parent_node(el.id())); if let Some(ret) = arrow_func_ancestor { arrow_func_ancestor_records.insert(ret.id()); } @@ -317,9 +315,7 @@ impl Rule for FuncStyle { // We deal with arrow functions that do not contain this and super for node in arrow_func_nodes { if !arrow_func_ancestor_records.contains(&node.id()) { - let Some(parent) = semantic.nodes().parent_node(node.id()) else { - return; - }; + let parent = semantic.nodes().parent_node(node.id()); if let AstKind::VariableDeclarator(decl) = parent.kind() { let is_type_annotation = self.allow_type_annotation && decl.id.type_annotation.is_some(); diff --git a/crates/oxc_linter/src/rules/eslint/getter_return.rs b/crates/oxc_linter/src/rules/eslint/getter_return.rs index 1fc804787e989..17910ec54cc14 100644 --- a/crates/oxc_linter/src/rules/eslint/getter_return.rs +++ b/crates/oxc_linter/src/rules/eslint/getter_return.rs @@ -147,66 +147,66 @@ impl GetterReturn { } /// Checks whether it is necessary to check the node - fn is_wanted_node(node: &AstNode, ctx: &LintContext<'_>) -> Option { - let parent = ctx.nodes().parent_node(node.id())?; + fn is_wanted_node(node: &AstNode, ctx: &LintContext<'_>) -> bool { + let parent = ctx.nodes().parent_node(node.id()); match parent.kind() { AstKind::MethodDefinition(mdef) => { if matches!(mdef.kind, MethodDefinitionKind::Get) { - return Some(true); + return true; } } AstKind::ObjectProperty(ObjectProperty { kind, key: prop_key, .. }) => { if matches!(kind, PropertyKind::Get) { - return Some(true); + return true; } if prop_key.name().is_some_and(|key| key != "get") { - return Some(false); + return false; } - let parent_2 = ctx.nodes().parent_node(parent.id())?; - let parent_3 = ctx.nodes().parent_node(parent_2.id())?; - let parent_4 = ctx.nodes().parent_node(parent_3.id())?; + let parent_2 = ctx.nodes().parent_node(parent.id()); + let parent_3 = ctx.nodes().parent_node(parent_2.id()); + let parent_4 = ctx.nodes().parent_node(parent_3.id()); // handle (X()) match parent_4.kind() { AstKind::ParenthesizedExpression(p) => { if Self::handle_paren_expr(&p.expression) { - return Some(true); + return true; } } AstKind::CallExpression(ce) => { if Self::handle_actual_expression(&ce.callee) { - return Some(true); + return true; } } _ => {} } - let parent_5 = ctx.nodes().parent_node(parent_4.id())?; - let parent_6 = ctx.nodes().parent_node(parent_5.id())?; + let parent_5 = ctx.nodes().parent_node(parent_4.id()); + let parent_6 = ctx.nodes().parent_node(parent_5.id()); match parent_6.kind() { AstKind::ParenthesizedExpression(p) => { if Self::handle_paren_expr(&p.expression) { - return Some(true); + return true; } } AstKind::CallExpression(ce) => { if Self::handle_actual_expression(&ce.callee) { - return Some(true); + return true; } } _ => { - return Some(false); + return false; } } } _ => {} } - Some(false) + false } fn run_diagnostic<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>, span: Span) { - if !Self::is_wanted_node(node, ctx).unwrap_or_default() { + if !Self::is_wanted_node(node, ctx) { return; } diff --git a/crates/oxc_linter/src/rules/eslint/id_length.rs b/crates/oxc_linter/src/rules/eslint/id_length.rs index d0d68bae6f1ca..368db12e8fb1a 100644 --- a/crates/oxc_linter/src/rules/eslint/id_length.rs +++ b/crates/oxc_linter/src/rules/eslint/id_length.rs @@ -224,9 +224,7 @@ impl IdLength { return; } - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent_node = ctx.nodes().parent_node(node.id()); match parent_node.kind() { AstKind::ImportSpecifier(import_specifier) => { if import_specifier.imported.name() == import_specifier.local.name { @@ -238,7 +236,7 @@ impl IdLength { } } AstKind::BindingProperty(_) => { - if let Some(AstKind::ObjectPattern(object_pattern)) = + if let AstKind::ObjectPattern(object_pattern) = ctx.nodes().parent_kind(parent_node.id()) { let binding_property_option = @@ -279,9 +277,7 @@ impl IdLength { return; } - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent_node = ctx.nodes().parent_node(node.id()); match parent_node.kind() { AstKind::ExportSpecifier(_) | AstKind::ImportAttribute(_) @@ -292,10 +288,8 @@ impl IdLength { AstKind::ComputedMemberExpression(_) | AstKind::PrivateFieldExpression(_) | AstKind::StaticMemberExpression(_) => { - let Some(parent_parent_node) = ctx.nodes().parent_node(parent_node.id()) else { - return; - }; - let AstKind::SimpleAssignmentTarget(_) = parent_parent_node.kind() else { + let AstKind::SimpleAssignmentTarget(_) = ctx.nodes().parent_kind(parent_node.id()) + else { return; }; @@ -308,11 +302,9 @@ impl IdLength { return; } - let Some(mut parent_parent_node) = ctx.nodes().parent_node(parent_node.id()) else { - return; - }; + let mut parent_parent_node = ctx.nodes().parent_node(parent_node.id()); if matches!(parent_parent_node.kind(), AstKind::BindingProperty(_)) { - parent_parent_node = ctx.nodes().parent_node(parent_parent_node.id()).unwrap(); + parent_parent_node = ctx.nodes().parent_node(parent_parent_node.id()); } match parent_parent_node.kind() { diff --git a/crates/oxc_linter/src/rules/eslint/init_declarations.rs b/crates/oxc_linter/src/rules/eslint/init_declarations.rs index 335475a558203..632bdf2bfd5ab 100644 --- a/crates/oxc_linter/src/rules/eslint/init_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/init_declarations.rs @@ -128,9 +128,7 @@ impl Rule for InitDeclarations { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::VariableDeclaration(decl) = node.kind() { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); // support for TypeScript's declare variables if self.mode == Mode::Always { if decl.declare { diff --git a/crates/oxc_linter/src/rules/eslint/max_depth.rs b/crates/oxc_linter/src/rules/eslint/max_depth.rs index 9e12dc8103918..d84fb8a345bd5 100644 --- a/crates/oxc_linter/src/rules/eslint/max_depth.rs +++ b/crates/oxc_linter/src/rules/eslint/max_depth.rs @@ -147,7 +147,7 @@ impl Rule for MaxDepth { } fn should_count(node: &AstNode<'_>, nodes: &AstNodes<'_>) -> bool { - matches!(node.kind(), AstKind::IfStatement(_) if !matches!(nodes.parent_kind(node.id()), Some(AstKind::IfStatement(_)))) + matches!(node.kind(), AstKind::IfStatement(_) if !matches!(nodes.parent_kind(node.id()), AstKind::IfStatement(_))) || matches!(node.kind(), |AstKind::SwitchStatement(_)| AstKind::TryStatement(_) | AstKind::DoWhileStatement(_) | AstKind::WhileStatement(_) diff --git a/crates/oxc_linter/src/rules/eslint/max_lines_per_function.rs b/crates/oxc_linter/src/rules/eslint/max_lines_per_function.rs index 4c35503db06a5..2c74b6cc43ec9 100644 --- a/crates/oxc_linter/src/rules/eslint/max_lines_per_function.rs +++ b/crates/oxc_linter/src/rules/eslint/max_lines_per_function.rs @@ -218,8 +218,7 @@ impl Rule for MaxLinesPerFunction { let final_lines = lines_in_function.saturating_sub(comment_lines); if final_lines > self.max { - let name = - get_function_name_with_kind(node, ctx.nodes().parent_node(node.id()).unwrap()); + let name = get_function_name_with_kind(node, ctx.nodes().parent_node(node.id())); ctx.diagnostic(max_lines_per_function_diagnostic(&name, final_lines, self.max, span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs index b2666505f79e3..ae3dee290aabf 100644 --- a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs +++ b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs @@ -70,19 +70,19 @@ impl Rule for NoAwaitInLoop { // Perform validation for AwaitExpression and ForOfStatement that contains await let mut parent_node = nodes.parent_node(node.id()); let mut is_in_loop = false; - while let Some(parent) = parent_node { + while !matches!(parent_node.kind(), AstKind::Program(_)) { // Check if the current node is the boundary of the loop - if Self::is_boundary(parent) { + if Self::is_boundary(parent_node) { break; } // if AwaitExpression or AwaitForOfStatement are in loop, break and report error - if Self::is_looped(span, parent) { + if Self::is_looped(span, parent_node) { is_in_loop = true; break; } - parent_node = nodes.parent_node(parent.id()); + parent_node = nodes.parent_node(parent_node.id()); } if is_in_loop { diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index 3957c52a8984a..ab34f5835e925 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -142,10 +142,9 @@ impl Rule for NoConsole { ctx.diagnostic_with_suggestion( no_console_diagnostic(diagnostic_span, &self.allow), |fixer| { - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if let AstKind::CallExpression(_) = parent.kind() { - return remove_console(fixer, ctx, parent); - } + let parent = ctx.nodes().parent_node(node.id()); + if let AstKind::CallExpression(_) = parent.kind() { + return remove_console(fixer, ctx, parent); } fixer.noop() }, diff --git a/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs b/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs index 0e0d282a9a1ee..b717a42e66b30 100644 --- a/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs +++ b/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs @@ -110,7 +110,7 @@ impl Rule for NoDupeElseIf { let AstKind::IfStatement(if_stmt) = node.kind() else { return; }; - let Some(AstKind::IfStatement(parent_if_stmt)) = ctx.nodes().parent_kind(node.id()) else { + let AstKind::IfStatement(parent_if_stmt) = ctx.nodes().parent_kind(node.id()) else { return; }; let Some(Statement::IfStatement(child_if_stmt)) = &parent_if_stmt.alternate else { @@ -134,7 +134,8 @@ impl Rule for NoDupeElseIf { .collect(); let mut current_node = node; - while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { + loop { + let parent_node = ctx.nodes().parent_node(current_node.id()); let AstKind::IfStatement(stmt) = parent_node.kind() else { break; }; diff --git a/crates/oxc_linter/src/rules/eslint/no_else_return.rs b/crates/oxc_linter/src/rules/eslint/no_else_return.rs index bb52f16b32b9b..558bc5c118ded 100644 --- a/crates/oxc_linter/src/rules/eslint/no_else_return.rs +++ b/crates/oxc_linter/src/rules/eslint/no_else_return.rs @@ -346,12 +346,8 @@ impl Rule for NoElseReturn { return; }; - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; - if !matches!( - parent_node.kind(), + ctx.nodes().parent_kind(node.id()), AstKind::Program(_) | AstKind::BlockStatement(_) | AstKind::StaticBlock(_) diff --git a/crates/oxc_linter/src/rules/eslint/no_empty.rs b/crates/oxc_linter/src/rules/eslint/no_empty.rs index 5994447d7e390..7719e34d7daf3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty.rs @@ -62,7 +62,7 @@ impl Rule for NoEmpty { match node.kind() { AstKind::BlockStatement(block) if block.body.is_empty() => { let parent = ctx.nodes().parent_kind(node.id()); - if self.allow_empty_catch && matches!(parent, Some(AstKind::CatchClause(_))) { + if self.allow_empty_catch && matches!(parent, AstKind::CatchClause(_)) { return; } @@ -70,30 +70,23 @@ impl Rule for NoEmpty { return; } ctx.diagnostic_with_suggestion(no_empty_diagnostic("block", block.span), |fixer| { - if let Some(parent) = parent { - if let AstKind::TryStatement(try_stmt) = parent { - if let Some(try_block_stmt) = &try_stmt.finalizer { - if try_block_stmt.span == block.span { - return if let Some(finally_kw_start) = - find_finally_start(ctx, block) - { - fixer.delete_range(Span::new( - finally_kw_start, - block.span.end, - )) - } else { - fixer.noop() - }; - } + if let AstKind::TryStatement(try_stmt) = parent { + if let Some(try_block_stmt) = &try_stmt.finalizer { + if try_block_stmt.span == block.span { + return if let Some(finally_kw_start) = + find_finally_start(ctx, block) + { + fixer.delete_range(Span::new(finally_kw_start, block.span.end)) + } else { + fixer.noop() + }; } } - if matches!(parent, AstKind::CatchClause(_)) { - return fixer.noop(); - } - fixer.delete(&parent) - } else { - fixer.noop() } + if matches!(parent, AstKind::CatchClause(_)) { + return fixer.noop(); + } + fixer.delete(&parent) }); } AstKind::SwitchStatement(switch) if switch.cases.is_empty() => { diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index 79db1b0292679..db5a0cc8fa768 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -184,7 +184,7 @@ impl Rule for NoEval { } } AstKind::ThisExpression(_) if !self.allow_indirect => { - let parent = ctx.nodes().parent_node(node.id()).unwrap(); + let parent = ctx.nodes().parent_node(node.id()); let property_info = match parent.kind() { AstKind::StaticMemberExpression(mem_expr) => { Some(mem_expr.static_property_info()) 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 29b0f48d98e9f..18e457eef4b41 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extend_native.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extend_native.rs @@ -214,7 +214,7 @@ fn get_prototype_property_accessed<'a>( let AstKind::IdentifierReference(_) = node.kind() else { return None; }; - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(node.id()); let mut prototype_node = Some(parent); match parent.kind() { prop_access_expr if prop_access_expr.is_member_expression_kind() => { @@ -224,13 +224,11 @@ fn get_prototype_property_accessed<'a>( if prop_name != "prototype" { return None; } - let grandparent_node = ctx.nodes().parent_node(parent.id())?; + let grandparent_node = ctx.nodes().parent_node(parent.id()); if let AstKind::ChainExpression(_) = grandparent_node.kind() { - prototype_node = Some(grandparent_node); - if let Some(grandparent_parent) = ctx.nodes().parent_node(grandparent_node.id()) { - prototype_node = Some(grandparent_parent); - } + let grandparent_parent = ctx.nodes().parent_node(grandparent_node.id()); + prototype_node = Some(grandparent_parent); } prototype_node diff --git a/crates/oxc_linter/src/rules/eslint/no_extra_label.rs b/crates/oxc_linter/src/rules/eslint/no_extra_label.rs index b70a77b5c442e..5869a889a284a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extra_label.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extra_label.rs @@ -109,7 +109,7 @@ fn report_label_if_extra(label: &LabelIdentifier, node: &AstNode, ctx: &LintCont if !is_breakable_statement(nodes.kind(ancestor_id)) { continue; } - let Some(AstKind::LabeledStatement(labeled_stmt)) = nodes.parent_kind(ancestor_id) else { + let AstKind::LabeledStatement(labeled_stmt) = nodes.parent_kind(ancestor_id) else { return; // no need to check outer loops/switches }; if labeled_stmt.label.name != label.name { diff --git a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs index 9e67dd161504b..24304c1780c0a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs +++ b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs @@ -461,7 +461,7 @@ fn get_switch_semantic_cases( .and_then(|it| it.node_id) .map(|id| ctx.nodes().parent_kind(id)) .and_then(|it| match it { - Some(AstKind::SwitchCase(case)) => Some(case), + AstKind::SwitchCase(case) => Some(case), _ => None, }) }) diff --git a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs index 53a8b34523a81..93bbdac06ec22 100644 --- a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs @@ -58,22 +58,17 @@ impl Rule for NoImportAssign { let is_namespace_specifier = matches!(kind, AstKind::ImportNamespaceSpecifier(_)); for reference in symbol_table.get_resolved_references(symbol_id) { if is_namespace_specifier { - let Some(parent_node) = ctx.nodes().parent_node(reference.node_id()) else { - return; - }; + let parent_node = ctx.nodes().parent_node(reference.node_id()); if parent_node.kind().is_member_expression_kind() { let expr = parent_node.kind(); - let Some(parent_parent_node) = ctx.nodes().parent_node(parent_node.id()) - else { - return; - }; + let parent_parent_node = ctx.nodes().parent_node(parent_node.id()); let is_unary_expression_with_delete_operator = |kind| matches!(kind, AstKind::UnaryExpression(expr) if expr.operator == UnaryOperator::Delete); let parent_parent_kind = parent_parent_node.kind(); if matches!(parent_parent_kind, AstKind::SimpleAssignmentTarget(_)) // delete namespace.module || is_unary_expression_with_delete_operator(parent_parent_kind) // delete namespace?.module - || matches!(parent_parent_kind, AstKind::ChainExpression(_) if ctx.nodes().parent_kind(parent_parent_node.id()).is_some_and(is_unary_expression_with_delete_operator)) + || matches!(parent_parent_kind, AstKind::ChainExpression(_) if is_unary_expression_with_delete_operator(ctx.nodes().parent_kind(parent_parent_node.id()))) { if let Some((span, _)) = match expr { AstKind::StaticMemberExpression(expr) => { @@ -118,10 +113,9 @@ impl Rule for NoImportAssign { /// - `Reflect.setPrototypeOf` fn is_argument_of_well_known_mutation_function(node_id: NodeId, ctx: &LintContext<'_>) -> bool { let current_node = ctx.nodes().get_node(node_id); - let call_expression_node = - ctx.nodes().parent_node(node_id).and_then(|node| ctx.nodes().parent_kind(node.id())); + let call_expression_node = ctx.nodes().parent_kind(ctx.nodes().parent_id(node_id)); - let Some(AstKind::CallExpression(expr)) = call_expression_node else { + let AstKind::CallExpression(expr) = call_expression_node else { return false; }; diff --git a/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs b/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs index fbe9d70ba9f5a..5c4a8a9460ebb 100644 --- a/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs @@ -84,7 +84,7 @@ impl Rule for NoInnerDeclarations { _ => return, } - let parent_node = ctx.nodes().parent_node(node.id()).unwrap(); + let parent_node = ctx.nodes().parent_node(node.id()); if matches!( parent_node.kind(), AstKind::Program(_) @@ -98,9 +98,9 @@ impl Rule for NoInnerDeclarations { let mut body = "program"; let mut parent = ctx.nodes().parent_node(parent_node.id()); - while let Some(parent_node) = parent { - let parent_kind = parent_node.kind(); - match parent_kind { + loop { + match parent.kind() { + AstKind::Program(_) => break, AstKind::StaticBlock(_) => { body = "class static block body"; break; @@ -109,7 +109,7 @@ impl Rule for NoInnerDeclarations { body = "function body"; break; } - _ => parent = ctx.nodes().parent_node(parent_node.id()), + _ => parent = ctx.nodes().parent_node(parent.id()), } } diff --git a/crates/oxc_linter/src/rules/eslint/no_lone_blocks.rs b/crates/oxc_linter/src/rules/eslint/no_lone_blocks.rs index 76f58ff4ed277..a2a8bc93b53a8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_lone_blocks.rs +++ b/crates/oxc_linter/src/rules/eslint/no_lone_blocks.rs @@ -57,9 +57,7 @@ impl Rule for NoLoneBlocks { return; }; - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent_node = ctx.nodes().parent_node(node.id()); if stmt.body.is_empty() { let is_comment_in_stmt = diff --git a/crates/oxc_linter/src/rules/eslint/no_lonely_if.rs b/crates/oxc_linter/src/rules/eslint/no_lonely_if.rs index e189b088cfb9e..ce2fb5e3b6927 100644 --- a/crates/oxc_linter/src/rules/eslint/no_lonely_if.rs +++ b/crates/oxc_linter/src/rules/eslint/no_lonely_if.rs @@ -99,7 +99,7 @@ impl Rule for NoLonelyIf { return; }; - if let Some(AstKind::IfStatement(_)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::IfStatement(_) = ctx.nodes().parent_kind(node.id()) { return; } diff --git a/crates/oxc_linter/src/rules/eslint/no_magic_numbers.rs b/crates/oxc_linter/src/rules/eslint/no_magic_numbers.rs index 970e233ab1f76..8dcb97156a9a7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_magic_numbers.rs +++ b/crates/oxc_linter/src/rules/eslint/no_magic_numbers.rs @@ -294,13 +294,13 @@ impl Rule for NoMagicNumbers { } let nodes = ctx.nodes(); - let config = InternConfig::from(node, nodes.parent_node(node.id()).unwrap()); + let config = InternConfig::from(node, nodes.parent_node(node.id())); if self.is_skipable(&config, nodes) { return; } - let parent_kind = nodes.parent_node(config.node.id()).unwrap().kind(); + let parent_kind = nodes.parent_kind(config.node.id()); let span = config.node.kind().span(); let Some(reason) = self.get_report_reason(&parent_kind) else { @@ -400,7 +400,7 @@ fn is_ts_numeric_literal<'a>(parent_parent_node: &AstNode<'a>, nodes: &AstNodes< node.kind(), AstKind::TSUnionType(_) | AstKind::TSIntersectionType(_) | AstKind::TSParenthesizedType(_) ) { - node = nodes.parent_node(node.id()).unwrap(); + node = nodes.parent_node(node.id()); } matches!(node.kind(), AstKind::TSTypeAliasDeclaration(_)) @@ -417,7 +417,7 @@ fn is_ts_indexed_access_type<'a>(parent_parent_node: &AstNode<'a>, nodes: &AstNo node.kind(), AstKind::TSUnionType(_) | AstKind::TSIntersectionType(_) | AstKind::TSParenthesizedType(_) ) { - node = nodes.parent_node(node.id()).unwrap(); + node = nodes.parent_node(node.id()); } matches!(node.kind(), AstKind::TSIndexedAccessType(_)) @@ -429,7 +429,7 @@ impl NoMagicNumbers { return true; } - let parent = nodes.parent_node(config.node.id()).unwrap(); + let parent = nodes.parent_node(config.node.id()); let parent_kind = parent.kind(); if self.ignore_enums && is_ts_enum(&parent_kind) { @@ -452,7 +452,7 @@ impl NoMagicNumbers { return true; } - let parent_parent = nodes.parent_node(parent.id()).unwrap(); + let parent_parent = nodes.parent_node(parent.id()); if is_parse_int_radix(parent_parent) { return true; diff --git a/crates/oxc_linter/src/rules/eslint/no_multi_str.rs b/crates/oxc_linter/src/rules/eslint/no_multi_str.rs index 4579c88f6cb19..7bbd1fc84fdd7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_multi_str.rs +++ b/crates/oxc_linter/src/rules/eslint/no_multi_str.rs @@ -53,7 +53,7 @@ impl Rule for NoMultiStr { } fn is_within_jsx_attribute(id: NodeId, ctx: &LintContext) -> bool { - matches!(ctx.nodes().parent_kind(id), Some(AstKind::JSXAttribute(_))) + matches!(ctx.nodes().parent_kind(id), AstKind::JSXAttribute(_)) } #[test] diff --git a/crates/oxc_linter/src/rules/eslint/no_plusplus.rs b/crates/oxc_linter/src/rules/eslint/no_plusplus.rs index 0a1a08c40fe57..812e20e5ea6ff 100644 --- a/crates/oxc_linter/src/rules/eslint/no_plusplus.rs +++ b/crates/oxc_linter/src/rules/eslint/no_plusplus.rs @@ -116,8 +116,7 @@ impl Rule for NoPlusplus { return; }; - if self.allow_for_loop_afterthoughts && is_for_loop_afterthought(node, ctx).unwrap_or(false) - { + if self.allow_for_loop_afterthoughts && is_for_loop_afterthought(node, ctx) { return; } @@ -144,14 +143,14 @@ impl Rule for NoPlusplus { /// - An operand of a sequence expression that is the update node: for (;; foo(), i++) {} /// - An operand of a sequence expression that is child of another sequence expression, etc., /// up to the sequence expression that is the update node: for (;; foo(), (bar(), (baz(), i++))) {} -fn is_for_loop_afterthought(node: &AstNode, ctx: &LintContext) -> Option { - let mut cur = ctx.nodes().parent_node(node.id())?; +fn is_for_loop_afterthought(node: &AstNode, ctx: &LintContext) -> bool { + let mut cur = ctx.nodes().parent_node(node.id()); while let AstKind::SequenceExpression(_) | AstKind::ParenthesizedExpression(_) = cur.kind() { - cur = ctx.nodes().parent_node(cur.id())?; + cur = ctx.nodes().parent_node(cur.id()); } - Some(matches!(cur.kind(), AstKind::ForStatement(stmt) if stmt.update.is_some())) + matches!(cur.kind(), AstKind::ForStatement(stmt) if stmt.update.is_some()) } #[test] diff --git a/crates/oxc_linter/src/rules/eslint/no_return_assign.rs b/crates/oxc_linter/src/rules/eslint/no_return_assign.rs index a220ec2076016..15cb1bb591404 100644 --- a/crates/oxc_linter/src/rules/eslint/no_return_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_return_assign.rs @@ -69,36 +69,35 @@ impl Rule for NoReturnAssign { return; }; if !self.always_disallow_assignment_in_return - && ctx - .nodes() - .parent_node(node.id()) - .is_some_and(|node| node.kind().as_parenthesized_expression().is_some()) + && ctx.nodes().parent_kind(node.id()).as_parenthesized_expression().is_some() { return; } let mut parent_node = ctx.nodes().parent_node(node.id()); - while parent_node.is_some_and(|parent| !is_sentinel_node(parent.kind())) { - parent_node = ctx.nodes().parent_node(parent_node.unwrap().id()); + while !is_sentinel_node(parent_node.kind()) { + if matches!(parent_node.kind(), AstKind::Program(_)) { + break; + } + parent_node = ctx.nodes().parent_node(parent_node.id()); } - if let Some(parent) = parent_node { - match parent.kind() { - AstKind::ReturnStatement(_) => { + + match parent_node.kind() { + AstKind::ReturnStatement(_) => { + ctx.diagnostic(no_return_assign_diagnostic( + assign.span(), + "Return statements should not contain an assignment.", + )); + } + AstKind::ArrowFunctionExpression(arrow) => { + if arrow.expression { ctx.diagnostic(no_return_assign_diagnostic( assign.span(), - "Return statements should not contain an assignment.", + "Arrow functions should not return an assignment.", )); } - AstKind::ArrowFunctionExpression(arrow) => { - if arrow.expression { - ctx.diagnostic(no_return_assign_diagnostic( - assign.span(), - "Arrow functions should not return an assignment.", - )); - } - } - _ => (), } + _ => (), } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_script_url.rs b/crates/oxc_linter/src/rules/eslint/no_script_url.rs index 0d39f92ffbc4c..54e29fcefcb90 100644 --- a/crates/oxc_linter/src/rules/eslint/no_script_url.rs +++ b/crates/oxc_linter/src/rules/eslint/no_script_url.rs @@ -74,7 +74,7 @@ impl Rule for NoScriptUrl { fn is_tagged_template_expression(ctx: &LintContext, node: &AstNode, literal_span: Span) -> bool { matches!( ctx.nodes().parent_kind(node.id()), - Some(AstKind::TaggedTemplateExpression(expr)) if expr.quasi.span == literal_span + AstKind::TaggedTemplateExpression(expr) if expr.quasi.span == literal_span ) } diff --git a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs index b084c3ca5571f..e34840a832cdc 100644 --- a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs +++ b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs @@ -76,14 +76,12 @@ impl Rule for NoThisBeforeSuper { } AstKind::Super(_) => { let basic_block_id = node.cfg_id(); - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if let AstKind::CallExpression(call_expr) = parent.kind() { - let has_this_or_super_in_args = - Self::contains_this_or_super_in_args(&call_expr.arguments); + if let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(node.id()) { + let has_this_or_super_in_args = + Self::contains_this_or_super_in_args(&call_expr.arguments); - if !has_this_or_super_in_args { - basic_blocks_with_super_called.insert(basic_block_id); - } + if !has_this_or_super_in_args { + basic_blocks_with_super_called.insert(basic_block_id); } } if !basic_blocks_with_super_called.contains(&basic_block_id) { @@ -129,7 +127,7 @@ impl Rule for NoThisBeforeSuper { // the parent must exist, because of Self::is_wanted_node // so the unwrap() is safe here. The parent node is the // AstKind::MethodDefinition for `constructor`. - let parent_span = ctx.nodes().parent_node(node.id()).unwrap().kind().span(); + let parent_span = ctx.nodes().parent_kind(node.id()).span(); ctx.diagnostic(no_this_before_super_diagnostic(parent_span)); } } @@ -138,12 +136,12 @@ impl Rule for NoThisBeforeSuper { impl NoThisBeforeSuper { fn is_wanted_node(node: &AstNode, ctx: &LintContext<'_>) -> Option { - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(node.id()); let method_def = parent.kind().as_method_definition()?; if matches!(method_def.kind, MethodDefinitionKind::Constructor) { - let parent_2 = ctx.nodes().parent_node(parent.id())?; - let parent_3 = ctx.nodes().parent_node(parent_2.id())?; + let parent_2 = ctx.nodes().parent_node(parent.id()); + let parent_3 = ctx.nodes().parent_node(parent_2.id()); let class = parent_3.kind().as_class()?; let super_class = class.super_class.as_ref()?; diff --git a/crates/oxc_linter/src/rules/eslint/no_undef.rs b/crates/oxc_linter/src/rules/eslint/no_undef.rs index 396fd0ee0c979..2e556b77b3517 100644 --- a/crates/oxc_linter/src/rules/eslint/no_undef.rs +++ b/crates/oxc_linter/src/rules/eslint/no_undef.rs @@ -79,11 +79,12 @@ impl Rule for NoUndef { } fn has_typeof_operator(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { - ctx.nodes().parent_node(node.id()).is_some_and(|parent| match parent.kind() { + let parent = ctx.nodes().parent_node(node.id()); + match parent.kind() { AstKind::UnaryExpression(expr) => expr.operator == UnaryOperator::Typeof, AstKind::ParenthesizedExpression(_) => has_typeof_operator(parent, ctx), _ => false, - }) + } } #[test] diff --git a/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs b/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs index 96ae0f5328a46..8a8cd7d6ac00b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs @@ -113,7 +113,7 @@ impl Rule for NoUnexpectedMultiline { if call_expr.optional { return; } - if let Some(AstKind::ChainExpression(_)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::ChainExpression(_) = ctx.nodes().parent_kind(node.id()) { return; } @@ -175,7 +175,7 @@ impl Rule for NoUnexpectedMultiline { if binary_expr.operator != BinaryOperator::Division { return; } - let Some(AstKind::BinaryExpression(parent_binary_expr)) = + let AstKind::BinaryExpression(parent_binary_expr) = ctx.nodes().parent_kind(node.id()) else { return; diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs index 51379e4a52c34..d6fff338fe6eb 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs @@ -79,8 +79,7 @@ impl Rule for NoUnsafeFinally { break; } - let Some(parent_node_id) = nodes.parent_id(node_id) else { break }; - let parent_kind = nodes.kind(parent_node_id); + let parent_kind = nodes.parent_kind(node_id); if let AstKind::LabeledStatement(labeled_stmt) = parent_kind { if label_name == Some(&labeled_stmt.label.name) { @@ -90,7 +89,7 @@ impl Rule for NoUnsafeFinally { // Finally Block let parent_parent_kind = nodes.parent_kind(node_id); - if let Some(AstKind::TryStatement(try_stmt)) = parent_parent_kind { + if let AstKind::TryStatement(try_stmt) = parent_parent_kind { if let Some(try_block_stmt) = &try_stmt.finalizer { if let AstKind::BlockStatement(block_stmt) = ast_kind { if try_block_stmt.span == block_stmt.span { diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs b/crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs index 4cedcaf25d8fb..2f4c4cdc3b944 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs @@ -93,11 +93,11 @@ impl Rule for NoUnusedExpressions { } fn is_parent_arrow_function_expression<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { return false }; + let parent = ctx.nodes().parent_node(node.id()); let AstKind::FunctionBody(_) = parent.kind() else { return false }; - let Some(grand_parent) = ctx.nodes().parent_node(parent.id()) else { return false }; + let grand_parent = ctx.nodes().parent_node(parent.id()); let AstKind::ArrowFunctionExpression(arrow_function_expression) = grand_parent.kind() else { return false; 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 8665f884ed0cd..1000cc61e7fda 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 @@ -114,7 +114,7 @@ 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).unwrap_or(current_node_id)) + .ancestors(nodes.parent_id(current_node_id)) .tuple_windows::<(&AstNode<'_>, &AstNode<'_>)>() { match (curr.kind(), parent.kind()) { @@ -141,10 +141,7 @@ fn is_read(current_node_id: NodeId, nodes: &AstNodes) -> bool { } (AstKind::AssignmentTarget(_), AstKind::AssignmentExpression(_)) | (_, AstKind::UpdateExpression(_)) => { - return !matches!( - nodes.parent_kind(parent.id()), - Some(AstKind::ExpressionStatement(_)) - ); + return !matches!(nodes.parent_kind(parent.id()), AstKind::ExpressionStatement(_)); } _ => return true, } 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 8c60abd367b1c..650b69567ca8a 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 @@ -115,7 +115,7 @@ impl NoUnusedVars { symbol: &Symbol<'_, '_>, declaration_id: NodeId, ) -> bool { - matches!(symbol.nodes().parent_kind(declaration_id), Some(AstKind::TSMappedType(_))) + matches!(symbol.nodes().parent_kind(declaration_id), AstKind::TSMappedType(_)) } /// Returns `true` if this unused parameter should be allowed (i.e. not diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs index 49fb6ceae0ebc..9aecf2e8dee5b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs @@ -35,12 +35,7 @@ impl NoUnusedVars { return fixer.noop(); } - let Some(parent) = symbol.nodes().parent_node(decl_id) else { - #[cfg(debug_assertions)] - panic!("VariableDeclarator nodes should always have a parent node"); - #[cfg(not(debug_assertions))] - return fixer.noop(); - }; + let parent = symbol.nodes().parent_node(decl_id); let (span, declarations) = match parent.kind() { AstKind::VariableDeclaration(decl) => (decl.span, &decl.declarations), _ => { @@ -53,10 +48,9 @@ impl NoUnusedVars { } }; - if let Some( - AstKind::ForOfStatement(ForOfStatement { span, .. }) - | AstKind::ForInStatement(ForInStatement { span, .. }), - ) = symbol.nodes().parent_kind(parent.id()) + if let AstKind::ForOfStatement(ForOfStatement { span, .. }) + | AstKind::ForInStatement(ForInStatement { span, .. }) = + symbol.nodes().parent_kind(parent.id()) { if span.contains_inclusive(symbol.span()) { if let Some(new_name) = self.get_unused_var_name(symbol) { 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 f9d3d971f9b3e..45ac111e9f7ea 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 @@ -848,12 +848,7 @@ impl<'a> Symbol<'_, 'a> { node = match node.kind() { AstKind::TSTypeQuery(_) => return true, AstKind::TSQualifiedName(_) | AstKind::IdentifierReference(_) => { - if let Some(parent) = self.nodes().parent_node(node.id()) { - parent - } else { - debug_assert!(false); - return false; - } + self.nodes().parent_node(node.id()) } _ => return false, }; diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs index 51c7c165794e9..5e0c38634809d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs @@ -118,7 +118,7 @@ impl Rule for NoUselessEscape { literal.span.start, &check_string(literal.span.source_text(ctx.source_text())), ), - AstKind::TemplateLiteral(literal) if !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::TaggedTemplateExpression(expr)) if expr.quasi.span == literal.span) => { + AstKind::TemplateLiteral(literal) if !matches!(ctx.nodes().parent_kind(node.id()), AstKind::TaggedTemplateExpression(expr) if expr.quasi.span == literal.span) => { for template_element in &literal.quasis { check( ctx, @@ -153,7 +153,7 @@ impl Rule for NoUselessEscape { } fn is_within_jsx_attribute(id: NodeId, ctx: &LintContext) -> bool { - matches!(ctx.nodes().parent_kind(id), Some(AstKind::JSXAttribute(_))) + matches!(ctx.nodes().parent_kind(id), AstKind::JSXAttribute(_)) } #[expect(clippy::cast_possible_truncation)] diff --git a/crates/oxc_linter/src/rules/eslint/no_var.rs b/crates/oxc_linter/src/rules/eslint/no_var.rs index f97196a71dd35..f5d8cf42621c1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_var.rs +++ b/crates/oxc_linter/src/rules/eslint/no_var.rs @@ -59,7 +59,7 @@ impl Rule for NoVar { let is_written_to = dec.declarations.iter().any(|v| is_written_to(&v.id, ctx)); let span = Span::sized(dec.span.start, 3); ctx.diagnostic_with_fix(no_var_diagnostic(span), |fixer| { - let parent_span = ctx.nodes().parent_kind(node.id()).unwrap().span(); + let parent_span = ctx.nodes().parent_kind(node.id()).span(); if dec.declarations.iter().any(|decl| { decl.id.get_binding_identifiers().iter().any(|ident| { ctx.symbol_references(ident.symbol_id()).any(|id| { diff --git a/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs b/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs index b32c0d606f681..b86e365a064e5 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs @@ -145,14 +145,12 @@ impl Rule for PreferObjectSpread { let needs_paren = !matches!( ctx.nodes().parent_kind(node.id()), - Some( - AstKind::VariableDeclarator(_) - | AstKind::ArrayExpression(_) - | AstKind::ReturnStatement(_) - | AstKind::Argument(_) - | AstKind::ObjectProperty(_) - | AstKind::AssignmentExpression(_) - ) + AstKind::VariableDeclarator(_) + | AstKind::ArrayExpression(_) + | AstKind::ReturnStatement(_) + | AstKind::Argument(_) + | AstKind::ObjectProperty(_) + | AstKind::AssignmentExpression(_) ); let Some(callee_left_paren_span) = find_char_span(ctx, call_expr, b'(') else { diff --git a/crates/oxc_linter/src/rules/eslint/prefer_promise_reject_errors.rs b/crates/oxc_linter/src/rules/eslint/prefer_promise_reject_errors.rs index 88a6593262164..c2399c7728d80 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_promise_reject_errors.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_promise_reject_errors.rs @@ -148,10 +148,8 @@ fn check_reject_in_function( }; ctx.symbol_references(reject_arg.symbol_id()).for_each(|reference| { - let Some(node) = ctx.nodes().parent_node(reference.node_id()) else { - return; - }; - if let AstKind::CallExpression(call_expr) = node.kind() { + if let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(reference.node_id()) + { check_reject_call(call_expr, ctx, allow_empty_reject); } }); @@ -168,7 +166,7 @@ fn check_reject_in_function( continue; } - let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else { continue }; + let parent = ctx.nodes().parent_node(reference.node_id()); let AstKind::ComputedMemberExpression(member_expr) = parent.kind() else { continue; }; @@ -181,11 +179,7 @@ fn check_reject_in_function( continue; } - let Some(node) = ctx.nodes().parent_node(parent.id()) else { - continue; - }; - - if let AstKind::CallExpression(call_expr) = node.kind() { + if let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(parent.id()) { check_reject_call(call_expr, ctx, allow_empty_reject); } } diff --git a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs index 75468e8b7309d..7e2a1fc2c8029 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs @@ -83,7 +83,8 @@ impl Rule for PreferRestParams { fn is_inside_of_function(node: &AstNode, ctx: &LintContext) -> bool { let mut current = node; - while let Some(parent) = ctx.nodes().parent_node(current.id()) { + while !matches!(current.kind(), AstKind::Program(_)) { + let parent = ctx.nodes().parent_node(current.id()); if matches!(parent.kind(), AstKind::Function(_)) { return true; } @@ -93,11 +94,8 @@ fn is_inside_of_function(node: &AstNode, ctx: &LintContext) -> bool { } fn is_not_normal_member_access(identifier: &AstNode, ctx: &LintContext) -> bool { - let parent = ctx.nodes().parent_node(identifier.id()); - if let Some(parent) = parent { - if let AstKind::StaticMemberExpression(member) = parent.kind() { - return member.object.span() == identifier.span(); - } + if let AstKind::StaticMemberExpression(member) = ctx.nodes().parent_kind(identifier.id()) { + return member.object.span() == identifier.span(); } false } diff --git a/crates/oxc_linter/src/rules/eslint/require_await.rs b/crates/oxc_linter/src/rules/eslint/require_await.rs index 3b6be1cf4e745..72af56a00d24e 100644 --- a/crates/oxc_linter/src/rules/eslint/require_await.rs +++ b/crates/oxc_linter/src/rules/eslint/require_await.rs @@ -84,9 +84,7 @@ impl Rule for RequireAwait { if body.is_empty() { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); match parent.kind() { AstKind::Function(func) => { @@ -104,10 +102,10 @@ impl Rule for RequireAwait { ); } else { let parent_parent_node = ctx.nodes().parent_kind(parent.id()); - if let Some( - AstKind::ObjectProperty(ObjectProperty { span, key, .. }) - | AstKind::MethodDefinition(MethodDefinition { span, key, .. }), - ) = parent_parent_node + if let AstKind::ObjectProperty(ObjectProperty { span, key, .. }) + | AstKind::MethodDefinition(MethodDefinition { + span, key, .. + }) = parent_parent_node { let need_delete_span = get_delete_span(ctx, span.start); let check_span = if matches!(key, PropertyKey::StaticIdentifier(_)) diff --git a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs index 61db10fb5b215..44ceaa8d4f718 100644 --- a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs +++ b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs @@ -104,7 +104,7 @@ impl Rule for ValidTypeof { }; let binary_expr = match ctx.nodes().parent_kind(node.id()) { - Some(AstKind::BinaryExpression(binary_expr)) if binary_expr.operator.is_equality() => { + AstKind::BinaryExpression(binary_expr) if binary_expr.operator.is_equality() => { binary_expr } _ => return, diff --git a/crates/oxc_linter/src/rules/eslint/vars_on_top.rs b/crates/oxc_linter/src/rules/eslint/vars_on_top.rs index 31fe8e3b9a83a..d7e91f98913bb 100644 --- a/crates/oxc_linter/src/rules/eslint/vars_on_top.rs +++ b/crates/oxc_linter/src/rules/eslint/vars_on_top.rs @@ -99,16 +99,13 @@ impl Rule for VarsOnTop { if declaration.kind != VariableDeclarationKind::Var { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); match parent.kind() { AstKind::ExportNamedDeclaration(_) => { - if let Some(grand_parent) = ctx.nodes().parent_node(parent.id()) { - if let AstKind::Program(grand_parent) = grand_parent.kind() { - global_var_check(parent, grand_parent, ctx); - } + let grand_parent = ctx.nodes().parent_node(parent.id()); + if let AstKind::Program(grand_parent) = grand_parent.kind() { + global_var_check(parent, grand_parent, ctx); } } AstKind::Program(parent) => { @@ -150,14 +147,12 @@ fn is_var_on_top(node: &AstNode, statements: &[Statement], ctx: &LintContext) -> let len = statements.len(); let parent = ctx.nodes().parent_node(node.id()); - if let Some(parent) = parent { - if !matches!(parent.kind(), AstKind::StaticBlock(_)) { - while i < len { - if !looks_like_directive(&statements[i]) && !looks_like_import(&statements[i]) { - break; - } - i += 1; + if !matches!(parent.kind(), AstKind::StaticBlock(_)) { + while i < len { + if !looks_like_directive(&statements[i]) && !looks_like_import(&statements[i]) { + break; } + i += 1; } } @@ -184,25 +179,24 @@ fn global_var_check(node: &AstNode, parent: &Program, ctx: &LintContext) { } fn block_scope_var_check(node: &AstNode, ctx: &LintContext) { - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - match parent.kind() { - AstKind::BlockStatement(block) => { - if check_var_on_top_in_function_scope(node, &block.body, parent, ctx) { - return; - } + let parent = ctx.nodes().parent_node(node.id()); + match parent.kind() { + AstKind::BlockStatement(block) => { + if check_var_on_top_in_function_scope(node, &block.body, parent, ctx) { + return; } - AstKind::FunctionBody(block) => { - if check_var_on_top_in_function_scope(node, &block.statements, parent, ctx) { - return; - } + } + AstKind::FunctionBody(block) => { + if check_var_on_top_in_function_scope(node, &block.statements, parent, ctx) { + return; } - AstKind::StaticBlock(block) => { - if is_var_on_top(node, &block.body, ctx) { - return; - } + } + AstKind::StaticBlock(block) => { + if is_var_on_top(node, &block.body, ctx) { + return; } - _ => {} } + _ => {} } ctx.diagnostic(vars_on_top_diagnostic(node.span())); } @@ -213,14 +207,13 @@ fn check_var_on_top_in_function_scope( parent: &AstNode, ctx: &LintContext, ) -> bool { - if let Some(grandparent) = ctx.nodes().parent_node(parent.id()) { - if matches!( - grandparent.kind(), - AstKind::Function(_) | AstKind::FunctionBody(_) | AstKind::ArrowFunctionExpression(_) - ) && is_var_on_top(node, statements, ctx) - { - return true; - } + let grandparent = ctx.nodes().parent_node(parent.id()); + if matches!( + grandparent.kind(), + AstKind::Function(_) | AstKind::FunctionBody(_) | AstKind::ArrowFunctionExpression(_) + ) && is_var_on_top(node, statements, ctx) + { + return true; } false diff --git a/crates/oxc_linter/src/rules/eslint/yoda.rs b/crates/oxc_linter/src/rules/eslint/yoda.rs index e7c57491a0b2a..d51b2207be496 100644 --- a/crates/oxc_linter/src/rules/eslint/yoda.rs +++ b/crates/oxc_linter/src/rules/eslint/yoda.rs @@ -218,16 +218,15 @@ impl Rule for Yoda { return; }; - if let Some(parent_node) = ctx.nodes().parent_node(node.id()) { - if let AstKind::LogicalExpression(logical_expr) = parent_node.kind() { - let parent_logical_expr = ctx.nodes().parent_node(parent_node.id()); - - if self.except_range - && parent_logical_expr.is_some_and(|e| is_parenthesized(e)) - && is_range(logical_expr, ctx) - { - return; - } + let parent_node = ctx.nodes().parent_node(node.id()); + if let AstKind::LogicalExpression(logical_expr) = parent_node.kind() { + let parent_logical_expr = ctx.nodes().parent_node(parent_node.id()); + + if self.except_range + && is_parenthesized(parent_logical_expr) + && is_range(logical_expr, ctx) + { + return; } } diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 3c336db97c3ef..5df3c44ddb20c 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -166,55 +166,54 @@ impl Rule for Namespace { }; ctx.scoping().get_resolved_references(symbol_id).for_each(|reference| { - if let Some(node) = ctx.nodes().parent_node(reference.node_id()) { - let name = entry.local_name.name(); - - match node.kind() { - member if member.is_member_expression_kind() => { - if matches!( - ctx.nodes().parent_kind(node.id()), - Some(AstKind::SimpleAssignmentTarget(_)) - ) { - ctx.diagnostic(assignment(member.span(), name)); - } - - if !self.allow_computed - && matches!(member, AstKind::ComputedMemberExpression(_)) - { - return ctx.diagnostic(computed_reference(member.span(), name)); - } - - check_deep_namespace_for_node( - node, - &source, - vec![entry.local_name.name().to_string()].as_slice(), - &module, - ctx, - ); + let parent = ctx.nodes().parent_node(reference.node_id()); + let name = entry.local_name.name(); + + match parent.kind() { + member if member.is_member_expression_kind() => { + if matches!( + ctx.nodes().parent_kind(parent.id()), + AstKind::SimpleAssignmentTarget(_) + ) { + ctx.diagnostic(assignment(member.span(), name)); } - AstKind::JSXMemberExpression(expr) => { - check_binding_exported( - &expr.property.name, - || no_export(expr.property.span, &expr.property.name, &source), - &module, - ctx, - ); - } - AstKind::VariableDeclarator(decl) => { - let BindingPatternKind::ObjectPattern(pattern) = &decl.id.kind else { - return; - }; - - check_deep_namespace_for_object_pattern( - pattern, - &source, - &[entry.local_name.name().to_string()], - &module, - ctx, - ); + + if !self.allow_computed + && matches!(member, AstKind::ComputedMemberExpression(_)) + { + return ctx.diagnostic(computed_reference(member.span(), name)); } - _ => {} + + check_deep_namespace_for_node( + parent, + &source, + vec![entry.local_name.name().to_string()].as_slice(), + &module, + ctx, + ); + } + AstKind::JSXMemberExpression(expr) => { + check_binding_exported( + &expr.property.name, + || no_export(expr.property.span, &expr.property.name, &source), + &module, + ctx, + ); + } + AstKind::VariableDeclarator(decl) => { + let BindingPatternKind::ObjectPattern(pattern) = &decl.id.kind else { + return; + }; + + check_deep_namespace_for_object_pattern( + pattern, + &source, + &[entry.local_name.name().to_string()], + &module, + ctx, + ); } + _ => {} } }); } @@ -276,7 +275,7 @@ fn check_deep_namespace_for_node( }; if let Some(module_source) = get_module_request_name(name, module) { - let parent_node = ctx.nodes().parent_node(node.id())?; + let parent_node = ctx.nodes().parent_node(node.id()); let loaded_modules = module.loaded_modules.read().unwrap(); let module_record = loaded_modules.get(module_source.as_str())?; let mut namespaces = namespaces.to_owned(); diff --git a/crates/oxc_linter/src/rules/import/no_commonjs.rs b/crates/oxc_linter/src/rules/import/no_commonjs.rs index 3bbbee9555346..57817689531f0 100644 --- a/crates/oxc_linter/src/rules/import/no_commonjs.rs +++ b/crates/oxc_linter/src/rules/import/no_commonjs.rs @@ -118,10 +118,12 @@ fn is_conditional(parent_node: &AstNode, ctx: &LintContext) -> bool { if is_cond { true } else { - let Some(parent) = ctx.nodes().parent_node(parent_node.id()) else { - return false; - }; - is_conditional(parent, ctx) + let parent = ctx.nodes().parent_node(parent_node.id()); + if matches!(parent.kind(), AstKind::Program(_)) { + false + } else { + is_conditional(parent, ctx) + } } } /// @@ -232,9 +234,7 @@ impl Rule for NoCommonjs { return; } - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent_node = ctx.nodes().parent_node(node.id()); if self.allow_conditional_require && is_conditional(parent_node, ctx) { return; diff --git a/crates/oxc_linter/src/rules/import/no_mutable_exports.rs b/crates/oxc_linter/src/rules/import/no_mutable_exports.rs index b34d592d78860..d7684fab09551 100644 --- a/crates/oxc_linter/src/rules/import/no_mutable_exports.rs +++ b/crates/oxc_linter/src/rules/import/no_mutable_exports.rs @@ -116,12 +116,9 @@ fn get_reference_declaration<'a>( let reference_node = ctx.symbol_declaration(symbol_id); if matches!(reference_node.kind(), AstKind::VariableDeclarator(_)) { // we need return reference_node's parent node - if let Some(parent) = ctx.nodes().parent_node(reference_node.id()) { - if let AstKind::VariableDeclaration(decl) = parent.kind() { - if matches!(decl.kind, VariableDeclarationKind::Let | VariableDeclarationKind::Var) - { - return Some(decl); - } + if let AstKind::VariableDeclaration(decl) = ctx.nodes().parent_kind(reference_node.id()) { + if matches!(decl.kind, VariableDeclarationKind::Let | VariableDeclarationKind::Var) { + return Some(decl); } } } diff --git a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs index 7844acfef07d6..868624b049e50 100644 --- a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs +++ b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs @@ -139,9 +139,7 @@ fn check_parents<'a>( return InConditional(false); } - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return InConditional(false); - }; + let parent_node = ctx.nodes().parent_node(node.id()); match parent_node.kind() { AstKind::CallExpression(call_expr) => { @@ -185,9 +183,7 @@ fn check_parents<'a>( // To avoid infinite loop, we need to check if the function is already visited when // call `check_parents`. let boolean = symbol_table.get_resolved_references(symbol_id).any(|reference| { - let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else { - return false; - }; + let parent = ctx.nodes().parent_node(reference.node_id()); matches!(check_parents(parent, visited, in_conditional, ctx), InConditional(true)) }); return InConditional(boolean); diff --git a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs index 3ff11bc3066de..2e92ed75a93d5 100644 --- a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs +++ b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs @@ -137,9 +137,7 @@ fn collect_jest_reference_id( if !is_jest_call(ctx.semantic().reference_name(reference)) { continue; } - let Some(parent_node) = nodes.parent_node(reference.node_id()) else { - continue; - }; + let parent_node = nodes.parent_node(reference.node_id()); if !parent_node.kind().is_member_expression_kind() { continue; } @@ -161,9 +159,7 @@ fn handle_jest_set_time_out<'a>( for reference_id in reference_id_list { let reference = symbol_table.get_reference(reference_id); - let Some(parent_node) = nodes.parent_node(reference.node_id()) else { - continue; - }; + let parent_node = nodes.parent_node(reference.node_id()); if !is_jest_call(ctx.semantic().reference_name(reference)) { if is_jest_fn_call(parent_node, id_to_jest_node_map, ctx) { @@ -202,17 +198,11 @@ fn is_jest_fn_call<'a>( let mut id = parent_node.id(); loop { let parent = ctx.nodes().parent_node(id); - if let Some(parent) = parent { - let parent_kind = parent.kind(); - if matches!( - parent_kind, - AstKind::CallExpression(_) | AstKind::TaggedTemplateExpression(_) - ) || parent_kind.is_member_expression_kind() - { - id = parent.id(); - } else { - break; - } + let parent_kind = parent.kind(); + if matches!(parent_kind, AstKind::CallExpression(_) | AstKind::TaggedTemplateExpression(_)) + || parent_kind.is_member_expression_kind() + { + id = parent.id(); } else { break; } diff --git a/crates/oxc_linter/src/rules/jest/no_identical_title.rs b/crates/oxc_linter/src/rules/jest/no_identical_title.rs index b6d7318a7b022..f880f2eda46d2 100644 --- a/crates/oxc_linter/src/rules/jest/no_identical_title.rs +++ b/crates/oxc_linter/src/rules/jest/no_identical_title.rs @@ -162,7 +162,7 @@ fn get_closest_block(node: &AstNode, ctx: &LintContext) -> Option { Some(node.id()) } _ => { - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(node.id()); get_closest_block(parent, ctx) } } diff --git a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs index 89c23c8b22e24..c61c14a53c036 100644 --- a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs +++ b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs @@ -62,10 +62,8 @@ impl Rule for NoMocksImport { for &reference_id in require_reference_ids { let reference = ctx.scoping().get_reference(reference_id); - let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else { - return; - }; - let AstKind::CallExpression(call_expr) = parent.kind() else { + let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(reference.node_id()) + else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs b/crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs index be4f91b1ae4b1..a61de4a70c126 100644 --- a/crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs +++ b/crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs @@ -133,19 +133,14 @@ fn is_correct_place_to_call_expect<'a>( id_nodes_mapping: &FxHashMap>, ctx: &LintContext<'a>, ) -> Option<()> { - let mut parent = ctx.nodes().parent_node(node.id())?; + let mut parent = ctx.nodes().parent_node(node.id()); // loop until find the closest function body - loop { - if matches!(parent.kind(), AstKind::FunctionBody(_)) { - break; - } - - parent = ctx.nodes().parent_node(parent.id())?; + while !matches!(parent.kind(), AstKind::FunctionBody(_) | AstKind::Program(_)) { + parent = ctx.nodes().parent_node(parent.id()); } - let node = parent; - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(parent.id()); match parent.kind() { AstKind::Function(function) => { @@ -155,7 +150,7 @@ fn is_correct_place_to_call_expect<'a>( } if function.is_expression() { - let grandparent = ctx.nodes().parent_node(parent.id())?; + let grandparent = ctx.nodes().parent_node(parent.id()); // `test('foo', function () { expect(1).toBe(1) })` // `const foo = function() {expect(1).toBe(1)}` @@ -172,7 +167,7 @@ fn is_correct_place_to_call_expect<'a>( } } AstKind::ArrowFunctionExpression(_) => { - let grandparent = ctx.nodes().parent_node(parent.id())?; + let grandparent = ctx.nodes().parent_node(parent.id()); // `test('foo', () => expect(1).toBe(1))` // `const foo = () => expect(1).toBe(1)` return if is_var_declarator_or_test_block( @@ -217,7 +212,8 @@ fn is_var_declarator_or_test_block<'a>( } AstKind::Argument(_) | AstKind::ArrayExpression(_) | AstKind::ObjectExpression(_) => { let mut current = node; - while let Some(parent) = ctx.nodes().parent_node(current.id()) { + loop { + let parent = ctx.nodes().parent_node(current.id()); match parent.kind() { AstKind::CallExpression(_) | AstKind::VariableDeclarator(_) => { return is_var_declarator_or_test_block( diff --git a/crates/oxc_linter/src/rules/jest/prefer_jest_mocked.rs b/crates/oxc_linter/src/rules/jest/prefer_jest_mocked.rs index 9b805b35acd81..2347ea3a50e09 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_jest_mocked.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_jest_mocked.rs @@ -57,7 +57,7 @@ declare_oxc_lint!( impl Rule for PreferJestMocked { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::TSAsExpression(ts_expr) = node.kind() { - if !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::TSAsExpression(_))) { + if !matches!(ctx.nodes().parent_kind(node.id()), AstKind::TSAsExpression(_)) { Self::check_ts_as_expression(node, ts_expr, ctx); } } else if let AstKind::TSTypeAssertion(assert_type) = node.kind() { diff --git a/crates/oxc_linter/src/rules/jest/valid_expect.rs b/crates/oxc_linter/src/rules/jest/valid_expect.rs index 8821c70873f07..f49d1d6598fed 100644 --- a/crates/oxc_linter/src/rules/jest/valid_expect.rs +++ b/crates/oxc_linter/src/rules/jest/valid_expect.rs @@ -199,15 +199,13 @@ impl ValidExpect { return; }; - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); let should_be_awaited = jest_fn_call.modifiers().iter().any(|modifier| modifier.is_name_unequal("not")) || self.async_matchers.contains(&matcher_name.to_string()); - if ctx.nodes().parent_node(parent.id()).is_none() || !should_be_awaited { + if matches!(parent.kind(), AstKind::Program(_)) || !should_be_awaited { return; } @@ -218,9 +216,7 @@ impl ValidExpect { let Some(final_node) = find_promise_call_expression_node(node, ctx, target_node) else { return; }; - let Some(parent) = ctx.nodes().parent_node(final_node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(final_node.id()); if !is_acceptable_return_node(parent, !self.always_await, ctx) { let span; let (error, help) = if target_node.id() == final_node.id() { @@ -249,7 +245,7 @@ fn find_top_most_member_expression<'a, 'b>( let mut node = node; loop { - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(node.id()); match node.kind() { member_expr if member_expr.is_member_expression_kind() => { top_most_member_expression = Some(member_expr); @@ -282,10 +278,7 @@ fn is_acceptable_return_node<'a, 'b>( | AstKind::Argument(_) | AstKind::ExpressionStatement(_) | AstKind::FunctionBody(_) => { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return false; - }; - node = parent; + node = ctx.nodes().parent_node(node.id()); } AstKind::ArrowFunctionExpression(arrow_expr) => return arrow_expr.expression, AstKind::AwaitExpression(_) => return true, @@ -304,7 +297,7 @@ fn get_parent_with_ignore<'a, 'b>( ) -> Option> { let mut node = node; loop { - let parent = ctx.nodes().parent_node(node.id())?; + let parent = ctx.nodes().parent_node(node.id()); if !matches!(parent.kind(), AstKind::Argument(_)) { // we don't want to report `Promise.all([invalidExpectCall_1, invalidExpectCall_2])` twice. // so we need mark whether the node is the first item of an array. @@ -347,7 +340,7 @@ fn find_promise_call_expression_node<'a, 'b>( if let Some(member_expr) = call_expr.callee.as_member_expression() { if let Expression::Identifier(ident) = member_expr.object() { if matches!(ident.name.as_str(), "Promise") - && ctx.nodes().parent_node(parent.id()).is_some() + && !matches!(parent.kind(), AstKind::Program(_)) { if is_first_array_item { return Some(parent); @@ -365,12 +358,7 @@ fn get_parent_if_thenable<'a, 'b>( node: &'b AstNode<'a>, ctx: &'b LintContext<'a>, ) -> &'b AstNode<'a> { - let grandparent = - ctx.nodes().parent_node(node.id()).and_then(|node| ctx.nodes().parent_node(node.id())); - - let Some(grandparent) = grandparent else { - return node; - }; + let grandparent = ctx.nodes().parent_node(ctx.nodes().parent_id(node.id())); let AstKind::CallExpression(call_expr) = grandparent.kind() else { return node; }; diff --git a/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs b/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs index 2224452a0241b..c11f883ef3343 100644 --- a/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs +++ b/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs @@ -61,7 +61,8 @@ declare_oxc_lint!( fn is_function_inside_of_class<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { let mut current_node = node; - while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { + loop { + let parent_node = ctx.nodes().parent_node(current_node.id()); match parent_node.kind() { AstKind::MethodDefinition(_) | AstKind::PropertyDefinition(_) => return true, // Keep looking up only if the node is wrapped by `()` @@ -71,8 +72,6 @@ fn is_function_inside_of_class<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintConte _ => return false, } } - - false } impl Rule for ImplementsOnClasses { diff --git a/crates/oxc_linter/src/rules/jsdoc/require_returns.rs b/crates/oxc_linter/src/rules/jsdoc/require_returns.rs index 43379816a6770..aff2e2af05e04 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_returns.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_returns.rs @@ -145,7 +145,8 @@ impl Rule for RequireReturns { // IMO: This is a fault of the original rule design... AstKind::ReturnStatement(return_stmt) => { let mut current_node = node; - while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { + while !matches!(current_node.kind(), AstKind::Program(_)) { + let parent_node = ctx.nodes().parent_node(current_node.id()); match parent_node.kind() { AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => { // Ignore `return;` @@ -278,7 +279,7 @@ fn is_promise_resolve_with_value(expr: &Expression, ctx: &LintContext) -> Option // IMO: This is a fault of the original rule design... for resolve_ref in ctx.scoping().get_resolved_references(ident.symbol_id()) { // Check if `resolve` is called with value - match ctx.nodes().parent_node(resolve_ref.node_id())?.kind() { + match ctx.nodes().parent_kind(resolve_ref.node_id()) { // `resolve(foo)` AstKind::CallExpression(call_expr) => { if !call_expr.arguments.is_empty() { diff --git a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs index b6c7abcd979e7..1c70ae437bc87 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs @@ -198,7 +198,8 @@ impl Rule for RequireYields { // Find the nearest generator function let mut generator_func_node = None; let mut current_node = node; - while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { + while !matches!(current_node.kind(), AstKind::Program(_)) { + let parent_node = ctx.nodes().parent_node(current_node.id()); // If syntax is valid, `yield` should be inside a generator function if let AstKind::Function(func) = parent_node.kind() { if func.generator && (func.is_expression() || func.is_declaration()) { diff --git a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs index 9018fa55b1e4b..a21d121f8140f 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs @@ -187,7 +187,7 @@ impl Rule for AltText { // if let Some(custom_tags) = &self.object { if name == "object" || custom_tags.iter().any(|i| i == name) { - if let Some(AstKind::JSXElement(parent)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::JSXElement(parent) = ctx.nodes().parent_kind(node.id()) { object_rule(jsx_el, parent, ctx); return; } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs index d2777f33c5d38..f6a1e84f0d0fc 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs @@ -97,7 +97,7 @@ impl Rule for HeadingHasContent { return; } - if let Some(AstKind::JSXElement(parent)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::JSXElement(parent) = ctx.nodes().parent_kind(node.id()) { if object_has_accessible_child(ctx, parent) { return; } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs b/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs index e7ed18387940c..83107594cb40c 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs @@ -138,7 +138,7 @@ impl Rule for MediaHasCaption { return; } - let Some(AstKind::JSXElement(parent)) = ctx.nodes().parent_kind(node.id()) else { + let AstKind::JSXElement(parent) = ctx.nodes().parent_kind(node.id()) else { return; }; diff --git a/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs b/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs index 3fdb21bad2d41..2fad5678c936d 100644 --- a/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs +++ b/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs @@ -99,8 +99,7 @@ impl Rule for InlineScriptId { let AstKind::ImportDefaultSpecifier(specifier) = node.kind() else { return; }; - let Some(AstKind::ImportDeclaration(import_decl)) = ctx.nodes().parent_kind(node.id()) - else { + let AstKind::ImportDeclaration(import_decl) = ctx.nodes().parent_kind(node.id()) else { return; }; @@ -111,13 +110,12 @@ impl Rule for InlineScriptId { 'references_loop: for reference in ctx.semantic().symbol_references(specifier.local.symbol_id()) { - let parent_node = ctx.nodes().parent_node(reference.node_id()).unwrap(); + let parent_node = ctx.nodes().parent_node(reference.node_id()); let AstKind::JSXOpeningElement(jsx_opening_element) = parent_node.kind() else { continue; }; - let Some(AstKind::JSXElement(jsx_element)) = ctx.nodes().parent_kind(parent_node.id()) - else { + let AstKind::JSXElement(jsx_element) = ctx.nodes().parent_kind(parent_node.id()) else { continue; }; diff --git a/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs index c9d16502ba7ff..f963f19aa0ad9 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs @@ -105,10 +105,7 @@ impl Rule for NoDuplicateHead { continue; } - if !matches!( - nodes.parent_kind(reference.node_id()), - Some(AstKind::JSXOpeningElement(_)) - ) { + if !matches!(nodes.parent_kind(reference.node_id()), AstKind::JSXOpeningElement(_)) { continue; } diff --git a/crates/oxc_linter/src/rules/nextjs/no_page_custom_font.rs b/crates/oxc_linter/src/rules/nextjs/no_page_custom_font.rs index 00e6aba5be3fc..cc1898d91a094 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_page_custom_font.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_page_custom_font.rs @@ -72,7 +72,7 @@ impl Rule for NoPageCustomFont { let in_document = ctx.file_path().file_name().is_some_and(|file_name| { file_name.to_str().is_some_and(|file_name| file_name.starts_with("_document.")) }); - let span = ctx.nodes().parent_kind(node.id()).unwrap().span(); + let span = ctx.nodes().parent_kind(node.id()).span(); let diagnostic = if in_document { if is_inside_export_default(node, ctx) { return; @@ -104,7 +104,7 @@ fn is_inside_export_default(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { let name = id.map_or_else( || { - let parent_parent_kind = ctx.nodes().parent_kind(parent_node.id())?; + let parent_parent_kind = ctx.nodes().parent_kind(parent_node.id()); let AstKind::VariableDeclarator(declarator) = parent_parent_kind else { return None; diff --git a/crates/oxc_linter/src/rules/nextjs/no_script_component_in_head.rs b/crates/oxc_linter/src/rules/nextjs/no_script_component_in_head.rs index 27ca0752d12f0..0e1d22088aae5 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_script_component_in_head.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_script_component_in_head.rs @@ -65,12 +65,11 @@ impl Rule for NoScriptComponentInHead { }; for reference in ctx.semantic().symbol_references(default_import.local.symbol_id()) { - let parent_node = ctx.nodes().parent_node(reference.node_id()).unwrap(); + let parent_node = ctx.nodes().parent_node(reference.node_id()); let AstKind::JSXOpeningElement(jsx_opening_element) = parent_node.kind() else { continue; }; - let Some(AstKind::JSXElement(jsx_element)) = ctx.nodes().parent_kind(parent_node.id()) - else { + let AstKind::JSXElement(jsx_element) = ctx.nodes().parent_kind(parent_node.id()) else { continue; }; diff --git a/crates/oxc_linter/src/rules/nextjs/no_title_in_document_head.rs b/crates/oxc_linter/src/rules/nextjs/no_title_in_document_head.rs index 15a36b78793e8..7ca95694f5eda 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_title_in_document_head.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_title_in_document_head.rs @@ -90,12 +90,11 @@ impl Rule for NoTitleInDocumentHead { }; for reference in ctx.semantic().symbol_references(default_import.local.symbol_id()) { - let parent_node = ctx.nodes().parent_node(reference.node_id()).unwrap(); + let parent_node = ctx.nodes().parent_node(reference.node_id()); let AstKind::JSXOpeningElement(jsx_opening_element) = parent_node.kind() else { continue; }; - let Some(AstKind::JSXElement(jsx_element)) = ctx.nodes().parent_kind(parent_node.id()) - else { + let AstKind::JSXElement(jsx_element) = ctx.nodes().parent_kind(parent_node.id()) else { continue; }; diff --git a/crates/oxc_linter/src/rules/node/no_exports_assign.rs b/crates/oxc_linter/src/rules/node/no_exports_assign.rs index 06938ce581cc7..597b95d226aa4 100644 --- a/crates/oxc_linter/src/rules/node/no_exports_assign.rs +++ b/crates/oxc_linter/src/rules/node/no_exports_assign.rs @@ -93,8 +93,7 @@ impl Rule for NoExportsAssign { } } - if let Some(AstKind::AssignmentExpression(assign_expr)) = ctx.nodes().parent_kind(node.id()) - { + if let AstKind::AssignmentExpression(assign_expr) = ctx.nodes().parent_kind(node.id()) { if is_module_exports(assign_expr.left.as_member_expression(), ctx) { return; } diff --git a/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs b/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs index 15fbd90150912..877a702b800eb 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs @@ -57,16 +57,11 @@ impl Rule for BadArrayMethodOnArguments { if !node.kind().is_specific_id_reference("arguments") { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); let Some(member_expr) = parent.kind().as_member_expression_kind() else { return; }; - let Some(grandparent) = ctx.nodes().parent_node(parent.id()) else { - return; - }; - let AstKind::CallExpression(_) = grandparent.kind() else { + let AstKind::CallExpression(_) = ctx.nodes().parent_kind(parent.id()) else { return; }; let Some(name) = member_expr.static_property_name() else { diff --git a/crates/oxc_linter/src/rules/oxc/bad_char_at_comparison.rs b/crates/oxc_linter/src/rules/oxc/bad_char_at_comparison.rs index 1f0cf36001756..cf47e76dd0f33 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_char_at_comparison.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_char_at_comparison.rs @@ -59,11 +59,7 @@ impl Rule for BadCharAtComparison { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; - - let AstKind::BinaryExpression(binary_expr) = parent.kind() else { + let AstKind::BinaryExpression(binary_expr) = ctx.nodes().parent_kind(node.id()) else { return; }; if !matches!( diff --git a/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs b/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs index db11507495e36..6cd1bf9b3d62a 100644 --- a/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs +++ b/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs @@ -141,9 +141,7 @@ impl Rule for NoAccumulatingSpread { return; }; let declaration_id = symbols.symbol_declaration(referenced_symbol_id); - let Some(declaration) = ctx.nodes().parent_node(declaration_id) else { - return; - }; + let declaration = ctx.nodes().parent_node(declaration_id); check_reduce_usage(declaration, referenced_symbol_id, spread.span, ctx); check_loop_usage( @@ -219,18 +217,17 @@ fn check_loop_usage<'a>( return; }; - let Some(assignment_target) = ctx.nodes().parent_node(write_reference.node_id()) else { - return; - }; + let assignment_target = ctx.nodes().parent_node(write_reference.node_id()); let AstKind::SimpleAssignmentTarget(_) = assignment_target.kind() else { return }; - let Some(assignment_expr) = ctx.nodes().parent_node(assignment_target.id()) else { return }; + let assignment_expr = ctx.nodes().parent_node(assignment_target.id()); if !matches!(assignment_expr.kind(), AstKind::AssignmentTarget(_)) { return; } - let Some(assignment) = ctx.nodes().parent_node(assignment_expr.id()) else { return }; - let AstKind::AssignmentExpression(assignment_expression) = assignment.kind() else { + let AstKind::AssignmentExpression(assignment_expression) = + ctx.nodes().parent_kind(assignment_expr.id()) + else { return; }; diff --git a/crates/oxc_linter/src/rules/oxc/no_async_await.rs b/crates/oxc_linter/src/rules/oxc/no_async_await.rs index 7403ee1f00580..bf35a2275bc33 100644 --- a/crates/oxc_linter/src/rules/oxc/no_async_await.rs +++ b/crates/oxc_linter/src/rules/oxc/no_async_await.rs @@ -50,7 +50,7 @@ impl Rule for NoAsyncAwait { // async bar() {} // } // ``` - Some(AstKind::MethodDefinition(method_def)) => { + AstKind::MethodDefinition(method_def) => { Span::new(method_def.span.start, method_def.key.span().start) } // The function is part of an object property like: @@ -59,7 +59,7 @@ impl Rule for NoAsyncAwait { // async foo() {} // }; // ``` - Some(AstKind::ObjectProperty(obj_prop)) => { + AstKind::ObjectProperty(obj_prop) => { Span::new(obj_prop.span.start, obj_prop.key.span().start) } _ => func_decl.span, diff --git a/crates/oxc_linter/src/rules/oxc/no_rest_spread_properties.rs b/crates/oxc_linter/src/rules/oxc/no_rest_spread_properties.rs index 308b0cb362433..54fa84601b347 100644 --- a/crates/oxc_linter/src/rules/oxc/no_rest_spread_properties.rs +++ b/crates/oxc_linter/src/rules/oxc/no_rest_spread_properties.rs @@ -88,11 +88,7 @@ impl Rule for NoRestSpreadProperties { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { AstKind::SpreadElement(spread_element) => { - if ctx - .nodes() - .parent_kind(node.id()) - .is_some_and(|parent| matches!(parent, AstKind::ObjectExpression(_))) - { + if matches!(ctx.nodes().parent_kind(node.id()), AstKind::ObjectExpression(_)) { ctx.diagnostic(no_rest_spread_properties_diagnostic( spread_element.span, "object spread property", @@ -101,11 +97,7 @@ impl Rule for NoRestSpreadProperties { } } AstKind::BindingRestElement(rest_element) => { - if ctx - .nodes() - .parent_kind(node.id()) - .is_some_and(|parent| matches!(parent, AstKind::ObjectPattern(_))) - { + if matches!(ctx.nodes().parent_kind(node.id()), AstKind::ObjectPattern(_)) { ctx.diagnostic(no_rest_spread_properties_diagnostic( rest_element.span, "object rest property", diff --git a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs index 2021f65a2ec5e..12bda904a37e6 100644 --- a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs +++ b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs @@ -168,8 +168,7 @@ fn create_diagnostic( for reference in ctx.semantic().symbol_references(function_id.symbol_id()) { let node = ctx.nodes().get_node(reference.node_id()); - if let Some(AstKind::CallExpression(call_expr)) = ctx.nodes().parent_kind(node.id()) - { + if let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(node.id()) { if call_expr.arguments.len() != function_parameters.items.len() || function_span.contains_inclusive(call_expr.span) { @@ -276,11 +275,11 @@ fn is_argument_only_used_in_recursion<'a>( let function_symbol_id = function_id.symbol_id(); for reference in references { - let Some(AstKind::Argument(argument)) = ctx.nodes().parent_kind(reference.node_id()) else { + let AstKind::Argument(argument) = ctx.nodes().parent_kind(reference.node_id()) else { return false; }; - let Some(AstKind::CallExpression(call_expr)) = - ctx.nodes().parent_kind(ctx.nodes().parent_node(reference.node_id()).unwrap().id()) + let AstKind::CallExpression(call_expr) = + ctx.nodes().parent_kind(ctx.nodes().parent_node(reference.node_id()).id()) else { return false; }; @@ -318,9 +317,7 @@ fn is_property_only_used_in_recursion_jsx( // 1. The reference is inside a JSXExpressionContainer. // 2. The JSXElement calls the recursive function itself. // 3. The reference is in a JSXAttribute, and the attribute name has the same name as the function. - let Some(may_jsx_expr_container) = ctx.nodes().parent_node(reference.node_id()) else { - return false; - }; + let may_jsx_expr_container = ctx.nodes().parent_node(reference.node_id()); let AstKind::JSXExpressionContainer(_) = may_jsx_expr_container.kind() else { // In this case, we simply ignore the references inside JSXExpressionContainer that are not single-node expression. // e.g. @@ -383,10 +380,7 @@ fn is_function_maybe_reassigned<'a>( ctx: &'a LintContext<'_>, ) -> bool { ctx.semantic().symbol_references(function_id.symbol_id()).any(|reference| { - matches!( - ctx.nodes().parent_kind(reference.node_id()), - Some(AstKind::SimpleAssignmentTarget(_)) - ) + matches!(ctx.nodes().parent_kind(reference.node_id()), AstKind::SimpleAssignmentTarget(_)) }) } diff --git a/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs b/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs index b507636fcb6c6..815df17073b95 100644 --- a/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs +++ b/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs @@ -60,16 +60,12 @@ impl Rule for UninvokedArrayCallback { return; } - let Some(member_expr_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; - + let member_expr_node = ctx.nodes().parent_node(node.id()); let Some(member_expr) = member_expr_node.kind().as_member_expression_kind() else { return; }; - let Some(AstKind::CallExpression(call_expr)) = - ctx.nodes().parent_kind(member_expr_node.id()) + let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(member_expr_node.id()) else { return; }; 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 5145dfdd9a225..2b657e8aa7589 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 @@ -129,7 +129,7 @@ impl Rule for NoCallbackInPromise { impl NoCallbackInPromise { fn is_inside_promise(node: &AstNode, ctx: &LintContext) -> bool { if !matches!(node.kind(), AstKind::Function(_) | AstKind::ArrowFunctionExpression(_)) - || !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::Argument(_))) + || !matches!(ctx.nodes().parent_kind(node.id()), AstKind::Argument(_)) { return false; } diff --git a/crates/oxc_linter/src/rules/promise/no_nesting.rs b/crates/oxc_linter/src/rules/promise/no_nesting.rs index a7bce1c47e5fc..87a304ce861d9 100644 --- a/crates/oxc_linter/src/rules/promise/no_nesting.rs +++ b/crates/oxc_linter/src/rules/promise/no_nesting.rs @@ -71,7 +71,7 @@ declare_oxc_lint!( fn is_inside_promise(node: &AstNode, ctx: &LintContext) -> bool { if !matches!(node.kind(), AstKind::Function(_) | AstKind::ArrowFunctionExpression(_)) - || !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::Argument(_))) + || !matches!(ctx.nodes().parent_kind(node.id()), AstKind::Argument(_)) { return false; } diff --git a/crates/oxc_linter/src/rules/promise/no_promise_in_callback.rs b/crates/oxc_linter/src/rules/promise/no_promise_in_callback.rs index 7c526204cca09..b0d69e68a0f55 100644 --- a/crates/oxc_linter/src/rules/promise/no_promise_in_callback.rs +++ b/crates/oxc_linter/src/rules/promise/no_promise_in_callback.rs @@ -61,7 +61,7 @@ impl Rule for NoPromiseInCallback { // When a Promise is returned in a ReturnStatement, the function is most likely // being used as part of a Promise chain rather than as a callback function. // To avoid false positives, this case is intentionally excluded from the scope of this rule. - if let Some(AstKind::ReturnStatement(_)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::ReturnStatement(_) = ctx.nodes().parent_kind(node.id()) { return; } @@ -107,14 +107,12 @@ fn is_within_promise_handler<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b return false; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return false; - }; + let parent = ctx.nodes().parent_node(node.id()); if !matches!(ctx.nodes().kind(parent.id()), AstKind::Argument(_)) { return false; } - let Some(AstKind::CallExpression(call_expr)) = ctx.nodes().parent_kind(parent.id()) else { + let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(parent.id()) else { return false; }; diff --git a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs index 083e36eaeb5e1..fa5b20f53136e 100644 --- a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs +++ b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs @@ -435,21 +435,15 @@ impl Rule for ExhaustiveDeps { let reference = ctx.scoping().get_reference(ident.reference_id()); let has_write_reference = reference.symbol_id().is_some_and(|symbol_id| { ctx.semantic().symbol_references(symbol_id).any(|reference| { - ctx.nodes().parent_node(reference.node_id()).is_some_and(|parent| { - let AstKind::StaticMemberExpression(member_expr) = parent.kind() - else { - return false; - }; - if member_expr.property.name != "current" { - return false; - } - ctx.nodes().parent_node(parent.id()).is_some_and(|grand_parent| { - matches!( - grand_parent.kind(), - AstKind::SimpleAssignmentTarget(_) - ) - }) - }) + let parent = ctx.nodes().parent_node(reference.node_id()); + let AstKind::StaticMemberExpression(member_expr) = parent.kind() else { + return false; + }; + if member_expr.property.name != "current" { + return false; + } + let grand_parent = ctx.nodes().parent_node(parent.id()); + matches!(grand_parent.kind(), AstKind::SimpleAssignmentTarget(_)) }) }); @@ -1026,7 +1020,7 @@ fn is_stable_value<'a, 'b>( .any(|reference| { matches!( ctx.nodes().parent_kind(reference.node_id()), - Some(AstKind::SimpleAssignmentTarget(_)) + AstKind::SimpleAssignmentTarget(_) ) }) { diff --git a/crates/oxc_linter/src/rules/react/jsx_curly_brace_presence.rs b/crates/oxc_linter/src/rules/react/jsx_curly_brace_presence.rs index e363109d8ae67..a9af3c8ecbfc7 100644 --- a/crates/oxc_linter/src/rules/react/jsx_curly_brace_presence.rs +++ b/crates/oxc_linter/src/rules/react/jsx_curly_brace_presence.rs @@ -550,7 +550,7 @@ fn has_adjacent_jsx_expression_containers<'a>( node_id: NodeId, // element: &JSXElement<'a>, ) -> bool { - let Some(parent) = ctx.nodes().parent_kind(node_id) else { return false }; + let parent = ctx.nodes().parent_kind(node_id); let children = match parent { AstKind::JSXElement(el) => &el.children, AstKind::JSXFragment(fragment) => &fragment.children, diff --git a/crates/oxc_linter/src/rules/react/jsx_key.rs b/crates/oxc_linter/src/rules/react/jsx_key.rs index e2403479ab61f..4d9d308d8a971 100644 --- a/crates/oxc_linter/src/rules/react/jsx_key.rs +++ b/crates/oxc_linter/src/rules/react/jsx_key.rs @@ -161,8 +161,8 @@ fn is_in_array_or_iter<'a, 'b>( let mut is_explicit_return = false; let mut argument = None; - loop { - let parent = ctx.nodes().parent_node(node.id())?; + while !matches!(node.kind(), AstKind::Program(_)) { + let parent = ctx.nodes().parent_node(node.id()); match parent.kind() { AstKind::ArrowFunctionExpression(arrow_expr) => { let is_arrow_expr_statement = matches!( @@ -173,9 +173,7 @@ fn is_in_array_or_iter<'a, 'b>( return None; } - let parent = ctx.nodes().parent_node(parent.id())?; - - if let AstKind::ObjectProperty(_) = parent.kind() { + if let AstKind::ObjectProperty(_) = ctx.nodes().parent_kind(parent.id()) { return None; } if is_outside_containing_function { @@ -184,9 +182,7 @@ fn is_in_array_or_iter<'a, 'b>( is_outside_containing_function = true; } AstKind::Function(_) => { - let parent = ctx.nodes().parent_node(parent.id())?; - - if let AstKind::ObjectProperty(_) = parent.kind() { + if let AstKind::ObjectProperty(_) = ctx.nodes().parent_kind(parent.id()) { return None; } if is_outside_containing_function { @@ -234,6 +230,8 @@ fn is_in_array_or_iter<'a, 'b>( } node = parent; } + + None } fn check_jsx_element<'a>(node: &AstNode<'a>, jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { diff --git a/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs b/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs index d9a546299c518..1b45d00ce0540 100644 --- a/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs +++ b/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs @@ -236,9 +236,7 @@ fn trim_like_react(text: &str) -> &str { } fn can_fix(node: &AstNode, children: &ArenaVec>, ctx: &LintContext) -> bool { - let Some(parent) = ctx.nodes().parent_kind(node.id()) else { - return false; - }; + let parent = ctx.nodes().parent_kind(node.id()); if !matches!(parent, AstKind::JSXElement(_) | AstKind::JSXFragment(_)) { // const a = <> @@ -306,7 +304,7 @@ fn is_padding_spaces(v: &JSXChild<'_>) -> bool { } fn is_child_of_html_element(node: &AstNode, ctx: &LintContext) -> bool { - if let Some(AstKind::JSXElement(elem)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::JSXElement(elem) = ctx.nodes().parent_kind(node.id()) { if is_html_element(&elem.opening_element.name) { return true; } @@ -367,9 +365,7 @@ fn is_fragment_with_only_text_and_is_not_child<'a>( } if let Some(JSXChild::Text(_)) = node.first() { - let Some(parent) = ctx.nodes().parent_kind(id) else { - return false; - }; + let parent = ctx.nodes().parent_kind(id); return !matches!(parent, AstKind::JSXElement(_) | AstKind::JSXFragment(_)); } diff --git a/crates/oxc_linter/src/rules/react/no_render_return_value.rs b/crates/oxc_linter/src/rules/react/no_render_return_value.rs index dc5222ab34164..c1177dc358c93 100644 --- a/crates/oxc_linter/src/rules/react/no_render_return_value.rs +++ b/crates/oxc_linter/src/rules/react/no_render_return_value.rs @@ -60,29 +60,28 @@ impl Rule for NoRenderReturnValue { if ident.name == "ReactDOM" { if let Some((property_span, property_name)) = member_expr.static_property_info() { if property_name == "render" { - if let Some(parent_node) = ctx.nodes().parent_node(node.id()) { - if matches!( - parent_node.kind(), - AstKind::VariableDeclarator(_) - | AstKind::ObjectProperty(_) - | AstKind::ReturnStatement(_) - | AstKind::AssignmentExpression(_) - ) { - ctx.diagnostic(no_render_return_value_diagnostic( - ident.span.merge(property_span), - )); - } + let parent_node = ctx.nodes().parent_node(node.id()); + if matches!( + parent_node.kind(), + AstKind::VariableDeclarator(_) + | AstKind::ObjectProperty(_) + | AstKind::ReturnStatement(_) + | AstKind::AssignmentExpression(_) + ) { + ctx.diagnostic(no_render_return_value_diagnostic( + ident.span.merge(property_span), + )); + } - let scope_id = parent_node.scope_id(); - if ctx.scoping().scope_flags(scope_id).is_arrow() { - if let AstKind::ArrowFunctionExpression(e) = - ctx.nodes().kind(ctx.scoping().get_node_id(scope_id)) - { - if e.expression { - ctx.diagnostic(no_render_return_value_diagnostic( - ident.span.merge(property_span), - )); - } + let scope_id = parent_node.scope_id(); + if ctx.scoping().scope_flags(scope_id).is_arrow() { + if let AstKind::ArrowFunctionExpression(e) = + ctx.nodes().kind(ctx.scoping().get_node_id(scope_id)) + { + if e.expression { + ctx.diagnostic(no_render_return_value_diagnostic( + ident.span.merge(property_span), + )); } } } diff --git a/crates/oxc_linter/src/rules/react/require_render_return.rs b/crates/oxc_linter/src/rules/react/require_render_return.rs index 3f477bf942e86..daaf071aeb948 100644 --- a/crates/oxc_linter/src/rules/react/require_render_return.rs +++ b/crates/oxc_linter/src/rules/react/require_render_return.rs @@ -73,9 +73,7 @@ impl Rule for RequireRenderReturn { if !matches!(node.kind(), AstKind::ArrowFunctionExpression(_) | AstKind::Function(_)) { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); if !is_render_fn(parent) { return; } @@ -192,28 +190,28 @@ fn is_render_fn(node: &AstNode) -> bool { } fn is_in_es5_component<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { - let Some(ancestors_0) = ctx.nodes().parent_node(node.id()) else { return false }; + let ancestors_0 = ctx.nodes().parent_node(node.id()); if !matches!(ancestors_0.kind(), AstKind::ObjectExpression(_)) { return false; } - let Some(ancestors_1) = ctx.nodes().parent_node(ancestors_0.id()) else { return false }; + let ancestors_1 = ctx.nodes().parent_node(ancestors_0.id()); if !matches!(ancestors_1.kind(), AstKind::Argument(_)) { return false; } - let Some(ancestors_2) = ctx.nodes().parent_node(ancestors_1.id()) else { return false }; + let ancestors_2 = ctx.nodes().parent_node(ancestors_1.id()); is_es5_component(ancestors_2) } fn is_in_es6_component<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { return false }; + let parent = ctx.nodes().parent_node(node.id()); if !matches!(parent.kind(), AstKind::ClassBody(_)) { return false; } - let Some(grandparent) = ctx.nodes().parent_node(parent.id()) else { return false }; + let grandparent = ctx.nodes().parent_node(parent.id()); is_es6_component(grandparent) } 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 273c3ec4ce047..327da3ca0a1df 100644 --- a/crates/oxc_linter/src/rules/react/rules_of_hooks.rs +++ b/crates/oxc_linter/src/rules/react/rules_of_hooks.rs @@ -140,11 +140,7 @@ impl Rule for RulesOfHooks { // Check if our parent function is part of a class. if matches!( nodes.parent_kind(parent_func.id()), - Some( - AstKind::MethodDefinition(_) - | AstKind::StaticBlock(_) - | AstKind::PropertyDefinition(_) - ) + AstKind::MethodDefinition(_) | AstKind::StaticBlock(_) | AstKind::PropertyDefinition(_) ) { return ctx.diagnostic(diagnostics::class_component(span, hook_name)); } @@ -339,12 +335,12 @@ fn parent_func<'a>(nodes: &'a AstNodes<'a>, node: &AstNode) -> Option<&'a AstNod /// Returns `true` if this node is a function argument and that isn't a React special function. /// Otherwise it would return `false`. fn is_non_react_func_arg(nodes: &AstNodes, node_id: NodeId) -> bool { - let argument = match nodes.parent_node(node_id) { - Some(parent) if matches!(parent.kind(), AstKind::Argument(_)) => parent, - _ => return false, - }; + let parent = nodes.parent_node(node_id); + if !matches!(parent.kind(), AstKind::Argument(_)) { + return false; + } - let Some(AstKind::CallExpression(call)) = nodes.parent_kind(argument.id()) else { + let AstKind::CallExpression(call) = nodes.parent_kind(parent.id()) else { return false; }; diff --git a/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs b/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs index 7d97ff5167847..a832496732503 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs @@ -89,11 +89,7 @@ impl Rule for ConsistentGenericConstructors { self.check(type_ann, init, ctx); } AstKind::AssignmentPattern(assignment_pattern) => { - let Some(parent) = ctx.nodes().parent_kind(node.id()) else { - return; - }; - - if !matches!(parent, AstKind::FormalParameter(_)) { + if !matches!(ctx.nodes().parent_kind(node.id()), AstKind::FormalParameter(_)) { return; } diff --git a/crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs b/crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs index fb42b760b4978..c8bd7db0804d5 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs @@ -139,7 +139,7 @@ impl Rule for ConsistentIndexedObjectStyle { for t in &uni.types { if let TSType::TSTypeReference(tref) = t { if let TSTypeName::IdentifierReference(ide) = &tref.type_name { - let Some(AstKind::TSTypeAliasDeclaration(dec)) = + let AstKind::TSTypeAliasDeclaration(dec) = ctx.nodes().parent_kind(node.id()) else { return; @@ -179,18 +179,13 @@ impl Rule for ConsistentIndexedObjectStyle { match &sig.type_annotation.type_annotation { TSType::TSTypeReference(r) => match &r.type_name { TSTypeName::IdentifierReference(ide) => { - let Some(parent) = ctx.nodes().parent_kind(node.id()) else { + let AstKind::TSTypeAliasDeclaration(dec) = + ctx.nodes().parent_kind(node.id()) + else { return; }; - let parent_name = - if let AstKind::TSTypeAliasDeclaration(dec) = parent { - &dec.id.name - } else { - return; - }; - - if ide.name != parent_name { + if ide.name != dec.id.name { ctx.diagnostic(consistent_indexed_object_style_diagnostic( "record", "index signature", @@ -210,7 +205,7 @@ impl Rule for ConsistentIndexedObjectStyle { for t in &uni.types { if let TSType::TSTypeReference(tref) = t { if let TSTypeName::IdentifierReference(ide) = &tref.type_name { - let Some(AstKind::TSTypeAliasDeclaration(dec)) = + let AstKind::TSTypeAliasDeclaration(dec) = ctx.nodes().parent_kind(node.id()) else { return; 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 c8f5ac617d4e4..af3d940dc83d0 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 @@ -398,9 +398,7 @@ impl ExplicitFunctionReturnType { } fn check_allow_expressions(&self, node: &AstNode, ctx: &LintContext) -> bool { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return false; - }; + let parent = ctx.nodes().parent_node(node.id()); self.allow_expressions && !matches!( parent.kind(), @@ -497,9 +495,7 @@ fn is_constructor_argument(node: &AstNode) -> bool { } fn is_constructor_or_setter(node: &AstNode, ctx: &LintContext) -> bool { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return false; - }; + let parent = ctx.nodes().parent_node(node.id()); is_constructor(parent) || is_setter(parent) } @@ -718,9 +714,7 @@ fn is_property_of_object_with_type(node: &AstNode, ctx: &LintContext) -> bool { if !matches!(node.kind(), AstKind::ObjectProperty(_)) { return false; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return false; - }; + let parent = ctx.nodes().parent_node(node.id()); if !matches!(parent.kind(), AstKind::ObjectExpression(_)) { return false; } diff --git a/crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs b/crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs index a5b51be7f2ee7..d4869c96d2cd3 100644 --- a/crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs +++ b/crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs @@ -181,10 +181,7 @@ fn check_type_literal( if matches!(allow_object_types, AllowObjectTypes::Always) { return; } - let Some(parent_node) = ctx.nodes().parent_node(node_id) else { - return; - }; - match parent_node.kind() { + match ctx.nodes().parent_kind(node_id) { AstKind::TSIntersectionType(_) => return, AstKind::TSTypeAliasDeclaration(alias) => { if alias.id.name.as_str() == allow_with_name { diff --git a/crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs b/crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs index 01e7ebdd2f462..971d18d187f63 100644 --- a/crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs +++ b/crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs @@ -161,7 +161,7 @@ impl Rule for NoExtraneousClass { if has_decorators { return fixer.noop(); } - if let Some(AstKind::ExportNamedDeclaration(decl)) = + if let AstKind::ExportNamedDeclaration(decl) = ctx.nodes().parent_kind(node.id()) { fixer.delete(decl) diff --git a/crates/oxc_linter/src/rules/typescript/no_namespace.rs b/crates/oxc_linter/src/rules/typescript/no_namespace.rs index 9179da3cf4cdc..0c9a374a672dd 100644 --- a/crates/oxc_linter/src/rules/typescript/no_namespace.rs +++ b/crates/oxc_linter/src/rules/typescript/no_namespace.rs @@ -153,10 +153,8 @@ impl Rule for NoNamespace { return; } - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if let AstKind::TSModuleDeclaration(_) = parent.kind() { - return; - } + if let AstKind::TSModuleDeclaration(_) = ctx.nodes().parent_kind(node.id()) { + return; } if self.allow_declarations && is_declaration(node, ctx) { diff --git a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs index da232a6739d3a..3a0b0f8341877 100644 --- a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs +++ b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs @@ -125,8 +125,7 @@ impl Rule for NoNonNullAssertedOptionalChain { fn is_parent_member_or_call(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { let parent_kind = ctx.nodes().parent_kind(node.id()); - matches!(parent_kind, Some(AstKind::CallExpression(_))) - || parent_kind.is_some_and(|k| k.is_member_expression_kind()) + matches!(parent_kind, AstKind::CallExpression(_)) || parent_kind.is_member_expression_kind() } #[test] diff --git a/crates/oxc_linter/src/rules/typescript/no_unnecessary_parameter_property_assignment.rs b/crates/oxc_linter/src/rules/typescript/no_unnecessary_parameter_property_assignment.rs index f7bef94afd82f..9dfe860ad8d2c 100644 --- a/crates/oxc_linter/src/rules/typescript/no_unnecessary_parameter_property_assignment.rs +++ b/crates/oxc_linter/src/rules/typescript/no_unnecessary_parameter_property_assignment.rs @@ -73,10 +73,7 @@ impl Rule for NoUnnecessaryParameterPropertyAssignment { return; } - let Some(parent_node) = ctx.semantic().nodes().parent_node(node.id()) else { - return; - }; - let AstKind::ClassBody(class_body) = parent_node.kind() else { + let AstKind::ClassBody(class_body) = ctx.semantic().nodes().parent_kind(node.id()) else { return; }; diff --git a/crates/oxc_linter/src/rules/typescript/no_var_requires.rs b/crates/oxc_linter/src/rules/typescript/no_var_requires.rs index 1d5a5a1ec1228..d2bafaa4d88eb 100644 --- a/crates/oxc_linter/src/rules/typescript/no_var_requires.rs +++ b/crates/oxc_linter/src/rules/typescript/no_var_requires.rs @@ -50,17 +50,11 @@ impl Rule for NoVarRequires { // the grandparent is an expression statement => this is a top level require() let is_expression_statement = { let parent_node = ctx.nodes().parent_node(node.id()); - let grandparent_node = parent_node.and_then(|x| ctx.nodes().parent_node(x.id())); + let grandparent_node = ctx.nodes().parent_node(parent_node.id()); matches!( - ( - parent_node.map(oxc_semantic::AstNode::kind), - grandparent_node.map(oxc_semantic::AstNode::kind) - ), - (Some(AstKind::ExpressionStatement(_)), _) - | ( - Some(AstKind::ChainExpression(_)), - Some(AstKind::ExpressionStatement(_)) - ) + (parent_node.kind(), grandparent_node.kind()), + (AstKind::ExpressionStatement(_), _) + | (AstKind::ChainExpression(_), AstKind::ExpressionStatement(_)) ) }; diff --git a/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs b/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs index 133e62cd1e7e8..4ad21f589d5e1 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs @@ -184,15 +184,9 @@ impl Rule for PreferForOf { return false; } - let Some(ref_parent) = nodes.parent_node(ref_id) else { - return true; - }; - - let Some(ref_grand_parent) = nodes.parent_node(ref_parent.id()) else { - return true; - }; - - match ref_grand_parent.kind() { + let parent = nodes.parent_node(ref_id); + let grand_parent = nodes.parent_node(parent.id()); + match grand_parent.kind() { AstKind::SimpleAssignmentTarget(_) => { return true; } @@ -204,7 +198,7 @@ impl Rule for PreferForOf { _ => {} } - let parent_kind = ref_parent.kind(); + let parent_kind = parent.kind(); match parent_kind { mem_expr if mem_expr.is_member_expression_kind() => { let Some(mem_expr) = mem_expr.as_member_expression_kind() else { diff --git a/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs b/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs index 8a4679c44434d..2f2b49635df1f 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs @@ -149,14 +149,12 @@ fn check_member(member: &TSSignature, node: &AstNode<'_>, ctx: &LintContext<'_>) let mut is_parent_exported = false; let mut node_start = interface_decl.span.start; let mut node_end = interface_decl.span.end; - if let Some(parent_node) = ctx.nodes().parent_node(node.id()) { - if let AstKind::ExportNamedDeclaration(export_name_decl) = - parent_node.kind() - { - is_parent_exported = true; - node_start = export_name_decl.span.start; - node_end = export_name_decl.span.end; - } + if let AstKind::ExportNamedDeclaration(export_name_decl) = + ctx.nodes().parent_kind(node.id()) + { + is_parent_exported = true; + node_start = export_name_decl.span.start; + node_end = export_name_decl.span.end; } let has_comments = ctx.has_comments_between(interface_decl.span); diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_assert.rs b/crates/oxc_linter/src/rules/unicorn/consistent_assert.rs index 1cc7b849aa1db..ee0bb8dea707a 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_assert.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_assert.rs @@ -105,9 +105,7 @@ fn check_assert_calls(symbol_id: SymbolId, ctx: &LintContext<'_>) { let references = ctx.semantic().symbol_references(symbol_id); for reference in references { - let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else { - continue; - }; + let parent = ctx.nodes().parent_node(reference.node_id()); match parent.kind() { AstKind::CallExpression(call_expr) => { diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs b/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs index 2978bccacf0cd..8141bbdd237e7 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs @@ -380,7 +380,7 @@ fn get_short_span_for_fn_scope( let scope_id = match ctx.nodes().parent_kind(ctx.scoping().symbol_declaration(function_symbol_id)) { - Some(AstKind::AssignmentExpression(_) | AstKind::ObjectProperty(_)) => { + AstKind::AssignmentExpression(_) | AstKind::ObjectProperty(_) => { ctx.scoping().scope_parent_id(scope_id).unwrap_or(scope_id) } _ => scope_id, @@ -391,7 +391,7 @@ fn get_short_span_for_fn_scope( match node_creating_parent_scope.kind() { AstKind::Function(f) => f.id.as_ref().map(|id| (id.span(), "function")), AstKind::ArrowFunctionExpression(_) => { - let parent = ctx.nodes().parent_kind(node_creating_parent_scope.id())?; + let parent = ctx.nodes().parent_kind(node_creating_parent_scope.id()); match parent { AstKind::VariableDeclarator(v) => Some((v.id.span(), "arrow function")), AstKind::AssignmentExpression(a) => Some((a.left.span(), "arrow function")), diff --git a/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs b/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs index c94d419683758..c8d319cde3a0a 100644 --- a/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs +++ b/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs @@ -120,11 +120,11 @@ fn get_length_check_node<'a, 'b>( // (is_zero_length_check, length_check_node) ) -> Option<(bool, &'b AstNode<'a>)> { let parent = ctx.nodes().parent_node(node.id()); - parent.and_then(|parent| { - if let AstKind::BinaryExpression(binary_expr) = parent.kind() { - // Zero length check - // `foo.length === 0` - if is_compare_right(binary_expr, BinaryOperator::StrictEquality, 0.0) + + if let AstKind::BinaryExpression(binary_expr) = parent.kind() { + // Zero length check + // `foo.length === 0` + if is_compare_right(binary_expr, BinaryOperator::StrictEquality, 0.0) // `foo.length == 0` || is_compare_right(binary_expr, BinaryOperator::Equality, 0.0) // `foo.length < 1` @@ -135,12 +135,12 @@ fn get_length_check_node<'a, 'b>( || is_compare_left(binary_expr, BinaryOperator::Equality, 0.0) // `1 > foo.length` || is_compare_left(binary_expr, BinaryOperator::GreaterThan, 1.0) - { - return Some((true, parent)); - } - // Non-Zero length check - // `foo.length !== 0` - if is_compare_right(binary_expr, BinaryOperator::StrictInequality, 0.0) + { + return Some((true, parent)); + } + // Non-Zero length check + // `foo.length !== 0` + if is_compare_right(binary_expr, BinaryOperator::StrictInequality, 0.0) // `foo.length != 0` || is_compare_right(binary_expr, BinaryOperator::Inequality, 0.0) // `foo.length > 0` @@ -155,13 +155,12 @@ fn get_length_check_node<'a, 'b>( || is_compare_left(binary_expr, BinaryOperator::LessThan, 0.0) // `1 <= foo.length` || is_compare_left(binary_expr, BinaryOperator::LessEqualThan, 1.0) - { - return Some((false, parent)); - } - return None; + { + return Some((false, parent)); } - None - }) + return None; + } + None } impl ExplicitLengthCheck { @@ -204,7 +203,7 @@ impl ExplicitLengthCheck { let mut need_pad_end = false; let parent = ctx.nodes().parent_kind(node.id()); let need_paren = matches!(kind, AstKind::UnaryExpression(_)) - && matches!(parent, Some(AstKind::UnaryExpression(_) | AstKind::AwaitExpression(_))); + && matches!(parent, AstKind::UnaryExpression(_) | AstKind::AwaitExpression(_)); if span.start > 1 { let start = ctx.source_text().as_bytes()[span.start as usize - 1]; need_pad_start = start.is_ascii_alphabetic() || !start.is_ascii(); @@ -267,11 +266,10 @@ impl Rule for ExplicitLengthCheck { return; } match ctx.nodes().parent_kind(node.id()) { - Some(AstKind::LogicalExpression(LogicalExpression { - operator, right, .. - })) if *operator == LogicalOperator::And - || (*operator == LogicalOperator::Or - && !matches!(right, Expression::NumericLiteral(_))) => + AstKind::LogicalExpression(LogicalExpression { operator, right, .. }) + if *operator == LogicalOperator::And + || (*operator == LogicalOperator::Or + && !matches!(right, Expression::NumericLiteral(_))) => { self.report(ctx, ancestor, is_negative, static_member_expr, false); } diff --git a/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs b/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs index 4f5ab20cf37a1..81a456199a60d 100644 --- a/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs +++ b/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs @@ -71,7 +71,7 @@ impl Rule for NewForBuiltins { if ENFORCE_NEW_FOR_BUILTINS.contains(builtin_name) { if builtin_name == "Object" { let parent_kind = ctx.nodes().parent_kind(node.id()); - if let Some(AstKind::BinaryExpression(bin_expr)) = parent_kind { + if let AstKind::BinaryExpression(bin_expr) = parent_kind { if bin_expr.operator == BinaryOperator::StrictEquality || bin_expr.operator == BinaryOperator::StrictInequality { 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 8f86e5612d0db..9d819f9586232 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs @@ -84,9 +84,7 @@ impl Rule for NoAccessorRecursion { let Some(nearest_func) = get_nearest_function(node, ctx) else { return; }; - let Some(func_parent) = ctx.nodes().parent_node(nearest_func.id()) else { - return; - }; + let func_parent = ctx.nodes().parent_node(nearest_func.id()); if !is_property_or_method_def(func_parent) { return; } @@ -222,17 +220,17 @@ fn is_property_or_method_def<'a>(parent: &'a AstNode<'a>) -> bool { } fn get_nearest_function<'a>(node: &AstNode, ctx: &'a LintContext) -> Option<&'a AstNode<'a>> { - let mut parent = ctx.nodes().parent_node(node.id())?; - while let Some(new_parent) = ctx.nodes().parent_node(parent.id()) { + let mut parent = ctx.nodes().parent_node(node.id()); + loop { match parent.kind() { - AstKind::Function(_) => break, + AstKind::Program(_) | AstKind::Function(_) => break, // If a class is declared in the accessor, ignore it // e.g. "let foo = { get bar() { class baz { } } }" AstKind::Class(_) => { return None; } _ => { - parent = new_parent; + parent = ctx.nodes().parent_node(parent.id()); } } } diff --git a/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs b/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs index def58bf1765fe..2bead2a996d6e 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs @@ -66,7 +66,7 @@ impl Rule for NoAwaitExpressionMember { if member_expr.optional() { return fixer.noop(); } - let Some(AstKind::VariableDeclarator(parent)) = + let AstKind::VariableDeclarator(parent) = ctx.nodes().parent_kind(node.id()) else { return fixer.noop(); diff --git a/crates/oxc_linter/src/rules/unicorn/no_lonely_if.rs b/crates/oxc_linter/src/rules/unicorn/no_lonely_if.rs index 1c74e3055891a..787be005b20cd 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_lonely_if.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_lonely_if.rs @@ -55,9 +55,7 @@ impl Rule for NoLonelyIf { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); let parent_if_stmt_span = match parent.kind() { AstKind::BlockStatement(block_stmt) => { @@ -65,11 +63,8 @@ impl Rule for NoLonelyIf { return; } - let Some(parent) = ctx.nodes().parent_node(parent.id()) else { - return; - }; - - let AstKind::IfStatement(parent_if_stmt) = parent.kind() else { + let AstKind::IfStatement(parent_if_stmt) = ctx.nodes().parent_kind(parent.id()) + else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/no_nested_ternary.rs b/crates/oxc_linter/src/rules/unicorn/no_nested_ternary.rs index deebd4d81f1a7..8d617b6a5f5c8 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_nested_ternary.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_nested_ternary.rs @@ -70,7 +70,8 @@ impl Rule for NoNestedTernary { let mut nested_level = 0; let mut current_node = node; - while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { + loop { + let parent_node = ctx.nodes().parent_node(current_node.id()); match parent_node.kind() { AstKind::ConditionalExpression(_) => { nested_level += 1; @@ -87,8 +88,7 @@ impl Rule for NoNestedTernary { match nested_level { 0 => {} 1 => { - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { unreachable!() }; - if let AstKind::ParenthesizedExpression(_) = parent_node.kind() { + if let AstKind::ParenthesizedExpression(_) = ctx.nodes().parent_kind(node.id()) { return; } ctx.diagnostic_with_fix(unparenthesized_nested_ternary(cond_expr.span), |fixer| { diff --git a/crates/oxc_linter/src/rules/unicorn/no_object_as_default_parameter.rs b/crates/oxc_linter/src/rules/unicorn/no_object_as_default_parameter.rs index 72efb1ab460a4..b6011ae6eb3bf 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_object_as_default_parameter.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_object_as_default_parameter.rs @@ -60,11 +60,7 @@ impl Rule for NoObjectAsDefaultParameter { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; - - if !matches!(parent.kind(), AstKind::FormalParameter(_)) { + if !matches!(ctx.nodes().parent_kind(node.id()), AstKind::FormalParameter(_)) { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/no_static_only_class.rs b/crates/oxc_linter/src/rules/unicorn/no_static_only_class.rs index d88387eada6b1..6dffe9269e332 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_static_only_class.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_static_only_class.rs @@ -118,12 +118,8 @@ impl Rule for NoStaticOnlyClass { return fixer.noop(); } - let Some(parent) = ctx.nodes().parent_kind(node.id()) else { - return fixer.noop(); - }; - if (matches!( - parent, + ctx.nodes().parent_kind(node.id()), AstKind::ExportDefaultDeclaration(_) | AstKind::ReturnStatement(_) ) && class.id.is_some()) { diff --git a/crates/oxc_linter/src/rules/unicorn/no_unnecessary_await.rs b/crates/oxc_linter/src/rules/unicorn/no_unnecessary_await.rs index 2ca4c97fca25e..c86ce7dcbc419 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_unnecessary_await.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_unnecessary_await.rs @@ -56,17 +56,16 @@ impl Rule for NoUnnecessaryAwait { || matches!(expr.argument, Expression::ClassExpression(_)) } || { // `+await +1` -> `++1` - ctx.nodes().parent_node(node.id()).is_some_and(|parent| { - if let ( - AstKind::UnaryExpression(parent_unary), - Expression::UnaryExpression(inner_unary), - ) = (parent.kind(), &expr.argument) - { - parent_unary.operator == inner_unary.operator - } else { - false - } - }) + let parent = ctx.nodes().parent_node(node.id()); + if let ( + AstKind::UnaryExpression(parent_unary), + Expression::UnaryExpression(inner_unary), + ) = (parent.kind(), &expr.argument) + { + parent_unary.operator == inner_unary.operator + } else { + false + } } { ctx.diagnostic(no_unnecessary_await_diagnostic(Span::sized(expr.span.start, 5))); } else { diff --git a/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs b/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs index a6346a6dadda2..fd1b9b29a55f6 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs @@ -178,15 +178,14 @@ fn get_function_like_node<'a, 'b>( let mut is_in_try_statement = false; let fnx = loop { - if let Some(grand_parent) = ctx.nodes().parent_node(parent.id()) { - parent = grand_parent; - if parent.kind().is_function_like() { - break parent; - } - if matches!(parent.kind(), AstKind::TryStatement(_)) { - is_in_try_statement = true; - } - } else { + parent = ctx.nodes().parent_node(parent.id()); + if parent.kind().is_function_like() { + break parent; + } + if matches!(parent.kind(), AstKind::TryStatement(_)) { + is_in_try_statement = true; + } + if matches!(parent.kind(), AstKind::Program(_)) { return None; } }; @@ -268,18 +267,13 @@ fn is_bind_member_expression(node: &AstNode) -> bool { } fn match_arrow_function_body<'a>(ctx: &LintContext<'a>, parent: &AstNode<'a>) -> bool { - match ctx.nodes().parent_node(parent.id()) { - Some(arrow_function_body) => match arrow_function_body.kind() { - AstKind::FunctionBody(_) => match ctx.nodes().parent_node(arrow_function_body.id()) { - Some(arrow_function) => { - matches!(arrow_function.kind(), AstKind::ArrowFunctionExpression(_)) - } - None => false, - }, - _ => false, - }, - None => false, + let parent = ctx.nodes().parent_node(parent.id()); + if !matches!(parent.kind(), AstKind::FunctionBody(_)) { + return false; } + + let grand_parent = ctx.nodes().parent_node(parent.id()); + matches!(grand_parent.kind(), AstKind::ArrowFunctionExpression(_)) } fn generate_fix<'a>( @@ -310,24 +304,20 @@ fn generate_fix<'a>( return fixer.noop(); } if is_yield { - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if let Some(grand_parent) = ctx.nodes().parent_node(parent.id()) { - if !matches!( - grand_parent.kind(), - AstKind::ExpressionStatement(_) | AstKind::ParenthesizedExpression(_) - ) { - return fixer.noop(); - } - } + let parent = ctx.nodes().parent_node(node.id()); + let grand_parent = ctx.nodes().parent_node(parent.id()); + if !matches!( + grand_parent.kind(), + AstKind::ExpressionStatement(_) | AstKind::ParenthesizedExpression(_) + ) { + return fixer.noop(); } } } let node = get_parenthesized_node(node, ctx); - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return fixer.noop(); - }; + let parent = ctx.nodes().parent_node(node.id()); let is_arrow_function_body = match parent.kind() { AstKind::ExpressionStatement(_) => match_arrow_function_body(ctx, parent), @@ -383,7 +373,8 @@ fn get_parenthesized_node<'a, 'b>( ctx: &'a LintContext<'b>, ) -> &'a AstNode<'b> { let mut node = node; - while let Some(parent_node) = ctx.nodes().parent_node(node.id()) { + loop { + let parent_node = ctx.nodes().parent_node(node.id()); if let AstKind::ParenthesizedExpression(_) = parent_node.kind() { node = parent_node; } else { diff --git a/crates/oxc_linter/src/rules/unicorn/no_useless_undefined.rs b/crates/oxc_linter/src/rules/unicorn/no_useless_undefined.rs index 8961803dfc412..4de4a2f75dee2 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_useless_undefined.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_useless_undefined.rs @@ -127,10 +127,9 @@ fn is_undefined(arg: &Argument) -> bool { } fn is_has_function_return_type(node: &AstNode, ctx: &LintContext<'_>) -> bool { - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return false; - }; + let parent_node = ctx.nodes().parent_node(node.id()); match parent_node.kind() { + AstKind::Program(_) => false, AstKind::ArrowFunctionExpression(arrow_func_express) => { arrow_func_express.return_type.is_some() } @@ -156,16 +155,15 @@ impl Rule for NoUselessUndefined { if undefined_literal.name == "undefined" => { let mut parent_node: &AstNode<'a> = node; - while let Some(parent) = ctx.nodes().parent_node(parent_node.id()) { - let parent_kind = parent.kind(); - - if let AstKind::ParenthesizedExpression(_) = parent_kind { + loop { + let parent = ctx.nodes().parent_node(parent_node.id()); + if let AstKind::ParenthesizedExpression(_) = parent.kind() { parent_node = parent; } else { break; } } - let Some(parent_node) = ctx.nodes().parent_node(parent_node.id()) else { return }; + let parent_node = ctx.nodes().parent_node(parent_node.id()); let parent_node_kind = parent_node.kind(); match parent_node_kind { @@ -204,19 +202,13 @@ impl Rule for NoUselessUndefined { if !self.check_arrow_function_body { return; } - let Some(grand_parent_node) = ctx.nodes().parent_node(parent_node.id()) - else { - return; - }; + let grand_parent_node = ctx.nodes().parent_node(parent_node.id()); let grand_parent_node_kind = grand_parent_node.kind(); let AstKind::FunctionBody(func_body) = grand_parent_node_kind else { return; }; - let Some(grand_grand_parent_node) = - ctx.nodes().parent_node(grand_parent_node.id()) - else { - return; - }; + let grand_grand_parent_node = + ctx.nodes().parent_node(grand_parent_node.id()); let grand_grand_parent_node_kind = grand_grand_parent_node.kind(); let AstKind::ArrowFunctionExpression(_) = grand_grand_parent_node_kind else { @@ -234,10 +226,7 @@ impl Rule for NoUselessUndefined { } // `let foo = undefined` / `var foo = undefined` AstKind::VariableDeclarator(variable_declarator) => { - let Some(grand_parent_node) = ctx.nodes().parent_node(parent_node.id()) - else { - return; - }; + let grand_parent_node = ctx.nodes().parent_node(parent_node.id()); let grand_parent_node_kind = grand_parent_node.kind(); let AstKind::VariableDeclaration(_) = grand_parent_node_kind else { return; diff --git a/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs b/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs index 43b22043abb17..b54f6a5e18cbb 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs @@ -74,10 +74,8 @@ impl Rule for NoZeroFractions { |fixer| { let mut fixed = fmt.clone(); let is_decimal_integer = fmt.parse::().is_ok(); - let is_member_expression = ctx - .nodes() - .parent_node(node.id()) - .is_some_and(|parent_node| parent_node.kind().is_member_expression_kind()); + let is_member_expression = + ctx.nodes().parent_kind(node.id()).is_member_expression_kind(); if is_member_expression && is_decimal_integer { fixed = format!("({fixed})"); 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 24e4e2b317d85..f738cf7f268bc 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs @@ -138,12 +138,12 @@ impl Rule for PreferArrayFind { for reference in ctx.symbol_references(ident.symbol_id()) { match ctx.nodes().parent_kind(reference.node_id()) { - Some(AstKind::ComputedMemberExpression(c)) + AstKind::ComputedMemberExpression(c) if c.expression.is_number_0() => { zero_index_nodes.push(reference); } - Some(AstKind::VariableDeclarator(var_declarator)) => { + AstKind::VariableDeclarator(var_declarator) => { if let BindingPatternKind::ArrayPattern(array_pat) = &var_declarator.id.kind { @@ -154,7 +154,7 @@ impl Rule for PreferArrayFind { } } } - Some(AstKind::AssignmentExpression(assignment_expr)) => { + AstKind::AssignmentExpression(assignment_expr) => { if let AssignmentTarget::ArrayAssignmentTarget(target) = &assignment_expr.left { @@ -240,23 +240,14 @@ fn is_filter_call(call_expr: &CallExpression) -> bool { } fn is_left_hand_side<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - match parent.kind() { - AstKind::ArrayPattern(_) | AstKind::SimpleAssignmentTarget(_) => return true, - AstKind::AssignmentExpression(expr) => { - return expr.left.span() == node.span(); - } - AstKind::AssignmentPattern(expr) => return expr.left.span() == node.span(), - AstKind::UpdateExpression(expr) => return expr.argument.span() == node.span(), - AstKind::UnaryExpression(expr) if expr.operator == UnaryOperator::Delete => { - return true; - } - - _ => return false, - } + match ctx.nodes().parent_kind(node.id()) { + AstKind::ArrayPattern(_) | AstKind::SimpleAssignmentTarget(_) => true, + AstKind::AssignmentExpression(expr) => expr.left.span() == node.span(), + AstKind::AssignmentPattern(expr) => expr.left.span() == node.span(), + AstKind::UpdateExpression(expr) => expr.argument.span() == node.span(), + AstKind::UnaryExpression(expr) => expr.operator == UnaryOperator::Delete, + _ => false, } - - false } #[test] diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_event_target.rs b/crates/oxc_linter/src/rules/unicorn/prefer_event_target.rs index 23bab3ff052a9..df29e66df031e 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_event_target.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_event_target.rs @@ -53,11 +53,7 @@ impl Rule for PreferEventTarget { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; - - match parent.kind() { + match ctx.nodes().parent_kind(node.id()) { AstKind::Class(_) => {} AstKind::NewExpression(new_expr) => { let Expression::Identifier(callee_ident) = &new_expr.callee else { 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 c6e4bc959ae91..c8c1e741a764e 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs @@ -69,7 +69,7 @@ impl Rule for PreferGlobalThis { return; } - if let Some(AstKind::StaticMemberExpression(e)) = ctx.nodes().parent_kind(node.id()) { + if let AstKind::StaticMemberExpression(e) = ctx.nodes().parent_kind(node.id()) { if let Expression::Identifier(ident) = &e.object { if ident.name == "self" && WEB_WORKER_SPECIFIC_APIS.contains(&e.property.name.as_str()) @@ -110,8 +110,7 @@ impl Rule for PreferGlobalThis { /// `window[foo]`, `self[bar]`, etc. are allowed. fn is_computed_member_expression_object(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { - let Some(AstKind::ComputedMemberExpression(member_expr)) = ctx.nodes().parent_kind(node.id()) - else { + let AstKind::ComputedMemberExpression(member_expr) = ctx.nodes().parent_kind(node.id()) else { return false; }; let Expression::Identifier(obj_ident) = &member_expr.object.get_inner_expression() else { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_native_coercion_functions.rs b/crates/oxc_linter/src/rules/unicorn/prefer_native_coercion_functions.rs index 59c1a5acae901..0d511755b7267 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_native_coercion_functions.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_native_coercion_functions.rs @@ -84,10 +84,8 @@ impl Rule for PreferNativeCoercionFunctions { if func.r#async || func.generator || func.params.items.is_empty() { return; } - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if matches!(parent.kind(), AstKind::ObjectProperty(_)) { - return; - } + if matches!(ctx.nodes().parent_kind(node.id()), AstKind::ObjectProperty(_)) { + return; } if let Some(function_body) = &func.body { if let Some(call_expr_ident) = @@ -199,15 +197,11 @@ fn check_array_callback_methods( is_arrow: bool, ctx: &LintContext, ) -> bool { - let Some(parent) = ctx.nodes().parent_node(node_id) else { - return false; - }; + let parent = ctx.nodes().parent_node(node_id); let AstKind::Argument(parent_call_expr_arg) = parent.kind() else { return false; }; - let Some(grand_parent) = ctx.nodes().parent_node(parent.id()) else { - return false; - }; + let grand_parent = ctx.nodes().parent_node(parent.id()); let AstKind::CallExpression(call_expr) = grand_parent.kind() else { return false; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs index 1a2e6a88de124..abb71dd930bf0 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs @@ -128,21 +128,18 @@ impl Rule for PreferNumberProperties { "isNaN" | "isFinite" | "parseFloat" | "parseInt" ) && matches!( ctx.nodes().parent_kind(node.id()), - Some(AstKind::ObjectProperty(_)) + AstKind::ObjectProperty(_) )) { let fixer = |fixer: RuleFixer<'_, 'a>| match ctx.nodes().parent_kind(node.id()) { - Some(AstKind::ObjectProperty(object_property)) - if object_property.shorthand => - { + AstKind::ObjectProperty(object_property) if object_property.shorthand => { fixer.insert_text_before( &ident_ref.span, format!("{}: Number.", ident_ref.name.as_str()), ) } - Some(_) => fixer.insert_text_before(&ident_ref.span, "Number."), - None => unreachable!(), + _ => fixer.insert_text_before(&ident_ref.span, "Number."), }; if ident_ref.name.as_str() == "isNaN" || ident_ref.name.as_str() == "isFinite" { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_optional_catch_binding.rs b/crates/oxc_linter/src/rules/unicorn/prefer_optional_catch_binding.rs index 6c110779ab749..e7b3df6363624 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_optional_catch_binding.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_optional_catch_binding.rs @@ -55,10 +55,7 @@ impl Rule for PreferOptionalCatchBinding { if references_count != 0 { return; } - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; - let AstKind::CatchClause(catch_clause) = parent_node.kind() else { + let AstKind::CatchClause(catch_clause) = ctx.nodes().parent_kind(node.id()) else { return; }; ctx.diagnostic_with_fix( diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs b/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs index 4b30b95b61a30..0311779f454ce 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs @@ -186,10 +186,8 @@ impl Rule for PreferSetHas { if references.any(|reference| { let node = ctx.nodes().get_node(reference.node_id()); - let Some(parent_id) = ctx.nodes().parent_id(node.id()) else { - return true; - }; - let Some(AstKind::CallExpression(call_expression)) = ctx.nodes().parent_kind(parent_id) + let parent_id = ctx.nodes().parent_id(node.id()); + let AstKind::CallExpression(call_expression) = ctx.nodes().parent_kind(parent_id) else { return true; }; @@ -200,8 +198,7 @@ impl Rule for PreferSetHas { if arg.is_spread() { return true; } - let Some(AstKind::StaticMemberExpression(member_expr)) = - ctx.nodes().parent_kind(node.id()) + let AstKind::StaticMemberExpression(member_expr) = ctx.nodes().parent_kind(node.id()) else { return true; }; @@ -272,10 +269,9 @@ impl Rule for PreferSetHas { let references = symbol_table.get_resolved_references(symbol_id); for reference in references { let node = ctx.nodes().get_node(reference.node_id()); - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - continue; - }; - let AstKind::StaticMemberExpression(member_expr) = parent.kind() else { + let AstKind::StaticMemberExpression(member_expr) = + ctx.nodes().parent_kind(node.id()) + else { continue; }; let property_info = member_expr.static_property_info(); diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_raw.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_raw.rs index 50b617d6be8fe..4f1b3483cc762 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_raw.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_raw.rs @@ -75,72 +75,68 @@ impl Rule for PreferStringRaw { let parent_node = ctx.nodes().parent_node(node.id()); - if let Some(parent_node) = parent_node { - match parent_node.kind() { - AstKind::Directive(_) => return, - AstKind::ImportDeclaration(decl) => { - if string_literal.span == decl.source.span { + match parent_node.kind() { + AstKind::Directive(_) => return, + AstKind::ImportDeclaration(decl) => { + if string_literal.span == decl.source.span { + return; + } + } + AstKind::ExportNamedDeclaration(decl) => { + if let Some(source) = &decl.source { + if string_literal.span == source.span { return; } } - AstKind::ExportNamedDeclaration(decl) => { - if let Some(source) = &decl.source { - if string_literal.span == source.span { - return; - } - } + } + AstKind::ExportAllDeclaration(decl) => { + if string_literal.span == decl.source.span { + return; } - AstKind::ExportAllDeclaration(decl) => { - if string_literal.span == decl.source.span { - return; - } + } + AstKind::ObjectProperty(prop) => { + let PropertyKey::StringLiteral(key) = &prop.key else { + return; + }; + + if !prop.computed && string_literal.span == key.span { + return; } - AstKind::ObjectProperty(prop) => { + } + AstKind::PropertyKey(_) => { + if let AstKind::ObjectProperty(prop) = ctx.nodes().parent_kind(parent_node.id()) { let PropertyKey::StringLiteral(key) = &prop.key else { return; }; - if !prop.computed && string_literal.span == key.span { + if !prop.computed && key.span == string_literal.span { return; } } - AstKind::PropertyKey(_) => { - if let Some(AstKind::ObjectProperty(prop)) = - ctx.nodes().parent_node(parent_node.id()).map(AstNode::kind) - { - let PropertyKey::StringLiteral(key) = &prop.key else { - return; - }; - - if !prop.computed && key.span == string_literal.span { - return; - } - } - } - AstKind::JSXAttribute(attr) => { - let Some(JSXAttributeValue::StringLiteral(value)) = &attr.value else { - return; - }; + } + AstKind::JSXAttribute(attr) => { + let Some(JSXAttributeValue::StringLiteral(value)) = &attr.value else { + return; + }; - if value.span == string_literal.span { - return; - } + if value.span == string_literal.span { + return; + } + } + AstKind::TSEnumMember(member) => { + if member.span == string_literal.span { + return; } - AstKind::TSEnumMember(member) => { - if member.span == string_literal.span { - return; - } - let TSEnumMemberName::String(id) = &member.id else { - return; - }; + let TSEnumMemberName::String(id) = &member.id else { + return; + }; - if id.span == string_literal.span { - return; - } + if id.span == string_literal.span { + return; } - _ => {} } + _ => {} } let raw = ctx.source_range(string_literal.span); diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs index da5e7c15ad817..051560ad73f83 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs @@ -64,9 +64,7 @@ impl Rule for PreferTypeError { return; } - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; + let parent = ctx.nodes().parent_node(node.id()); let AstKind::BlockStatement(block_stmt) = parent.kind() else { return; @@ -76,11 +74,7 @@ impl Rule for PreferTypeError { return; } - let Some(parent) = ctx.nodes().parent_node(parent.id()) else { - return; - }; - - let AstKind::IfStatement(if_stmt) = parent.kind() else { + let AstKind::IfStatement(if_stmt) = ctx.nodes().parent_kind(parent.id()) else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/text_encoding_identifier_case.rs b/crates/oxc_linter/src/rules/unicorn/text_encoding_identifier_case.rs index 35f0b9976c2e4..64946de3a9d45 100644 --- a/crates/oxc_linter/src/rules/unicorn/text_encoding_identifier_case.rs +++ b/crates/oxc_linter/src/rules/unicorn/text_encoding_identifier_case.rs @@ -85,9 +85,7 @@ impl Rule for TextEncodingIdentifierCase { } fn is_jsx_meta_elem_with_charset_attr(id: NodeId, ctx: &LintContext) -> bool { - let Some(parent) = ctx.nodes().parent_node(id) else { - return false; - }; + let parent = ctx.nodes().parent_node(id); let AstKind::JSXAttribute(jsx_attr) = parent.kind() else { return false; }; @@ -97,8 +95,7 @@ fn is_jsx_meta_elem_with_charset_attr(id: NodeId, ctx: &LintContext) -> bool { if !ident.name.eq_ignore_ascii_case("charset") { return false; } - let Some(AstKind::JSXOpeningElement(opening_elem)) = ctx.nodes().parent_kind(parent.id()) - else { + let AstKind::JSXOpeningElement(opening_elem) = ctx.nodes().parent_kind(parent.id()) else { return false; }; opening_elem diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 167be7563334c..be68f32a0877a 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -180,21 +180,17 @@ pub fn iter_possible_jest_call_node<'a, 'c>( std::iter::from_fn(move || { loop { let parent = semantic.nodes().parent_node(id); - if let Some(parent) = parent { - let parent_kind = parent.kind(); - if matches!(parent_kind, AstKind::CallExpression(_)) { - id = parent.id(); - return Some(PossibleJestNode { node: parent, original }); - } else if matches!( - parent_kind, - AstKind::StaticMemberExpression(_) - | AstKind::TaggedTemplateExpression(_) - | AstKind::ComputedMemberExpression(_) - ) { - id = parent.id(); - } else { - return None; - } + let parent_kind = parent.kind(); + if matches!(parent_kind, AstKind::CallExpression(_)) { + id = parent.id(); + return Some(PossibleJestNode { node: parent, original }); + } else if matches!( + parent_kind, + AstKind::StaticMemberExpression(_) + | AstKind::TaggedTemplateExpression(_) + | AstKind::ComputedMemberExpression(_) + ) { + id = parent.id(); } else { return None; } @@ -214,8 +210,7 @@ fn collect_ids_referenced_to_import<'a, 'c>( let symbol_id = SymbolId::from_usize(symbol_id); if semantic.scoping().symbol_flags(symbol_id).is_import() { let id = semantic.scoping().symbol_declaration(symbol_id); - let Some(AstKind::ImportDeclaration(import_decl)) = - semantic.nodes().parent_kind(id) + let AstKind::ImportDeclaration(import_decl) = semantic.nodes().parent_kind(id) else { return None; }; diff --git a/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs b/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs index 057eafbae2899..882766cc128f2 100644 --- a/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs +++ b/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs @@ -84,14 +84,12 @@ pub fn parse_jest_fn_call<'a>( // Ensure that we're at the "top" of the function call chain otherwise when // parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though // the full chain is not a valid jest function call chain - if ctx.nodes().parent_node(node.id()).is_some_and(|parent_node| { - matches!( - parent_node.kind(), - AstKind::CallExpression(_) - | AstKind::StaticMemberExpression(_) - | AstKind::ComputedMemberExpression(_) - ) - }) { + if matches!( + ctx.nodes().parent_kind(node.id()), + AstKind::CallExpression(_) + | AstKind::StaticMemberExpression(_) + | AstKind::ComputedMemberExpression(_) + ) { return None; } @@ -146,11 +144,11 @@ fn parse_jest_expect_fn_call<'a>( if matches!(expect_error, Some(ExpectError::MatcherNotFound)) { // If the parent is a member expression, we can assume that the matcher // is not called, so we can set the error to `MatcherNotCalled`. - match ctx.nodes().parent_kind(node.id())? { - AstKind::StaticMemberExpression(_) | AstKind::ComputedMemberExpression(_) => { - expect_error = Some(ExpectError::MatcherNotCalled); - } - _ => {} + if matches!( + ctx.nodes().parent_kind(node.id()), + AstKind::StaticMemberExpression(_) | AstKind::ComputedMemberExpression(_) + ) { + expect_error = Some(ExpectError::MatcherNotCalled); } } @@ -244,9 +242,7 @@ fn is_top_most_call_expr<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a> let mut node = node; loop { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return true; - }; + let parent = ctx.nodes().parent_node(node.id()); match parent.kind() { AstKind::CallExpression(_) => return false, diff --git a/crates/oxc_linter/src/utils/jsdoc.rs b/crates/oxc_linter/src/utils/jsdoc.rs index db41f8502df10..b11de7c6f666b 100644 --- a/crates/oxc_linter/src/utils/jsdoc.rs +++ b/crates/oxc_linter/src/utils/jsdoc.rs @@ -64,6 +64,7 @@ pub fn get_function_nearest_jsdoc_node<'a, 'b>( // Tie-breaker, otherwise every loop will end at `Program` node! // Maybe more checks should be added match current_node.kind() { + AstKind::Program(_) => return None, AstKind::VariableDeclaration(_) | AstKind::MethodDefinition(_) | AstKind::PropertyDefinition(_) @@ -79,13 +80,13 @@ pub fn get_function_nearest_jsdoc_node<'a, 'b>( => { // /** This JSDoc should NOT found for `VariableDeclaration` */ // export const foo = () => {} - let parent_node = semantic.nodes().parent_node(current_node.id())?; + let parent_node = semantic.nodes().parent_node(current_node.id()); match parent_node.kind() { AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => return Some(parent_node), _ => return None } }, - _ => current_node = semantic.nodes().parent_node(current_node.id())?, + _ => current_node = semantic.nodes().parent_node(current_node.id()), } } diff --git a/crates/oxc_linter/src/utils/unicorn/boolean.rs b/crates/oxc_linter/src/utils/unicorn/boolean.rs index 8bebd82dc6059..0515da0186b0d 100644 --- a/crates/oxc_linter/src/utils/unicorn/boolean.rs +++ b/crates/oxc_linter/src/utils/unicorn/boolean.rs @@ -29,9 +29,9 @@ pub fn is_boolean_call(kind: &AstKind) -> bool { } pub fn is_boolean_call_argument<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { let arg_id = ctx.nodes().parent_id(node.id()); - let parent = arg_id.and_then(|id| ctx.nodes().parent_kind(id)); + let parent = ctx.nodes().parent_kind(arg_id); // println!("{parent:#?}"); - matches!(parent, Some(parent) if is_boolean_call(&parent)) + is_boolean_call(&parent) } pub fn is_boolean_node<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool { @@ -89,11 +89,10 @@ pub fn get_boolean_ancestor<'a, 'b>( cur = parent; continue; } - if let Some(parent) = ctx.nodes().parent_node(parent.id()) { - if is_boolean_call(&parent.kind()) { - cur = parent; - continue; - } + let parent = ctx.nodes().parent_node(parent.id()); + if is_boolean_call(&parent.kind()) { + cur = parent; + continue; } break; } diff --git a/crates/oxc_mangler/src/keep_names.rs b/crates/oxc_mangler/src/keep_names.rs index d4c4a7a801f2c..16fbfc9afb0b4 100644 --- a/crates/oxc_mangler/src/keep_names.rs +++ b/crates/oxc_mangler/src/keep_names.rs @@ -112,7 +112,7 @@ impl<'a, 'b: 'a> NameSymbolCollector<'a, 'b> { } fn is_name_set_reference_node(&self, node: &AstNode, reference_id: ReferenceId) -> bool { - let Some(parent_node) = self.ast_nodes.parent_node(node.id()) else { return false }; + let parent_node = self.ast_nodes.parent_node(node.id()); match parent_node.kind() { AstKind::SimpleAssignmentTarget(_) => { let Some((grand_parent_node_kind, grand_grand_parent_node_kind)) = diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 019669252b3a5..801fbff732568 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -20,10 +20,9 @@ pub trait Binder<'a> { impl<'a> Binder<'a> for VariableDeclarator<'a> { fn bind(&self, builder: &mut SemanticBuilder<'a>) { - let is_declare = builder + let is_declare = matches!(builder .nodes - .parent_kind(builder.current_node_id) - .is_some_and(|kind| matches!(kind, AstKind::VariableDeclaration(decl) if decl.declare)); + .parent_kind(builder.current_node_id), AstKind::VariableDeclaration(decl) if decl.declare); let (mut includes, excludes) = match self.kind { VariableDeclarationKind::Const @@ -143,8 +142,7 @@ fn is_function_part_of_if_statement(function: &Function, builder: &SemanticBuild if builder.current_scope_flags().is_strict_mode() { return false; } - let Some(AstKind::IfStatement(stmt)) = builder.nodes.parent_kind(builder.current_node_id) - else { + let AstKind::IfStatement(stmt) = builder.nodes.parent_kind(builder.current_node_id) else { return false; }; if let Statement::FunctionDeclaration(func) = &stmt.consequent { @@ -193,9 +191,7 @@ impl<'a> Binder<'a> for Function<'a> { } // Bind scope flags: GetAccessor | SetAccessor - if let Some(AstKind::ObjectProperty(prop)) = - builder.nodes.parent_kind(builder.current_node_id) - { + if let AstKind::ObjectProperty(prop) = builder.nodes.parent_kind(builder.current_node_id) { let flags = builder.scoping.scope_flags_mut(builder.current_scope_id); match prop.kind { PropertyKind::Get => *flags |= ScopeFlags::GetAccessor, @@ -216,7 +212,7 @@ impl<'a> Binder<'a> for Function<'a> { impl<'a> Binder<'a> for BindingRestElement<'a> { // Binds the FormalParameters's rest of a function or method. fn bind(&self, builder: &mut SemanticBuilder) { - let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap(); + let parent_kind = builder.nodes.parent_kind(builder.current_node_id); let AstKind::FormalParameters(parameters) = parent_kind else { return; }; @@ -238,7 +234,7 @@ impl<'a> Binder<'a> for BindingRestElement<'a> { impl<'a> Binder<'a> for FormalParameter<'a> { // Binds the FormalParameter of a function or method. fn bind(&self, builder: &mut SemanticBuilder) { - let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap(); + let parent_kind = builder.nodes.parent_kind(builder.current_node_id); let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() }; if parameters.kind.is_signature() { @@ -305,8 +301,7 @@ fn declare_symbol_for_import_specifier( builder: &mut SemanticBuilder, ) { let includes = if is_type - || builder.nodes.parent_kind(builder.current_node_id).is_some_and( - |decl| matches!(decl, AstKind::ImportDeclaration(decl) if decl.import_kind.is_type()), + || matches!(builder.nodes.parent_kind(builder.current_node_id), AstKind::ImportDeclaration(decl) if decl.import_kind.is_type(), ) { SymbolFlags::TypeImport } else { @@ -688,7 +683,7 @@ impl<'a> Binder<'a> for TSTypeParameter<'a> { fn bind(&self, builder: &mut SemanticBuilder) { let scope_id = if matches!( builder.nodes.parent_kind(builder.current_node_id), - Some(AstKind::TSInferType(_)) + AstKind::TSInferType(_) ) { builder .scoping diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index e9e1727284f0c..e9f55e398f745 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -318,9 +318,7 @@ impl<'a> SemanticBuilder<'a> { } fn pop_ast_node(&mut self) { - if let Some(parent_id) = self.nodes.parent_id(self.current_node_id) { - self.current_node_id = parent_id; - } + self.current_node_id = self.nodes.parent_id(self.current_node_id); } #[inline] diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 3c375bc1a98ef..53e331d88e6d4 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -341,7 +341,7 @@ pub fn check_module_declaration(decl: &ModuleDeclarationKind, ctx: &SemanticBuil ctx.error(module_code(text, span)); } ModuleKind::Module => { - if matches!(ctx.nodes.parent_kind(ctx.current_node_id), Some(AstKind::Program(_))) { + if matches!(ctx.nodes.parent_kind(ctx.current_node_id), AstKind::Program(_)) { return; } ctx.error(top_level(text, span)); @@ -749,7 +749,7 @@ pub fn check_class(class: &Class, ctx: &SemanticBuilder<'_>) { && class.id.is_none() && !matches!( ctx.nodes.parent_kind(ctx.current_node_id), - Some(AstKind::ExportDefaultDeclaration(_)) + AstKind::ExportDefaultDeclaration(_) ) { let start = class.span.start; @@ -798,8 +798,8 @@ fn unexpected_super_reference(span: Span) -> OxcDiagnostic { pub fn check_super(sup: &Super, ctx: &SemanticBuilder<'_>) { let super_call_span = match ctx.nodes.parent_kind(ctx.current_node_id) { - Some(AstKind::CallExpression(expr)) => Some(expr.span), - Some(AstKind::NewExpression(expr)) => Some(expr.span), + AstKind::CallExpression(expr) => Some(expr.span), + AstKind::NewExpression(expr) => Some(expr.span), _ => None, }; @@ -809,7 +809,7 @@ pub fn check_super(sup: &Super, ctx: &SemanticBuilder<'_>) { if flags.is_function() && matches!( ctx.nodes.parent_kind(ctx.scoping.get_node_id(scope_id)), - Some(AstKind::ObjectProperty(_)) + AstKind::ObjectProperty(_) ) { if let Some(super_call_span) = super_call_span { @@ -877,10 +877,8 @@ pub fn check_super(sup: &Super, ctx: &SemanticBuilder<'_>) { // * It is a Syntax Error if FunctionBody Contains SuperProperty is true. // Check this function if is a class method, if it isn't, then it a plain function let function_node_id = ctx.scoping.get_node_id(scope_id); - let is_class_method = matches!( - ctx.nodes.parent_kind(function_node_id), - Some(AstKind::MethodDefinition(_)) - ); + let is_class_method = + matches!(ctx.nodes.parent_kind(function_node_id), AstKind::MethodDefinition(_)); if !is_class_method { ctx.error(unexpected_super_reference(sup.span)); } diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 5d7dd20bf1b72..51c8a9b08219e 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -485,7 +485,7 @@ pub fn check_jsx_expression_container( ctx: &SemanticBuilder<'_>, ) { if matches!(container.expression, JSXExpression::EmptyExpression(_)) - && matches!(ctx.nodes.parent_kind(ctx.current_node_id), Some(AstKind::JSXAttribute(_))) + && matches!(ctx.nodes.parent_kind(ctx.current_node_id), AstKind::JSXAttribute(_)) { ctx.error(invalid_jsx_attribute_value(container.span())); } diff --git a/crates/oxc_semantic/src/class/builder.rs b/crates/oxc_semantic/src/class/builder.rs index 985067bd60a5c..94de8f803ac4e 100644 --- a/crates/oxc_semantic/src/class/builder.rs +++ b/crates/oxc_semantic/src/class/builder.rs @@ -36,7 +36,7 @@ impl<'a> ClassTableBuilder<'a> { current_node_id: NodeId, nodes: &AstNodes, ) { - let parent_id = nodes.parent_id(current_node_id).unwrap_or_else(|| unreachable!()); + let parent_id = nodes.parent_id(current_node_id); self.current_class_id = Some(self.classes.declare_class(self.current_class_id, parent_id)); for element in &class.body { @@ -102,25 +102,21 @@ impl<'a> ClassTableBuilder<'a> { nodes: &AstNodes, ) { let parent_kind = nodes.parent_kind(current_node_id); - if let Some(parent_kind) = parent_kind { - if matches!(parent_kind, AstKind::PrivateInExpression(_)) - || parent_kind.is_member_expression_kind() - { - if let Some(class_id) = self.current_class_id { - let element_ids = self.classes.get_element_ids( - class_id, - &ident.name, - /* is_private */ true, - ); - - let reference = PrivateIdentifierReference::new( - current_node_id, - ident.name, - ident.span, - element_ids, - ); - self.classes.add_private_identifier_reference(class_id, reference); - } + + if matches!(parent_kind, AstKind::PrivateInExpression(_)) + || parent_kind.is_member_expression_kind() + { + if let Some(class_id) = self.current_class_id { + let element_ids = + self.classes.get_element_ids(class_id, &ident.name, /* is_private */ true); + + let reference = PrivateIdentifierReference::new( + current_node_id, + ident.name, + ident.span, + element_ids, + ); + self.classes.add_private_identifier_reference(class_id, reference); } } } diff --git a/crates/oxc_semantic/src/node.rs b/crates/oxc_semantic/src/node.rs index d5ed90f63e572..3be97d9b75a2a 100644 --- a/crates/oxc_semantic/src/node.rs +++ b/crates/oxc_semantic/src/node.rs @@ -102,7 +102,7 @@ pub struct AstNodes<'a> { root: Option, nodes: IndexVec>, /// `node` -> `parent` - parent_ids: IndexVec>, + parent_ids: IndexVec, } impl<'a> AstNodes<'a> { @@ -152,18 +152,18 @@ impl<'a> AstNodes<'a> { /// Get id of this node's parent. #[inline] - pub fn parent_id(&self, node_id: NodeId) -> Option { + pub fn parent_id(&self, node_id: NodeId) -> NodeId { self.parent_ids[node_id] } /// Get the kind of the parent node. - pub fn parent_kind(&self, node_id: NodeId) -> Option> { - self.parent_id(node_id).map(|node_id| self.kind(node_id)) + pub fn parent_kind(&self, node_id: NodeId) -> AstKind<'a> { + self.kind(self.parent_id(node_id)) } /// Get a reference to a node's parent. - pub fn parent_node(&self, node_id: NodeId) -> Option<&AstNode<'a>> { - self.parent_id(node_id).map(|node_id| self.get_node(node_id)) + pub fn parent_node(&self, node_id: NodeId) -> &AstNode<'a> { + self.get_node(self.parent_id(node_id)) } #[inline] @@ -216,8 +216,10 @@ impl<'a> AstNodes<'a> { /// /// [`Program`]: oxc_ast::ast::Program pub fn ancestor_ids(&self, node_id: NodeId) -> impl Iterator + '_ { - let parent_ids = &self.parent_ids; - std::iter::successors(Some(node_id), |&node_id| parent_ids[node_id]) + std::iter::successors(Some(node_id), |&node_id| { + let parent_id = self.parent_ids[node_id]; + if parent_id == node_id { None } else { Some(parent_id) } + }) } /// Create and add an [`AstNode`] to the [`AstNodes`] tree and get its [`NodeId`]. @@ -234,7 +236,7 @@ impl<'a> AstNodes<'a> { cfg_id: BlockNodeId, flags: NodeFlags, ) -> NodeId { - let node_id = self.parent_ids.push(Some(parent_node_id)); + let node_id = self.parent_ids.push(parent_node_id); let node = AstNode::new(kind, scope_id, cfg_id, flags, node_id); self.nodes.push(node); node_id @@ -248,7 +250,8 @@ impl<'a> AstNodes<'a> { cfg_id: BlockNodeId, flags: NodeFlags, ) -> NodeId { - let node_id = self.parent_ids.push(None); + let node_id = self.parent_ids.push(NodeId::DUMMY); + self.parent_ids[node_id] = node_id; self.root = Some(node_id); let node = AstNode::new(kind, scope_id, cfg_id, flags, node_id); self.nodes.push(node); @@ -282,7 +285,8 @@ impl<'s, 'a> Iterator for AstNodeParentIter<'s, 'a> { fn next(&mut self) -> Option { if let Some(node_id) = self.current_node_id { - self.current_node_id = self.nodes.parent_ids[node_id]; + let parent_id = self.nodes.parent_ids[node_id]; + self.current_node_id = if parent_id == node_id { None } else { Some(parent_id) }; Some(self.nodes.get_node(node_id)) } else { None