From 25da8045c5293ca99cf176a1e22b4c3d6c40cc6f Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Sat, 27 Apr 2024 16:23:30 +0100 Subject: [PATCH 1/2] refactor(ast): squash nested enums --- crates/oxc_allocator/src/arena.rs | 9 + crates/oxc_ast/src/ast/js.rs | 562 +++++++----- crates/oxc_ast/src/ast/jsx.rs | 17 +- crates/oxc_ast/src/ast/macros.rs | 823 ++++++++++++++++++ crates/oxc_ast/src/ast/mod.rs | 2 + crates/oxc_ast/src/ast/ts.rs | 178 ++-- crates/oxc_ast/src/ast_builder.rs | 62 +- crates/oxc_ast/src/ast_kind.rs | 4 +- crates/oxc_ast/src/lib.rs | 2 +- crates/oxc_ast/src/precedence.rs | 10 +- crates/oxc_ast/src/span.rs | 324 ++++++- .../gather_node_parts.rs | 10 +- .../syntax_directed_operations/prop_name.rs | 9 +- crates/oxc_ast/src/visit/visit.rs | 64 +- crates/oxc_ast/src/visit/visit_mut.rs | 64 +- crates/oxc_codegen/src/gen.rs | 129 ++- crates/oxc_codegen/src/gen_ts.rs | 32 +- crates/oxc_codegen/src/lib.rs | 2 +- crates/oxc_linter/src/ast_util.rs | 15 +- .../rules/deepscan/bad_bitwise_operator.rs | 2 +- .../src/rules/deepscan/bad_min_max_func.rs | 4 +- .../src/rules/deepscan/bad_replace_all_arg.rs | 6 +- .../rules/deepscan/number_arg_out_of_range.rs | 9 +- .../deepscan/uninvoked_array_callback.rs | 13 +- .../rules/eslint/array_callback_return/mod.rs | 21 +- .../array_callback_return/return_checker.rs | 6 +- .../src/rules/eslint/for_direction.rs | 5 +- .../src/rules/eslint/getter_return.rs | 12 +- .../rules/eslint/no_async_promise_executor.rs | 3 +- .../src/rules/eslint/no_case_declarations.rs | 46 +- .../oxc_linter/src/rules/eslint/no_console.rs | 2 +- .../eslint/no_constant_binary_expression.rs | 7 +- .../src/rules/eslint/no_control_regex.rs | 11 +- crates/oxc_linter/src/rules/eslint/no_eval.rs | 8 +- .../src/rules/eslint/no_extra_boolean_cast.rs | 6 +- .../src/rules/eslint/no_obj_calls.rs | 14 +- .../src/rules/eslint/no_regex_spaces.rs | 6 +- .../src/rules/eslint/no_self_assign.rs | 76 +- .../eslint/no_unsafe_optional_chaining.rs | 6 +- .../src/rules/eslint/no_useless_rename.rs | 8 +- .../oxc_linter/src/rules/eslint/use_isnan.rs | 32 +- crates/oxc_linter/src/rules/import/no_amd.rs | 3 +- .../src/rules/jest/expect_expect.rs | 6 +- .../src/rules/jest/no_conditional_expect.rs | 4 +- .../src/rules/jest/no_done_callback.rs | 10 +- .../src/rules/jest/no_identical_title.rs | 6 +- .../jest/no_interpolation_in_snapshots.rs | 7 +- .../src/rules/jest/no_jasmine_globals.rs | 12 +- .../src/rules/jest/no_mocks_import.rs | 9 +- .../rules/jest/no_restricted_jest_methods.rs | 4 +- .../rules/jest/no_test_return_statement.rs | 6 +- .../src/rules/jest/no_untyped_mock_factory.rs | 6 +- .../rules/jest/prefer_comparison_matcher.rs | 6 +- .../src/rules/jest/prefer_equality_matcher.rs | 2 +- .../src/rules/jest/prefer_expect_resolves.rs | 4 +- .../src/rules/jest/prefer_lowercase_title.rs | 11 +- .../jest/prefer_mock_promise_shorthand.rs | 6 +- .../src/rules/jest/prefer_spy_on.rs | 48 +- .../oxc_linter/src/rules/jest/prefer_to_be.rs | 15 +- .../src/rules/jest/prefer_to_contain.rs | 10 +- .../src/rules/jest/prefer_to_have_length.rs | 11 +- .../oxc_linter/src/rules/jest/prefer_todo.rs | 67 +- .../oxc_linter/src/rules/jest/require_hook.rs | 14 +- .../src/rules/jest/valid_describe_callback.rs | 76 +- .../oxc_linter/src/rules/jest/valid_expect.rs | 4 +- .../oxc_linter/src/rules/jest/valid_title.rs | 14 +- .../oxc_linter/src/rules/jsx_a11y/alt_text.rs | 10 +- .../src/rules/jsx_a11y/anchor_is_valid.rs | 32 +- .../src/rules/jsx_a11y/aria_role.rs | 7 +- .../src/rules/jsx_a11y/html_has_lang.rs | 8 +- .../src/rules/jsx_a11y/iframe_has_title.rs | 34 +- .../src/rules/jsx_a11y/img_redundant_alt.rs | 6 +- crates/oxc_linter/src/rules/jsx_a11y/lang.rs | 8 +- .../src/rules/jsx_a11y/media_has_caption.rs | 8 +- .../jsx_a11y/mouse_events_have_key_events.rs | 19 +- .../src/rules/jsx_a11y/no_access_key.rs | 8 +- .../src/rules/nextjs/inline_script_id.rs | 2 +- .../src/rules/nextjs/next_script_for_ga.rs | 6 +- .../rules/nextjs/no_async_client_component.rs | 15 +- .../src/rules/oxc/misrefactored_assign_op.rs | 19 +- .../src/rules/oxc/no_accumulating_spread.rs | 2 +- .../src/rules/oxc/no_barrel_file.rs | 11 +- .../src/rules/react/button_has_type.rs | 12 +- .../checked_requires_onchange_or_readonly.rs | 9 +- crates/oxc_linter/src/rules/react/jsx_key.rs | 4 +- .../src/rules/react/jsx_no_target_blank.rs | 12 +- .../rules/react/jsx_no_useless_fragment.rs | 6 +- .../src/rules/react/no_children_prop.rs | 6 +- .../oxc_linter/src/rules/react/no_danger.rs | 6 +- .../rules/react/no_direct_mutation_state.rs | 46 +- .../src/rules/react/no_is_mounted.rs | 17 +- .../src/rules/react/no_render_return_value.rs | 2 +- .../src/rules/react/no_string_refs.rs | 9 +- .../src/rules/react/require_render_return.rs | 4 +- .../react/void_dom_elements_no_children.rs | 13 +- .../rules/react_perf/jsx_no_jsx_as_prop.rs | 4 +- .../react_perf/jsx_no_new_array_as_prop.rs | 4 +- .../react_perf/jsx_no_new_function_as_prop.rs | 10 +- .../react_perf/jsx_no_new_object_as_prop.rs | 4 +- .../listener_map.rs | 92 +- .../adjacent_overload_signatures.rs | 31 +- .../src/rules/typescript/no_misused_new.rs | 2 +- .../no_non_null_asserted_optional_chain.rs | 25 +- .../src/rules/typescript/no_this_alias.rs | 35 +- .../typescript/prefer_enum_initializers.rs | 2 +- .../src/rules/typescript/prefer_for_of.rs | 30 +- .../typescript/triple_slash_reference.rs | 36 +- .../src/rules/unicorn/catch_error_name.rs | 5 +- .../src/rules/unicorn/error_message.rs | 27 +- .../rules/unicorn/explicit_length_check.rs | 3 +- .../src/rules/unicorn/new_for_builtins.rs | 8 +- .../src/rules/unicorn/no_array_for_each.rs | 9 +- .../src/rules/unicorn/no_array_reduce.rs | 8 +- .../unicorn/no_await_in_promise_methods.rs | 9 +- .../src/rules/unicorn/no_console_spaces.rs | 7 +- .../src/rules/unicorn/no_document_cookie.rs | 10 +- .../no_invalid_remove_event_listener.rs | 17 +- .../oxc_linter/src/rules/unicorn/no_null.rs | 6 +- .../no_single_promise_in_promise_methods.rs | 6 +- .../src/rules/unicorn/no_thenable.rs | 43 +- .../src/rules/unicorn/no_this_assignment.rs | 11 +- .../no_unreadable_array_destructuring.rs | 10 +- .../rules/unicorn/no_useless_length_check.rs | 8 +- .../src/rules/unicorn/no_useless_spread.rs | 7 +- .../unicorn/prefer_add_event_listener.rs | 10 +- .../src/rules/unicorn/prefer_array_flat.rs | 20 +- .../rules/unicorn/prefer_array_flat_map.rs | 4 +- .../src/rules/unicorn/prefer_array_some.rs | 8 +- .../unicorn/prefer_blob_reading_methods.rs | 4 +- .../src/rules/unicorn/prefer_code_point.rs | 4 +- .../src/rules/unicorn/prefer_date_now.rs | 8 +- .../rules/unicorn/prefer_dom_node_dataset.rs | 8 +- .../rules/unicorn/prefer_dom_node_remove.rs | 7 +- .../rules/unicorn/prefer_modern_dom_apis.rs | 12 +- .../rules/unicorn/prefer_modern_math_apis.rs | 12 +- .../prefer_native_coercion_functions.rs | 4 +- .../src/rules/unicorn/prefer_node_protocol.rs | 2 +- .../rules/unicorn/prefer_number_properties.rs | 8 +- .../rules/unicorn/prefer_prototype_methods.rs | 20 +- .../rules/unicorn/prefer_query_selector.rs | 9 +- .../src/rules/unicorn/prefer_reflect_apply.rs | 12 +- .../src/rules/unicorn/prefer_regexp_test.rs | 4 +- .../src/rules/unicorn/prefer_spread.rs | 17 +- .../unicorn/prefer_string_replace_all.rs | 15 +- .../src/rules/unicorn/prefer_type_error.rs | 10 +- .../unicorn/require_array_join_separator.rs | 7 +- .../src/rules/unicorn/throw_new_error.rs | 14 +- crates/oxc_linter/src/utils/jest.rs | 6 +- .../src/utils/jest/parse_jest_fn.rs | 10 +- crates/oxc_linter/src/utils/react.rs | 21 +- crates/oxc_linter/src/utils/unicorn.rs | 42 +- .../oxc_minifier/src/compressor/ast_util.rs | 20 +- crates/oxc_minifier/src/compressor/mod.rs | 14 +- crates/oxc_minifier/src/compressor/util.rs | 2 +- crates/oxc_minifier/tests/terser/mod.rs | 5 +- crates/oxc_module_lexer/src/lib.rs | 8 +- crates/oxc_parser/src/js/binding.rs | 2 +- crates/oxc_parser/src/js/declaration.rs | 2 +- crates/oxc_parser/src/js/expression.rs | 5 +- crates/oxc_parser/src/js/grammar.rs | 22 +- crates/oxc_parser/src/js/list.rs | 4 +- crates/oxc_parser/src/js/module.rs | 4 +- crates/oxc_parser/src/js/object.rs | 16 +- crates/oxc_parser/src/js/statement.rs | 6 +- crates/oxc_parser/src/jsx/mod.rs | 2 +- crates/oxc_parser/src/ts/list.rs | 6 +- crates/oxc_parser/src/ts/statement.rs | 2 +- crates/oxc_prettier/src/format/array.rs | 57 +- crates/oxc_prettier/src/format/assignment.rs | 15 +- .../oxc_prettier/src/format/call_arguments.rs | 52 +- .../src/format/call_expression.rs | 16 +- crates/oxc_prettier/src/format/mod.rs | 93 +- crates/oxc_prettier/src/format/module.rs | 2 +- crates/oxc_prettier/src/format/property.rs | 4 +- crates/oxc_prettier/src/needs_parens.rs | 136 +-- crates/oxc_semantic/src/binder.rs | 10 +- crates/oxc_semantic/src/builder.rs | 2 +- crates/oxc_semantic/src/checker/javascript.rs | 12 +- crates/oxc_semantic/src/checker/typescript.rs | 7 +- .../oxc_semantic/src/module_record/builder.rs | 4 +- .../src/helpers/module_imports.rs | 4 +- .../src/react/display_name/mod.rs | 30 +- crates/oxc_transformer/src/react/jsx/mod.rs | 20 +- .../oxc_transformer/src/react/jsx_self/mod.rs | 2 +- .../src/react/jsx_source/mod.rs | 4 +- .../src/typescript/annotations.rs | 16 +- crates/oxc_transformer/src/typescript/enum.rs | 15 +- .../oxc_transformer/src/typescript/module.rs | 6 +- .../src/typescript/namespace.rs | 189 ++-- tasks/prettier_conformance/src/spec.rs | 11 +- tasks/rulegen/src/main.rs | 110 ++- 191 files changed, 3082 insertions(+), 1945 deletions(-) create mode 100644 crates/oxc_ast/src/ast/macros.rs diff --git a/crates/oxc_allocator/src/arena.rs b/crates/oxc_allocator/src/arena.rs index ffd5a089b38ba..142464e725d48 100644 --- a/crates/oxc_allocator/src/arena.rs +++ b/crates/oxc_allocator/src/arena.rs @@ -39,6 +39,15 @@ impl<'alloc, T> Box<'alloc, T> { pub fn new_in(x: T, alloc: &Allocator) -> Self { Self(alloc.alloc(x).into(), PhantomData) } + + /// Create a fake `Box` with a dangling pointer. + /// # SAFETY + /// Safe to create, but must never be dereferenced, as does not point to a valid `T`. + /// Only purpose is for mocking types without allocating for const assertions. + #[allow(unsafe_code, clippy::missing_safety_doc)] + pub const unsafe fn dangling() -> Self { + Self(NonNull::dangling(), PhantomData) + } } impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> { diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 2abf85797749e..aad59dc490da8 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -17,6 +17,7 @@ use serde::Serialize; #[cfg(feature = "serialize")] use tsify::Tsify; +use super::inherit_variants; use super::{jsx::*, literal::*, ts::*}; #[cfg(feature = "serialize")] @@ -60,57 +61,113 @@ impl<'a> Program<'a> { } } +inherit_variants! { /// Expression +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum Expression<'a> { - BooleanLiteral(Box<'a, BooleanLiteral>), - NullLiteral(Box<'a, NullLiteral>), - NumericLiteral(Box<'a, NumericLiteral<'a>>), - BigintLiteral(Box<'a, BigIntLiteral<'a>>), - RegExpLiteral(Box<'a, RegExpLiteral<'a>>), - StringLiteral(Box<'a, StringLiteral<'a>>), - TemplateLiteral(Box<'a, TemplateLiteral<'a>>), - - Identifier(Box<'a, IdentifierReference<'a>>), - - MetaProperty(Box<'a, MetaProperty<'a>>), - Super(Box<'a, Super>), - - ArrayExpression(Box<'a, ArrayExpression<'a>>), - ArrowFunctionExpression(Box<'a, ArrowFunctionExpression<'a>>), - AssignmentExpression(Box<'a, AssignmentExpression<'a>>), - AwaitExpression(Box<'a, AwaitExpression<'a>>), - BinaryExpression(Box<'a, BinaryExpression<'a>>), - CallExpression(Box<'a, CallExpression<'a>>), - ChainExpression(Box<'a, ChainExpression<'a>>), - ClassExpression(Box<'a, Class<'a>>), - ConditionalExpression(Box<'a, ConditionalExpression<'a>>), - FunctionExpression(Box<'a, Function<'a>>), - ImportExpression(Box<'a, ImportExpression<'a>>), - LogicalExpression(Box<'a, LogicalExpression<'a>>), - MemberExpression(Box<'a, MemberExpression<'a>>), - NewExpression(Box<'a, NewExpression<'a>>), - ObjectExpression(Box<'a, ObjectExpression<'a>>), - ParenthesizedExpression(Box<'a, ParenthesizedExpression<'a>>), - SequenceExpression(Box<'a, SequenceExpression<'a>>), - TaggedTemplateExpression(Box<'a, TaggedTemplateExpression<'a>>), - ThisExpression(Box<'a, ThisExpression>), - UnaryExpression(Box<'a, UnaryExpression<'a>>), - UpdateExpression(Box<'a, UpdateExpression<'a>>), - YieldExpression(Box<'a, YieldExpression<'a>>), - PrivateInExpression(Box<'a, PrivateInExpression<'a>>), - - JSXElement(Box<'a, JSXElement<'a>>), - JSXFragment(Box<'a, JSXFragment<'a>>), - - TSAsExpression(Box<'a, TSAsExpression<'a>>), - TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>), - TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>), - TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>), - TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>), -} + BooleanLiteral(Box<'a, BooleanLiteral>) = 0, + NullLiteral(Box<'a, NullLiteral>) = 1, + NumericLiteral(Box<'a, NumericLiteral<'a>>) = 2, + BigintLiteral(Box<'a, BigIntLiteral<'a>>) = 3, + RegExpLiteral(Box<'a, RegExpLiteral<'a>>) = 4, + StringLiteral(Box<'a, StringLiteral<'a>>) = 5, + TemplateLiteral(Box<'a, TemplateLiteral<'a>>) = 6, + + Identifier(Box<'a, IdentifierReference<'a>>) = 7, + + MetaProperty(Box<'a, MetaProperty<'a>>) = 8, + Super(Box<'a, Super>) = 9, + + ArrayExpression(Box<'a, ArrayExpression<'a>>) = 10, + ArrowFunctionExpression(Box<'a, ArrowFunctionExpression<'a>>) = 11, + AssignmentExpression(Box<'a, AssignmentExpression<'a>>) = 12, + AwaitExpression(Box<'a, AwaitExpression<'a>>) = 13, + BinaryExpression(Box<'a, BinaryExpression<'a>>) = 14, + CallExpression(Box<'a, CallExpression<'a>>) = 15, + ChainExpression(Box<'a, ChainExpression<'a>>) = 16, + ClassExpression(Box<'a, Class<'a>>) = 17, + ConditionalExpression(Box<'a, ConditionalExpression<'a>>) = 18, + FunctionExpression(Box<'a, Function<'a>>) = 19, + ImportExpression(Box<'a, ImportExpression<'a>>) = 20, + LogicalExpression(Box<'a, LogicalExpression<'a>>) = 21, + NewExpression(Box<'a, NewExpression<'a>>) = 22, + ObjectExpression(Box<'a, ObjectExpression<'a>>) = 23, + ParenthesizedExpression(Box<'a, ParenthesizedExpression<'a>>) = 24, + SequenceExpression(Box<'a, SequenceExpression<'a>>) = 25, + TaggedTemplateExpression(Box<'a, TaggedTemplateExpression<'a>>) = 26, + ThisExpression(Box<'a, ThisExpression>) = 27, + UnaryExpression(Box<'a, UnaryExpression<'a>>) = 28, + UpdateExpression(Box<'a, UpdateExpression<'a>>) = 29, + YieldExpression(Box<'a, YieldExpression<'a>>) = 30, + PrivateInExpression(Box<'a, PrivateInExpression<'a>>) = 31, + + JSXElement(Box<'a, JSXElement<'a>>) = 32, + JSXFragment(Box<'a, JSXFragment<'a>>) = 33, + + TSAsExpression(Box<'a, TSAsExpression<'a>>) = 34, + TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 35, + TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 36, + TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 37, + TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>) = 38, + + // `MemberExpression` variants added here by `inherit_variants!` macro + @inherit MemberExpression +} +} + +/// Macro for matching `Expression`'s variants. +/// Includes `MemberExpression`'s variants. +#[macro_export] +macro_rules! match_expression { + ($ty:ident) => { + $ty::BooleanLiteral(_) + | $ty::NullLiteral(_) + | $ty::NumericLiteral(_) + | $ty::BigintLiteral(_) + | $ty::RegExpLiteral(_) + | $ty::StringLiteral(_) + | $ty::TemplateLiteral(_) + | $ty::Identifier(_) + | $ty::MetaProperty(_) + | $ty::Super(_) + | $ty::ArrayExpression(_) + | $ty::ArrowFunctionExpression(_) + | $ty::AssignmentExpression(_) + | $ty::AwaitExpression(_) + | $ty::BinaryExpression(_) + | $ty::CallExpression(_) + | $ty::ChainExpression(_) + | $ty::ClassExpression(_) + | $ty::ConditionalExpression(_) + | $ty::FunctionExpression(_) + | $ty::ImportExpression(_) + | $ty::LogicalExpression(_) + | $ty::NewExpression(_) + | $ty::ObjectExpression(_) + | $ty::ParenthesizedExpression(_) + | $ty::SequenceExpression(_) + | $ty::TaggedTemplateExpression(_) + | $ty::ThisExpression(_) + | $ty::UnaryExpression(_) + | $ty::UpdateExpression(_) + | $ty::YieldExpression(_) + | $ty::PrivateInExpression(_) + | $ty::JSXElement(_) + | $ty::JSXFragment(_) + | $ty::TSAsExpression(_) + | $ty::TSSatisfiesExpression(_) + | $ty::TSTypeAssertion(_) + | $ty::TSNonNullExpression(_) + | $ty::TSInstantiationExpression(_) + | $ty::ComputedMemberExpression(_) + | $ty::StaticMemberExpression(_) + | $ty::PrivateFieldExpression(_) + }; +} +pub use match_expression; impl<'a> Expression<'a> { pub fn is_typescript_syntax(&self) -> bool { @@ -233,9 +290,11 @@ impl<'a> Expression<'a> { pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool { match self.get_inner_expression() { - Expression::MemberExpression(expr) => expr.is_specific_member_access(object, property), + expr if expr.is_member_expression() => { + expr.to_member_expression().is_specific_member_access(object, property) + } Expression::ChainExpression(chain) => { - let ChainElement::MemberExpression(expr) = &chain.expression else { + let Some(expr) = chain.expression.as_member_expression() else { return false; }; expr.is_specific_member_access(object, property) @@ -304,12 +363,8 @@ impl<'a> Expression<'a> { pub fn get_member_expr(&self) -> Option<&MemberExpression<'a>> { match self.get_inner_expression() { - Expression::ChainExpression(chain_expr) => match &chain_expr.expression { - ChainElement::CallExpression(_) => None, - ChainElement::MemberExpression(member_expr) => Some(member_expr), - }, - Expression::MemberExpression(member_expr) => Some(member_expr), - _ => None, + Expression::ChainExpression(chain_expr) => chain_expr.expression.as_member_expression(), + expr => expr.as_member_expression(), } } @@ -433,16 +488,20 @@ pub struct ArrayExpression<'a> { pub trailing_comma: Option, } +inherit_variants! { /// Array Expression Element +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ArrayExpressionElement<'a> { - SpreadElement(Box<'a, SpreadElement<'a>>), - Expression(Expression<'a>), + SpreadElement(Box<'a, SpreadElement<'a>>) = 64, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression /// Array hole for sparse arrays /// - Elision(Elision), + Elision(Elision) = 65, +} } impl<'a> ArrayExpressionElement<'a> { @@ -500,34 +559,32 @@ pub struct ObjectProperty<'a> { pub computed: bool, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum PropertyKey<'a> { - Identifier(Box<'a, IdentifierName<'a>>), - PrivateIdentifier(Box<'a, PrivateIdentifier<'a>>), - Expression(Expression<'a>), + StaticIdentifier(Box<'a, IdentifierName<'a>>) = 64, + PrivateIdentifier(Box<'a, PrivateIdentifier<'a>>) = 65, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } impl<'a> PropertyKey<'a> { pub fn static_name(&self) -> Option { match self { - Self::Identifier(ident) => Some(ident.name.to_compact_str()), - Self::PrivateIdentifier(_) => None, - Self::Expression(expr) => match expr { - Expression::StringLiteral(lit) => Some(lit.value.to_compact_str()), - Expression::RegExpLiteral(lit) => Some(lit.regex.to_string().into()), - Expression::NumericLiteral(lit) => Some(lit.value.to_string().into()), - Expression::BigintLiteral(lit) => Some(lit.raw.to_compact_str()), - Expression::NullLiteral(_) => Some("null".into()), - Expression::TemplateLiteral(lit) => lit - .expressions - .is_empty() - .then(|| lit.quasi()) - .flatten() - .map(Atom::to_compact_str), - _ => None, - }, + Self::StaticIdentifier(ident) => Some(ident.name.to_compact_str()), + Self::StringLiteral(lit) => Some(lit.value.to_compact_str()), + Self::RegExpLiteral(lit) => Some(lit.regex.to_string().into()), + Self::NumericLiteral(lit) => Some(lit.value.to_string().into()), + Self::BigintLiteral(lit) => Some(lit.raw.to_compact_str()), + Self::NullLiteral(_) => Some("null".into()), + Self::TemplateLiteral(lit) => { + lit.expressions.is_empty().then(|| lit.quasi()).flatten().map(Atom::to_compact_str) + } + _ => None, } } @@ -536,7 +593,7 @@ impl<'a> PropertyKey<'a> { } pub fn is_identifier(&self) -> bool { - matches!(self, Self::PrivateIdentifier(_) | Self::Identifier(_)) + matches!(self, Self::PrivateIdentifier(_) | Self::StaticIdentifier(_)) } pub fn is_private_identifier(&self) -> bool { @@ -560,16 +617,13 @@ impl<'a> PropertyKey<'a> { pub fn is_specific_id(&self, name: &str) -> bool { match self { - PropertyKey::Identifier(ident) => ident.name == name, + PropertyKey::StaticIdentifier(ident) => ident.name == name, _ => false, } } pub fn is_specific_string_literal(&self, string: &str) -> bool { - match self { - PropertyKey::Expression(expr) => expr.is_specific_string_literal(string), - _ => false, - } + matches!(self, Self::StringLiteral(s) if s.value == string) } } @@ -643,17 +697,29 @@ pub struct TemplateElementValue<'a> { } /// +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum MemberExpression<'a> { /// `MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]` - ComputedMemberExpression(ComputedMemberExpression<'a>), + ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, /// `MemberExpression[?Yield, ?Await] . IdentifierName` - StaticMemberExpression(StaticMemberExpression<'a>), + StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` - PrivateFieldExpression(PrivateFieldExpression<'a>), + PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, +} + +/// Macro for matching `MemberExpression`'s variants. +#[macro_export] +macro_rules! match_member_expression { + ($ty:ident) => { + $ty::ComputedMemberExpression(_) + | $ty::StaticMemberExpression(_) + | $ty::PrivateFieldExpression(_) + }; } +pub use match_member_expression; impl<'a> MemberExpression<'a> { pub fn is_computed(&self) -> bool { @@ -718,8 +784,9 @@ impl<'a> MemberExpression<'a> { let object_matches = match self.object().without_parenthesized() { Expression::ChainExpression(x) => match &x.expression { ChainElement::CallExpression(_) => false, - ChainElement::MemberExpression(me) => { - me.object().without_parenthesized().is_specific_id(object) + match_member_expression!(ChainElement) => { + let member_expr = x.expression.to_member_expression(); + member_expr.object().without_parenthesized().is_specific_id(object) } }, x => x.is_specific_id(object), @@ -790,8 +857,7 @@ impl<'a> CallExpression<'a> { pub fn callee_name(&self) -> Option<&str> { match &self.callee { Expression::Identifier(ident) => Some(ident.name.as_str()), - Expression::MemberExpression(member) => member.static_property_name(), - _ => None, + expr => expr.as_member_expression().and_then(MemberExpression::static_property_name), } } @@ -803,9 +869,7 @@ impl<'a> CallExpression<'a> { id.name == "require" && matches!( self.arguments.first(), - Some(Argument::Expression( - Expression::StringLiteral(_) | Expression::TemplateLiteral(_), - )), + Some(Argument::StringLiteral(_) | Argument::TemplateLiteral(_)), ) } else { false @@ -816,11 +880,13 @@ impl<'a> CallExpression<'a> { // TODO: is 'Symbol' reference to global object match &self.callee { Expression::Identifier(id) => id.name == "Symbol", - Expression::MemberExpression(member) => { - matches!(member.object(), Expression::Identifier(id) if id.name == "Symbol") - && member.static_property_name() == Some("for") - } - _ => false, + expr => match expr.as_member_expression() { + Some(member) => { + matches!(member.object(), Expression::Identifier(id) if id.name == "Symbol") + && member.static_property_name() == Some("for") + } + None => false, + }, } } @@ -829,7 +895,7 @@ impl<'a> CallExpression<'a> { return None; } match &self.arguments[0] { - Argument::Expression(Expression::StringLiteral(str_literal)) => Some(str_literal), + Argument::StringLiteral(str_literal) => Some(str_literal), _ => None, } } @@ -868,13 +934,17 @@ pub struct SpreadElement<'a> { pub argument: Expression<'a>, } +inherit_variants! { /// Argument +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum Argument<'a> { - SpreadElement(Box<'a, SpreadElement<'a>>), - Expression(Expression<'a>), + SpreadElement(Box<'a, SpreadElement<'a>>) = 64, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } impl Argument<'_> { @@ -966,43 +1036,71 @@ pub struct AssignmentExpression<'a> { pub right: Expression<'a>, } +inherit_variants! { /// Destructuring Assignment +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum AssignmentTarget<'a> { - SimpleAssignmentTarget(SimpleAssignmentTarget<'a>), - AssignmentTargetPattern(AssignmentTargetPattern<'a>), + // `SimpleAssignmentTarget` variants added here by `inherit_variants!` macro + @inherit SimpleAssignmentTarget + // `AssignmentTargetPattern` variants added here by `inherit_variants!` macro + @inherit AssignmentTargetPattern +} } -impl<'a> AssignmentTarget<'a> { - pub fn is_simple(&self) -> bool { - matches!(self, Self::SimpleAssignmentTarget(_)) - } - - pub fn is_destructuring_pattern(&self) -> bool { - matches!(self, Self::AssignmentTargetPattern(_)) - } - - pub fn is_identifier(&self) -> bool { - matches!( - self, - Self::SimpleAssignmentTarget(SimpleAssignmentTarget::AssignmentTargetIdentifier(_)) - ) - } +/// Macro for matching `AssignmentTarget`'s variants. +/// Includes `SimpleAssignmentTarget`'s and `AssignmentTargetPattern`'s variants. +#[macro_export] +macro_rules! match_assignment_target { + ($ty:ident) => { + $ty::AssignmentTargetIdentifier(_) + | $ty::ComputedMemberExpression(_) + | $ty::StaticMemberExpression(_) + | $ty::PrivateFieldExpression(_) + | $ty::TSAsExpression(_) + | $ty::TSSatisfiesExpression(_) + | $ty::TSNonNullExpression(_) + | $ty::TSTypeAssertion(_) + | $ty::ArrayAssignmentTarget(_) + | $ty::ObjectAssignmentTarget(_) + }; } +pub use match_assignment_target; +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum SimpleAssignmentTarget<'a> { - AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>), - MemberAssignmentTarget(Box<'a, MemberExpression<'a>>), - TSAsExpression(Box<'a, TSAsExpression<'a>>), - TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>), - TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>), - TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>), -} + AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>) = 0, + // `MemberExpression` variants added here by `inherit_variants!` macro + @inherit MemberExpression + TSAsExpression(Box<'a, TSAsExpression<'a>>) = 1, + TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 2, + TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, + TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, +} +} + +/// Macro for matching `SimpleAssignmentTarget`'s variants. +/// Includes `MemberExpression`'s variants +#[macro_export] +macro_rules! match_simple_assignment_target { + ($ty:ident) => { + $ty::AssignmentTargetIdentifier(_) + | $ty::ComputedMemberExpression(_) + | $ty::StaticMemberExpression(_) + | $ty::PrivateFieldExpression(_) + | $ty::TSAsExpression(_) + | $ty::TSSatisfiesExpression(_) + | $ty::TSNonNullExpression(_) + | $ty::TSTypeAssertion(_) + }; +} +pub use match_simple_assignment_target; impl<'a> SimpleAssignmentTarget<'a> { pub fn get_expression(&self) -> Option<&Expression<'a>> { @@ -1016,13 +1114,23 @@ impl<'a> SimpleAssignmentTarget<'a> { } } +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum AssignmentTargetPattern<'a> { - ArrayAssignmentTarget(Box<'a, ArrayAssignmentTarget<'a>>), - ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>), + ArrayAssignmentTarget(Box<'a, ArrayAssignmentTarget<'a>>) = 8, + ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>) = 9, +} + +/// Macro for matching `AssignmentTargetPattern`'s variants. +#[macro_export] +macro_rules! match_assignment_target_pattern { + ($ty:ident) => { + $ty::ArrayAssignmentTarget(_) | $ty::ObjectAssignmentTarget(_) + }; } +pub use match_assignment_target_pattern; // See serializer in serialize.rs #[derive(Debug, Hash)] @@ -1094,28 +1202,30 @@ pub struct AssignmentTargetRest<'a> { pub target: AssignmentTarget<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum AssignmentTargetMaybeDefault<'a> { - AssignmentTarget(AssignmentTarget<'a>), - AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>), + // `AssignmentTarget` variants added here by `inherit_variants!` macro + @inherit AssignmentTarget + AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>) = 16, +} } impl<'a> AssignmentTargetMaybeDefault<'a> { pub fn name(&self) -> Option { - let target = match self { - Self::AssignmentTarget(target) => target, - Self::AssignmentTargetWithDefault(target) => &target.binding, - }; - - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(id), - ) = target - { - Some(id.name.clone()) - } else { - None + match self { + AssignmentTargetMaybeDefault::AssignmentTargetIdentifier(id) => Some(id.name.clone()), + Self::AssignmentTargetWithDefault(target) => { + if let AssignmentTarget::AssignmentTargetIdentifier(id) = &target.binding { + Some(id.name.clone()) + } else { + None + } + } + _ => None, } } } @@ -1197,12 +1307,16 @@ pub struct ChainExpression<'a> { pub expression: ChainElement<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ChainElement<'a> { - CallExpression(Box<'a, CallExpression<'a>>), - MemberExpression(Box<'a, MemberExpression<'a>>), + CallExpression(Box<'a, CallExpression<'a>>) = 0, + // `MemberExpression` variants added here by `inherit_variants!` macro + @inherit MemberExpression +} } /// Parenthesized Expression @@ -1215,33 +1329,37 @@ pub struct ParenthesizedExpression<'a> { pub expression: Expression<'a>, } +inherit_variants! { /// Statements +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum Statement<'a> { // Statements - BlockStatement(Box<'a, BlockStatement<'a>>), - BreakStatement(Box<'a, BreakStatement<'a>>), - ContinueStatement(Box<'a, ContinueStatement<'a>>), - DebuggerStatement(Box<'a, DebuggerStatement>), - DoWhileStatement(Box<'a, DoWhileStatement<'a>>), - EmptyStatement(Box<'a, EmptyStatement>), - ExpressionStatement(Box<'a, ExpressionStatement<'a>>), - ForInStatement(Box<'a, ForInStatement<'a>>), - ForOfStatement(Box<'a, ForOfStatement<'a>>), - ForStatement(Box<'a, ForStatement<'a>>), - IfStatement(Box<'a, IfStatement<'a>>), - LabeledStatement(Box<'a, LabeledStatement<'a>>), - ReturnStatement(Box<'a, ReturnStatement<'a>>), - SwitchStatement(Box<'a, SwitchStatement<'a>>), - ThrowStatement(Box<'a, ThrowStatement<'a>>), - TryStatement(Box<'a, TryStatement<'a>>), - WhileStatement(Box<'a, WhileStatement<'a>>), - WithStatement(Box<'a, WithStatement<'a>>), - - ModuleDeclaration(Box<'a, ModuleDeclaration<'a>>), - Declaration(Declaration<'a>), + BlockStatement(Box<'a, BlockStatement<'a>>) = 0, + BreakStatement(Box<'a, BreakStatement<'a>>) = 1, + ContinueStatement(Box<'a, ContinueStatement<'a>>) = 2, + DebuggerStatement(Box<'a, DebuggerStatement>) = 3, + DoWhileStatement(Box<'a, DoWhileStatement<'a>>) = 4, + EmptyStatement(Box<'a, EmptyStatement>) = 5, + ExpressionStatement(Box<'a, ExpressionStatement<'a>>) = 6, + ForInStatement(Box<'a, ForInStatement<'a>>) = 7, + ForOfStatement(Box<'a, ForOfStatement<'a>>) = 8, + ForStatement(Box<'a, ForStatement<'a>>) = 9, + IfStatement(Box<'a, IfStatement<'a>>) = 10, + LabeledStatement(Box<'a, LabeledStatement<'a>>) = 11, + ReturnStatement(Box<'a, ReturnStatement<'a>>) = 12, + SwitchStatement(Box<'a, SwitchStatement<'a>>) = 13, + ThrowStatement(Box<'a, ThrowStatement<'a>>) = 14, + TryStatement(Box<'a, TryStatement<'a>>) = 15, + WhileStatement(Box<'a, WhileStatement<'a>>) = 16, + WithStatement(Box<'a, WithStatement<'a>>) = 17, + // `Declaration` variants added here by `inherit_variants!` macro + @inherit Declaration + // `ModuleDeclaration` variants added here by `inherit_variants!` macro + @inherit ModuleDeclaration +} } impl<'a> Statement<'a> { @@ -1292,21 +1410,39 @@ pub struct BlockStatement<'a> { } /// Declarations and the Variable Statement +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum Declaration<'a> { - VariableDeclaration(Box<'a, VariableDeclaration<'a>>), - FunctionDeclaration(Box<'a, Function<'a>>), - ClassDeclaration(Box<'a, Class<'a>>), - UsingDeclaration(Box<'a, UsingDeclaration<'a>>), - - TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>), - TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>), - TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>), - TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>), - TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>), -} + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, + FunctionDeclaration(Box<'a, Function<'a>>) = 33, + ClassDeclaration(Box<'a, Class<'a>>) = 34, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 35, + + TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 36, + TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 37, + TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 38, + TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 39, + TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40, +} + +/// Macro for matching `Declaration`'s variants. +#[macro_export] +macro_rules! match_declaration { + ($ty:ident) => { + $ty::VariableDeclaration(_) + | $ty::FunctionDeclaration(_) + | $ty::ClassDeclaration(_) + | $ty::UsingDeclaration(_) + | $ty::TSTypeAliasDeclaration(_) + | $ty::TSInterfaceDeclaration(_) + | $ty::TSEnumDeclaration(_) + | $ty::TSModuleDeclaration(_) + | $ty::TSImportEqualsDeclaration(_) + }; +} +pub use match_declaration; impl<'a> Declaration<'a> { pub fn is_typescript_syntax(&self) -> bool { @@ -1493,13 +1629,17 @@ pub struct ForStatement<'a> { pub body: Statement<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ForStatementInit<'a> { - VariableDeclaration(Box<'a, VariableDeclaration<'a>>), - Expression(Expression<'a>), - UsingDeclaration(Box<'a, UsingDeclaration<'a>>), + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 64, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 65, +} } impl<'a> ForStatementInit<'a> { @@ -1508,13 +1648,6 @@ impl<'a> ForStatementInit<'a> { pub fn is_lexical_declaration(&self) -> bool { matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical()) } - - pub fn expression(&self) -> Option<&Expression<'a>> { - match self { - Self::Expression(e) => Some(e), - _ => None, - } - } } /// For-In Statement @@ -1542,13 +1675,17 @@ pub struct ForOfStatement<'a> { pub body: Statement<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ForStatementLeft<'a> { - VariableDeclaration(Box<'a, VariableDeclaration<'a>>), - AssignmentTarget(AssignmentTarget<'a>), - UsingDeclaration(Box<'a, UsingDeclaration<'a>>), + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 16, + // `AssignmentTarget` variants added here by `inherit_variants!` macro + @inherit AssignmentTarget + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 17, +} } impl<'a> ForStatementLeft<'a> { @@ -2290,27 +2427,42 @@ pub struct StaticBlock<'a> { pub body: Vec<'a, Statement<'a>>, } +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ModuleDeclaration<'a> { /// import hello from './world.js'; /// import * as t from './world.js'; - ImportDeclaration(Box<'a, ImportDeclaration<'a>>), + ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, /// export * as numbers from '../numbers.js' - ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>), + ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, /// export default 5; - ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>), + ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>) = 66, /// export {five} from './numbers.js'; /// export {six, seven}; - ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>), + ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>) = 67, /// export = 5; - TSExportAssignment(Box<'a, TSExportAssignment<'a>>), + TSExportAssignment(Box<'a, TSExportAssignment<'a>>) = 68, /// export as namespace React; - TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>), + TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>) = 69, } +/// Macro for matching `ModuleDeclaration`'s variants. +#[macro_export] +macro_rules! match_module_declaration { + ($ty:ident) => { + $ty::ImportDeclaration(_) + | $ty::ExportAllDeclaration(_) + | $ty::ExportDefaultDeclaration(_) + | $ty::ExportNamedDeclaration(_) + | $ty::TSExportAssignment(_) + | $ty::TSNamespaceExportDeclaration(_) + }; +} +pub use match_module_declaration; + impl<'a> ModuleDeclaration<'a> { pub fn is_import(&self) -> bool { matches!(self, Self::ImportDeclaration(_)) @@ -2552,27 +2704,31 @@ impl<'a> ExportSpecifier<'a> { } } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ExportDefaultDeclarationKind<'a> { - Expression(Expression<'a>), - FunctionDeclaration(Box<'a, Function<'a>>), - ClassDeclaration(Box<'a, Class<'a>>), + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression - TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>), - TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>), + FunctionDeclaration(Box<'a, Function<'a>>) = 64, + ClassDeclaration(Box<'a, Class<'a>>) = 65, + + TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 66, + TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 67, +} } impl<'a> ExportDefaultDeclarationKind<'a> { #[inline] pub fn is_typescript_syntax(&self) -> bool { match self { - ExportDefaultDeclarationKind::FunctionDeclaration(func) => func.is_typescript_syntax(), - ExportDefaultDeclarationKind::ClassDeclaration(class) => class.is_typescript_syntax(), - ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) - | ExportDefaultDeclarationKind::TSEnumDeclaration(_) => true, - ExportDefaultDeclarationKind::Expression(_) => false, + Self::FunctionDeclaration(func) => func.is_typescript_syntax(), + Self::ClassDeclaration(class) => class.is_typescript_syntax(), + Self::TSInterfaceDeclaration(_) | Self::TSEnumDeclaration(_) => true, + _ => false, } } } diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index 96eac3f4cd04e..423525bf6f1ba 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -12,6 +12,8 @@ use tsify::Tsify; use super::{js::*, literal::*, ts::*}; +use super::inherit_variants; + // 1.2 JSX Elements /// JSX Element @@ -144,12 +146,23 @@ pub struct JSXExpressionContainer<'a> { pub expression: JSXExpression<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum JSXExpression<'a> { - Expression(Expression<'a>), - EmptyExpression(JSXEmptyExpression), + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression + EmptyExpression(JSXEmptyExpression) = 64, +} +} + +impl<'a> JSXExpression<'a> { + /// Determines whether the given expr is a `undefined` literal + pub fn is_undefined(&self) -> bool { + matches!(self, Self::Identifier(ident) if ident.name == "undefined") + } } #[derive(Debug, Hash)] diff --git a/crates/oxc_ast/src/ast/macros.rs b/crates/oxc_ast/src/ast/macros.rs new file mode 100644 index 0000000000000..c356bd950d6a4 --- /dev/null +++ b/crates/oxc_ast/src/ast/macros.rs @@ -0,0 +1,823 @@ +/// Macro to inherit enum variants from another enum. +/// +/// The following types' variants can be inherited: +/// * `Expression` +/// * `MemberExpression` +/// * `AssignmentTarget` +/// * `SimpleAssignmentTarget` +/// * `AssignmentTargetPattern` +/// * `Declaration` +/// * `ModuleDeclaration` +/// * `TSType` +/// * `TSTypeName` +/// +/// ``` +/// inherit_variants! { +/// #[repr(C, u8)] +/// enum Statement<'a> { +/// pub enum Statement<'a> { +/// BlockStatement(Box<'a, BlockStatement<'a>>) = 0, +/// BreakStatement(Box<'a, BreakStatement<'a>>) = 1, +/// @inherit Declaration +/// @inherit ModuleDeclaration +/// } +/// } +/// } +/// ``` +/// +/// expands to: +/// +/// ``` +/// #[repr(C, u8)] +/// enum Statement<'a> { +/// pub enum Statement<'a> { +/// BlockStatement(Box<'a, BlockStatement<'a>>) = 0, +/// BreakStatement(Box<'a, BreakStatement<'a>>) = 1, +/// +/// // Inherited from `Declaration` +/// VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, +/// FunctionDeclaration(Box<'a, Function<'a>>) = 33, +/// // ...and many more +/// +/// // Inherited from `ModuleDeclaration` +/// ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, +/// ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, +/// // ...and many more +/// } +/// } +/// +/// shared_enum_variants!( +/// Statement, Declaration, +/// is_declaration, +/// as_declaration, as_declaration_mut, +/// to_declaration, to_declaration_mut, +/// [VariableDeclaration, FunctionDeclaration, ...more] +/// ) +/// +/// shared_enum_variants!( +/// Statement, ModuleDeclaration, +/// is_module_declaration, +/// as_module_declaration, as_module_declaration_mut, +/// to_module_declaration, to_module_declaration_mut, +/// [ImportDeclaration, ExportAllDeclaration, ...more] +/// ) +/// ``` +macro_rules! inherit_variants { + // Inherit `Expression`'s variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit Expression + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + // `Expression`'s own variants + + BooleanLiteral(Box<'a, BooleanLiteral>) = 0, + NullLiteral(Box<'a, NullLiteral>) = 1, + NumericLiteral(Box<'a, NumericLiteral<'a>>) = 2, + BigintLiteral(Box<'a, BigIntLiteral<'a>>) = 3, + RegExpLiteral(Box<'a, RegExpLiteral<'a>>) = 4, + StringLiteral(Box<'a, StringLiteral<'a>>) = 5, + TemplateLiteral(Box<'a, TemplateLiteral<'a>>) = 6, + + Identifier(Box<'a, IdentifierReference<'a>>) = 7, + + MetaProperty(Box<'a, MetaProperty<'a>>) = 8, + Super(Box<'a, Super>) = 9, + + ArrayExpression(Box<'a, ArrayExpression<'a>>) = 10, + ArrowFunctionExpression(Box<'a, ArrowFunctionExpression<'a>>) = 11, + AssignmentExpression(Box<'a, AssignmentExpression<'a>>) = 12, + AwaitExpression(Box<'a, AwaitExpression<'a>>) = 13, + BinaryExpression(Box<'a, BinaryExpression<'a>>) = 14, + CallExpression(Box<'a, CallExpression<'a>>) = 15, + ChainExpression(Box<'a, ChainExpression<'a>>) = 16, + ClassExpression(Box<'a, Class<'a>>) = 17, + ConditionalExpression(Box<'a, ConditionalExpression<'a>>) = 18, + FunctionExpression(Box<'a, Function<'a>>) = 19, + ImportExpression(Box<'a, ImportExpression<'a>>) = 20, + LogicalExpression(Box<'a, LogicalExpression<'a>>) = 21, + NewExpression(Box<'a, NewExpression<'a>>) = 22, + ObjectExpression(Box<'a, ObjectExpression<'a>>) = 23, + ParenthesizedExpression(Box<'a, ParenthesizedExpression<'a>>) = 24, + SequenceExpression(Box<'a, SequenceExpression<'a>>) = 25, + TaggedTemplateExpression(Box<'a, TaggedTemplateExpression<'a>>) = 26, + ThisExpression(Box<'a, ThisExpression>) = 27, + UnaryExpression(Box<'a, UnaryExpression<'a>>) = 28, + UpdateExpression(Box<'a, UpdateExpression<'a>>) = 29, + YieldExpression(Box<'a, YieldExpression<'a>>) = 30, + PrivateInExpression(Box<'a, PrivateInExpression<'a>>) = 31, + + JSXElement(Box<'a, JSXElement<'a>>) = 32, + JSXFragment(Box<'a, JSXFragment<'a>>) = 33, + + TSAsExpression(Box<'a, TSAsExpression<'a>>) = 34, + TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 35, + TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 36, + TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 37, + TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>) = 38, + + // Inherited from `MemberExpression` + @inherit MemberExpression + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + Expression, + is_expression, + as_expression, + as_expression_mut, + to_expression, + to_expression_mut, + [ + BooleanLiteral, + NullLiteral, + NumericLiteral, + BigintLiteral, + RegExpLiteral, + StringLiteral, + TemplateLiteral, + Identifier, + MetaProperty, + Super, + ArrayExpression, + ArrowFunctionExpression, + AssignmentExpression, + AwaitExpression, + BinaryExpression, + CallExpression, + ChainExpression, + ClassExpression, + ConditionalExpression, + FunctionExpression, + ImportExpression, + LogicalExpression, + NewExpression, + ObjectExpression, + ParenthesizedExpression, + SequenceExpression, + TaggedTemplateExpression, + ThisExpression, + UnaryExpression, + UpdateExpression, + YieldExpression, + PrivateInExpression, + JSXElement, + JSXFragment, + TSAsExpression, + TSSatisfiesExpression, + TSTypeAssertion, + TSNonNullExpression, + TSInstantiationExpression, + ComputedMemberExpression, + StaticMemberExpression, + PrivateFieldExpression, + ] + ); + }; + + // Inherit `MemberExpression`'s variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit MemberExpression + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + /// `MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]` + ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, + /// `MemberExpression[?Yield, ?Await] . IdentifierName` + StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, + /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` + PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + MemberExpression, + is_member_expression, + as_member_expression, + as_member_expression_mut, + to_member_expression, + to_member_expression_mut, + [ComputedMemberExpression, StaticMemberExpression, PrivateFieldExpression] + ); + }; + + // Inherit `AssignmentTarget` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit AssignmentTarget + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + @inherit SimpleAssignmentTarget + @inherit AssignmentTargetPattern + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + AssignmentTarget, + is_assignment_target, + as_assignment_target, + as_assignment_target_mut, + to_assignment_target, + to_assignment_target_mut, + [ + AssignmentTargetIdentifier, + ComputedMemberExpression, + StaticMemberExpression, + PrivateFieldExpression, + TSAsExpression, + TSSatisfiesExpression, + TSNonNullExpression, + TSTypeAssertion, + ArrayAssignmentTarget, + ObjectAssignmentTarget, + ] + ); + }; + + // Inherit `SimpleAssignmentTarget` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit SimpleAssignmentTarget + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>) = 0, + + // Inherited from `MemberExpression` + @inherit MemberExpression + + TSAsExpression(Box<'a, TSAsExpression<'a>>) = 1, + TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 2, + TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, + TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + SimpleAssignmentTarget, + is_simple_assignment_target, + as_simple_assignment_target, + as_simple_assignment_target_mut, + to_simple_assignment_target, + to_simple_assignment_target_mut, + [ + AssignmentTargetIdentifier, + ComputedMemberExpression, + StaticMemberExpression, + PrivateFieldExpression, + TSAsExpression, + TSSatisfiesExpression, + TSNonNullExpression, + TSTypeAssertion + ] + ); + }; + + // Inherit `AssignmentTargetPattern` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit AssignmentTargetPattern + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + ArrayAssignmentTarget(Box<'a, ArrayAssignmentTarget<'a>>) = 8, + ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>) = 9, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + AssignmentTargetPattern, + is_assignment_target_pattern, + as_assignment_target_pattern, + as_assignment_target_pattern_mut, + to_assignment_target_pattern, + to_assignment_target_pattern_mut, + [ArrayAssignmentTarget, ObjectAssignmentTarget] + ); + }; + + // Inherit `Declaration` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit Declaration + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, + FunctionDeclaration(Box<'a, Function<'a>>) = 33, + ClassDeclaration(Box<'a, Class<'a>>) = 34, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 35, + + TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 36, + TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 37, + TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 38, + TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 39, + TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + Declaration, + is_declaration, + as_declaration, + as_declaration_mut, + to_declaration, + to_declaration_mut, + [ + VariableDeclaration, + FunctionDeclaration, + ClassDeclaration, + UsingDeclaration, + TSTypeAliasDeclaration, + TSInterfaceDeclaration, + TSEnumDeclaration, + TSModuleDeclaration, + TSImportEqualsDeclaration, + ] + ); + }; + + // Inherit `ModuleDeclaration` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit ModuleDeclaration + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + /// import hello from './world.js'; + /// import * as t from './world.js'; + ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, + /// export * as numbers from '../numbers.js' + ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, + /// export default 5; + ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>) = 66, + /// export {five} from './numbers.js'; + /// export {six, seven}; + ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>) = 67, + + /// export = 5; + TSExportAssignment(Box<'a, TSExportAssignment<'a>>) = 68, + /// export as namespace React; + TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>) = 69, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + ModuleDeclaration, + is_module_declaration, + as_module_declaration, + as_module_declaration_mut, + to_module_declaration, + to_module_declaration_mut, + [ + ImportDeclaration, + ExportAllDeclaration, + ExportDefaultDeclaration, + ExportNamedDeclaration, + TSExportAssignment, + TSNamespaceExportDeclaration, + ] + ); + }; + + // Inherit `TSType` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit TSType + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + // Keyword + TSAnyKeyword(Box<'a, TSAnyKeyword>) = 0, + TSBigIntKeyword(Box<'a, TSBigIntKeyword>) = 1, + TSBooleanKeyword(Box<'a, TSBooleanKeyword>) = 2, + TSNeverKeyword(Box<'a, TSNeverKeyword>) = 3, + TSNullKeyword(Box<'a, TSNullKeyword>) = 4, + TSNumberKeyword(Box<'a, TSNumberKeyword>) = 5, + TSObjectKeyword(Box<'a, TSObjectKeyword>) = 6, + TSStringKeyword(Box<'a, TSStringKeyword>) = 7, + TSSymbolKeyword(Box<'a, TSSymbolKeyword>) = 8, + TSThisType(Box<'a, TSThisType>) = 9, + TSUndefinedKeyword(Box<'a, TSUndefinedKeyword>) = 10, + TSUnknownKeyword(Box<'a, TSUnknownKeyword>) = 11, + TSVoidKeyword(Box<'a, TSVoidKeyword>) = 12, + // Compound + TSArrayType(Box<'a, TSArrayType<'a>>) = 13, + TSConditionalType(Box<'a, TSConditionalType<'a>>) = 14, + TSConstructorType(Box<'a, TSConstructorType<'a>>) = 15, + TSFunctionType(Box<'a, TSFunctionType<'a>>) = 16, + TSImportType(Box<'a, TSImportType<'a>>) = 17, + TSIndexedAccessType(Box<'a, TSIndexedAccessType<'a>>) = 18, + TSInferType(Box<'a, TSInferType<'a>>) = 19, + TSIntersectionType(Box<'a, TSIntersectionType<'a>>) = 20, + TSLiteralType(Box<'a, TSLiteralType<'a>>) = 21, + TSMappedType(Box<'a, TSMappedType<'a>>) = 22, + TSNamedTupleMember(Box<'a, TSNamedTupleMember<'a>>) = 23, + TSQualifiedName(Box<'a, TSQualifiedName<'a>>) = 24, + TSTemplateLiteralType(Box<'a, TSTemplateLiteralType<'a>>) = 25, + TSTupleType(Box<'a, TSTupleType<'a>>) = 26, + TSTypeLiteral(Box<'a, TSTypeLiteral<'a>>) = 27, + TSTypeOperatorType(Box<'a, TSTypeOperator<'a>>) = 28, + TSTypePredicate(Box<'a, TSTypePredicate<'a>>) = 29, + TSTypeQuery(Box<'a, TSTypeQuery<'a>>) = 30, + TSTypeReference(Box<'a, TSTypeReference<'a>>) = 31, + TSUnionType(Box<'a, TSUnionType<'a>>) = 32, + // JSDoc + JSDocNullableType(Box<'a, JSDocNullableType<'a>>) = 33, + JSDocUnknownType(Box<'a, JSDocUnknownType>) = 34, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + TSType, + is_ts_type, + as_ts_type, + as_ts_type_mut, + to_ts_type, + to_ts_type_mut, + [ + TSAnyKeyword, + TSBigIntKeyword, + TSBooleanKeyword, + TSNeverKeyword, + TSNullKeyword, + TSNumberKeyword, + TSObjectKeyword, + TSStringKeyword, + TSSymbolKeyword, + TSThisType, + TSUndefinedKeyword, + TSUnknownKeyword, + TSVoidKeyword, + TSArrayType, + TSConditionalType, + TSConstructorType, + TSFunctionType, + TSImportType, + TSIndexedAccessType, + TSInferType, + TSIntersectionType, + TSLiteralType, + TSMappedType, + TSNamedTupleMember, + TSQualifiedName, + TSTemplateLiteralType, + TSTupleType, + TSTypeLiteral, + TSTypeOperatorType, + TSTypePredicate, + TSTypeQuery, + TSTypeReference, + TSUnionType, + JSDocNullableType, + JSDocUnknownType, + ] + ); + }; + + // Inherit `TSTypeName` variants + ( + $(#[$attr:meta])* + pub enum $ty:ident<'a> { + $($(#[$variant_attr:meta])* $variant_name:ident($variant_type:ty) = $variant_discrim:literal,)* + @inherit TSTypeName + $($rest:tt)* + } + ) => { + $crate::ast::macros::inherit_variants! { + $(#[$attr])* + pub enum $ty<'a> { + $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + + IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0, + QualifiedName(Box<'a, TSQualifiedName<'a>>) = 1, + + $($rest)* + } + } + + $crate::ast::macros::shared_enum_variants!( + $ty, + TSTypeName, + is_ts_type_name, + as_ts_type_name, + as_ts_type_name_mut, + to_ts_type_name, + to_ts_type_name_mut, + [IdentifierReference, QualifiedName] + ); + }; + + // Passthrough - no further inheritance to handle + ($($rest:tt)*) => {$($rest)*}; +} +pub(crate) use inherit_variants; + +/// Macro to allow conversion between 2 enum types where they share some of the same variants. +/// "Parent" enum contains all the "child"'s variants, plus parent contains further other variants. +/// e.g. `Statement` and `Declaration`. +/// +/// The discriminants and types of the shared variants must be identical between the 2 enums. +/// All variants must have a `Box<_>` payload. +/// Equality of types is guaranteed by `From` and `TryFrom` impls this macro creates. +/// These will fail to compile if the types differ for any variant. +/// Equality of discriminants is checked with a compile-time assertion. +/// +/// # SAFETY +/// Both enums must be `#[repr(C, u8)]` or using this macro is unsound. +/// +/// # Example +/// NB: For illustration only - `Statement` and `Declaration` in reality share 9 variants, not 2. +/// +/// ``` +/// shared_enum_variants!( +/// Statement, Declaration, +/// is_declaration, +/// as_declaration, as_declaration_mut, +/// to_declaration, to_declaration_mut, +/// [VariableDeclaration, FunctionDeclaration] +/// ) +/// ``` +/// +/// expands to: +/// +/// ``` +/// const _: () = { +/// assert!(discriminant!(Statement::VariableDeclaration) == discriminant!(Declaration::VariableDeclaration)); +/// assert!(discriminant!(Statement::FunctionDeclaration) == discriminant!(Declaration::FunctionDeclaration)); +/// }; +/// +/// impl<'a> Statement<'a> { +/// /// Return if a `Statement` is a `Declaration`. +/// #[inline] +/// pub fn is_declaration(&self) -> bool { +/// match self { +/// Self::VariableDeclaration(_) | Self::FunctionDeclaration(_) => true, +/// _ => false, +/// } +/// } +/// +/// /// Convert `&Statement` to `&Declaration`. +/// #[inline] +/// pub fn as_declaration(&self) -> Option<&Declaration<'a>> { +/// if self.is_declaration() { +/// Some(unsafe { &*(self as *const _ as *const Declaration) }) +/// } else { +/// None +/// } +/// } +/// +/// /// Convert `&mut Statement` to `&mut Declaration`. +/// #[inline] +/// pub fn as_declaration_mut(&mut self) -> Option<&mut Declaration<'a>> { +/// if self.is_declaration() { +/// Some(unsafe { &mut *(self as *mut _ as *mut Declaration) }) +/// } else { +/// None +/// } +/// } +/// +/// /// Convert `&Statement` to `&Declaration`. +/// /// # Panic +/// /// Panics if not convertible. +/// #[inline] +/// pub fn to_declaration(&self) -> &Declaration<'a> { +/// self.as_declaration().unwrap() +/// } +/// +/// /// Convert `&mut Statement` to `&mut Declaration`. +/// /// # Panic +/// /// Panics if not convertible. +/// #[inline] +/// pub fn to_declaration_mut(&mut self) -> Option<&mut Declaration<'a>> { +/// self.as_declaration_mut().unwrap() +/// } +/// } +/// +/// impl<'a> TryFrom> for Declaration<'a> { +/// type Error = (); +/// +/// /// "Convert `Statement` to `Declaration`. +/// #[inline] +/// fn try_from(value: Statement<'a>) -> Result { +/// match value { +/// Statement::VariableDeclaration(o) => Ok(Declaration::VariableDeclaration(o)), +/// Statement::FunctionDeclaration(o) => Ok(Declaration::FunctionDeclaration(o)), +/// _ => Err(()), +/// } +/// } +/// } +/// +/// impl<'a> From> for Statement<'a> { +/// /// Convert `Declaration` to `Statement`. +/// #[inline] +/// fn from(value: Declaration<'a>) -> Self { +/// match value { +/// Declaration::VariableDeclaration(o) => Statement::VariableDeclaration(o), +/// Declaration::FunctionDeclaration(o) => Statement::FunctionDeclaration(o), +/// } +/// } +/// } +/// ``` +macro_rules! shared_enum_variants { + ( + $parent:ident, $child:ident, + $is_child:ident, + $as_child:ident, $as_child_mut:ident, + $to_child:ident, $to_child_mut:ident, + [$($variant:ident),+ $(,)?] + ) => { + // Ensure discriminants match for all variants between parent and child types + const _: () = { + $( + assert!( + $crate::ast::macros::discriminant!($parent::$variant) + == $crate::ast::macros::discriminant!($child::$variant), + concat!( + "Non-matching discriminants for `", stringify!($variant), + "` between `", stringify!($parent), "` and `", stringify!($child), "`" + ) + ); + )+ + }; + + impl<'a> $parent<'a> { + #[doc = concat!(" Return if a `", stringify!($parent), "` is a `", stringify!($child), "`.")] + #[inline] + pub fn $is_child(&self) -> bool { + matches!( + self, + $(Self::$variant(_))|+ + ) + } + + #[doc = concat!(" Convert `&", stringify!($parent), "` to `&", stringify!($child), "`.")] + #[inline] + pub fn $as_child(&self) -> Option<&$child<'a>> { + if self.$is_child() { + #[allow(unsafe_code, clippy::ptr_as_ptr)] + // SAFETY: Transmute is safe because discriminants + types are identical between + // `$parent` and `$child` for $child variants + Some(unsafe { &*(self as *const _ as *const $child) }) + } else { + None + } + } + + #[doc = concat!(" Convert `&mut ", stringify!($parent), "` to `&mut ", stringify!($child), "`.")] + #[inline] + pub fn $as_child_mut(&mut self) -> Option<&mut $child<'a>> { + if self.$is_child() { + #[allow(unsafe_code, clippy::ptr_as_ptr)] + // SAFETY: Transmute is safe because discriminants + types are identical between + // `$parent` and `$child` for $child variants + Some(unsafe { &mut *(self as *mut _ as *mut $child) }) + } else { + None + } + } + + #[doc = concat!(" Convert `&", stringify!($parent), "` to `&", stringify!($child), "`.")] + #[doc = "# Panic"] + #[doc = "Panics if not convertible."] + #[inline] + pub fn $to_child(&self) -> &$child<'a> { + self.$as_child().unwrap() + } + + #[doc = concat!(" Convert `&mut ", stringify!($parent), "` to `&mut ", stringify!($child), "`.")] + #[doc = "# Panic"] + #[doc = "Panics if not convertible."] + #[inline] + pub fn $to_child_mut(&mut self) -> &mut $child<'a> { + self.$as_child_mut().unwrap() + } + } + + impl<'a> TryFrom<$parent<'a>> for $child<'a> { + type Error = (); + + #[doc = concat!(" Convert `", stringify!($parent), "` to `", stringify!($child), "`.")] + #[inline] + fn try_from(value: $parent<'a>) -> Result { + // Compiler should implement this as a check of discriminant and then zero-cost transmute, + // as discriminants for `$parent` and `$child` are aligned + match value { + $($parent::$variant(o) => Ok($child::$variant(o)),)+ + _ => Err(()) + } + } + } + + impl<'a> From<$child<'a>> for $parent<'a> { + #[doc = concat!(" Convert `", stringify!($child), "` to `", stringify!($parent), "`.")] + #[inline] + fn from(value: $child<'a>) -> Self { + // Compiler should implement this as zero-cost transmute as discriminants + // for `$child` and `$parent` are aligned + match value { + $($child::$variant(o) => $parent::$variant(o),)+ + } + } + } + } +} +pub(crate) use shared_enum_variants; + +/// Macro to get discriminant of an enum. +/// # SAFETY +/// Enum must be `#[repr(C, u8)]` or using this macro is unsound. +/// +macro_rules! discriminant { + ($ty:ident :: $variant:ident) => {{ + #[allow(unsafe_code, clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks)] + unsafe { + let t = std::mem::ManuallyDrop::new($ty::$variant(oxc_allocator::Box::dangling())); + *(&t as *const _ as *const u8) + } + }}; +} +pub(crate) use discriminant; diff --git a/crates/oxc_ast/src/ast/mod.rs b/crates/oxc_ast/src/ast/mod.rs index 04cf0bc403abb..e0954106c446a 100644 --- a/crates/oxc_ast/src/ast/mod.rs +++ b/crates/oxc_ast/src/ast/mod.rs @@ -3,6 +3,8 @@ mod js; mod jsx; mod literal; +mod macros; mod ts; pub use self::{js::*, jsx::*, literal::*, ts::*}; +use macros::inherit_variants; diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 1ab5f18770d19..a42e38a0b865a 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -13,7 +13,7 @@ use serde::Serialize; #[cfg(feature = "serialize")] use tsify::Tsify; -use super::{js::*, literal::*}; +use super::{inherit_variants, js::*, jsx::*, literal::*}; #[cfg(feature = "serialize")] #[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)] @@ -60,16 +60,20 @@ pub struct TSEnumMember<'a> { pub initializer: Option>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum TSEnumMemberName<'a> { - Identifier(Box<'a, IdentifierName<'a>>), - StringLiteral(Box<'a, StringLiteral<'a>>), - // Invalid Grammar `enum E { [computed] }` - ComputedPropertyName(Expression<'a>), + StaticIdentifier(Box<'a, IdentifierName<'a>>) = 64, + StaticStringLiteral(Box<'a, StringLiteral<'a>>) = 65, // Invalid Grammar `enum E { 1 }` - NumericLiteral(Box<'a, NumericLiteral<'a>>), + StaticNumericLiteral(Box<'a, NumericLiteral<'a>>) = 66, + // Invalid Grammar `enum E { [computed] }` + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } #[derive(Debug, Hash)] @@ -104,49 +108,93 @@ pub enum TSLiteral<'a> { UnaryExpression(Box<'a, UnaryExpression<'a>>), } +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))] pub enum TSType<'a> { // Keyword - TSAnyKeyword(Box<'a, TSAnyKeyword>), - TSBigIntKeyword(Box<'a, TSBigIntKeyword>), - TSBooleanKeyword(Box<'a, TSBooleanKeyword>), - TSNeverKeyword(Box<'a, TSNeverKeyword>), - TSNullKeyword(Box<'a, TSNullKeyword>), - TSNumberKeyword(Box<'a, TSNumberKeyword>), - TSObjectKeyword(Box<'a, TSObjectKeyword>), - TSStringKeyword(Box<'a, TSStringKeyword>), - TSSymbolKeyword(Box<'a, TSSymbolKeyword>), - TSThisType(Box<'a, TSThisType>), - TSUndefinedKeyword(Box<'a, TSUndefinedKeyword>), - TSUnknownKeyword(Box<'a, TSUnknownKeyword>), - TSVoidKeyword(Box<'a, TSVoidKeyword>), + TSAnyKeyword(Box<'a, TSAnyKeyword>) = 0, + TSBigIntKeyword(Box<'a, TSBigIntKeyword>) = 1, + TSBooleanKeyword(Box<'a, TSBooleanKeyword>) = 2, + TSNeverKeyword(Box<'a, TSNeverKeyword>) = 3, + TSNullKeyword(Box<'a, TSNullKeyword>) = 4, + TSNumberKeyword(Box<'a, TSNumberKeyword>) = 5, + TSObjectKeyword(Box<'a, TSObjectKeyword>) = 6, + TSStringKeyword(Box<'a, TSStringKeyword>) = 7, + TSSymbolKeyword(Box<'a, TSSymbolKeyword>) = 8, + TSThisType(Box<'a, TSThisType>) = 9, + TSUndefinedKeyword(Box<'a, TSUndefinedKeyword>) = 10, + TSUnknownKeyword(Box<'a, TSUnknownKeyword>) = 11, + TSVoidKeyword(Box<'a, TSVoidKeyword>) = 12, // Compound - TSArrayType(Box<'a, TSArrayType<'a>>), - TSConditionalType(Box<'a, TSConditionalType<'a>>), - TSConstructorType(Box<'a, TSConstructorType<'a>>), - TSFunctionType(Box<'a, TSFunctionType<'a>>), - TSImportType(Box<'a, TSImportType<'a>>), - TSIndexedAccessType(Box<'a, TSIndexedAccessType<'a>>), - TSInferType(Box<'a, TSInferType<'a>>), - TSIntersectionType(Box<'a, TSIntersectionType<'a>>), - TSLiteralType(Box<'a, TSLiteralType<'a>>), - TSMappedType(Box<'a, TSMappedType<'a>>), - TSNamedTupleMember(Box<'a, TSNamedTupleMember<'a>>), - TSQualifiedName(Box<'a, TSQualifiedName<'a>>), - TSTemplateLiteralType(Box<'a, TSTemplateLiteralType<'a>>), - TSTupleType(Box<'a, TSTupleType<'a>>), - TSTypeLiteral(Box<'a, TSTypeLiteral<'a>>), - TSTypeOperatorType(Box<'a, TSTypeOperator<'a>>), - TSTypePredicate(Box<'a, TSTypePredicate<'a>>), - TSTypeQuery(Box<'a, TSTypeQuery<'a>>), - TSTypeReference(Box<'a, TSTypeReference<'a>>), - TSUnionType(Box<'a, TSUnionType<'a>>), + TSArrayType(Box<'a, TSArrayType<'a>>) = 13, + TSConditionalType(Box<'a, TSConditionalType<'a>>) = 14, + TSConstructorType(Box<'a, TSConstructorType<'a>>) = 15, + TSFunctionType(Box<'a, TSFunctionType<'a>>) = 16, + TSImportType(Box<'a, TSImportType<'a>>) = 17, + TSIndexedAccessType(Box<'a, TSIndexedAccessType<'a>>) = 18, + TSInferType(Box<'a, TSInferType<'a>>) = 19, + TSIntersectionType(Box<'a, TSIntersectionType<'a>>) = 20, + TSLiteralType(Box<'a, TSLiteralType<'a>>) = 21, + TSMappedType(Box<'a, TSMappedType<'a>>) = 22, + TSNamedTupleMember(Box<'a, TSNamedTupleMember<'a>>) = 23, + TSQualifiedName(Box<'a, TSQualifiedName<'a>>) = 24, + TSTemplateLiteralType(Box<'a, TSTemplateLiteralType<'a>>) = 25, + TSTupleType(Box<'a, TSTupleType<'a>>) = 26, + TSTypeLiteral(Box<'a, TSTypeLiteral<'a>>) = 27, + TSTypeOperatorType(Box<'a, TSTypeOperator<'a>>) = 28, + TSTypePredicate(Box<'a, TSTypePredicate<'a>>) = 29, + TSTypeQuery(Box<'a, TSTypeQuery<'a>>) = 30, + TSTypeReference(Box<'a, TSTypeReference<'a>>) = 31, + TSUnionType(Box<'a, TSUnionType<'a>>) = 32, // JSDoc - JSDocNullableType(Box<'a, JSDocNullableType<'a>>), - JSDocUnknownType(Box<'a, JSDocUnknownType>), -} + JSDocNullableType(Box<'a, JSDocNullableType<'a>>) = 33, + JSDocUnknownType(Box<'a, JSDocUnknownType>) = 34, +} + +/// Macro for matching `TSType`'s variants. +#[macro_export] +macro_rules! match_ts_type { + ($ty:ident) => { + $ty::TSAnyKeyword(_) + | $ty::TSBigIntKeyword(_) + | $ty::TSBooleanKeyword(_) + | $ty::TSNeverKeyword(_) + | $ty::TSNullKeyword(_) + | $ty::TSNumberKeyword(_) + | $ty::TSObjectKeyword(_) + | $ty::TSStringKeyword(_) + | $ty::TSSymbolKeyword(_) + | $ty::TSThisType(_) + | $ty::TSUndefinedKeyword(_) + | $ty::TSUnknownKeyword(_) + | $ty::TSVoidKeyword(_) + | $ty::TSArrayType(_) + | $ty::TSConditionalType(_) + | $ty::TSConstructorType(_) + | $ty::TSFunctionType(_) + | $ty::TSImportType(_) + | $ty::TSIndexedAccessType(_) + | $ty::TSInferType(_) + | $ty::TSIntersectionType(_) + | $ty::TSLiteralType(_) + | $ty::TSMappedType(_) + | $ty::TSNamedTupleMember(_) + | $ty::TSQualifiedName(_) + | $ty::TSTemplateLiteralType(_) + | $ty::TSTupleType(_) + | $ty::TSTypeLiteral(_) + | $ty::TSTypeOperatorType(_) + | $ty::TSTypePredicate(_) + | $ty::TSTypeQuery(_) + | $ty::TSTypeReference(_) + | $ty::TSUnionType(_) + | $ty::JSDocNullableType(_) + | $ty::JSDocUnknownType(_) + }; +} +pub use match_ts_type; impl<'a> TSType<'a> { pub fn is_const_type_reference(&self) -> bool { @@ -292,13 +340,19 @@ pub struct TSRestType<'a> { pub type_annotation: TSType<'a>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))] pub enum TSTupleElement<'a> { - TSType(TSType<'a>), - TSOptionalType(Box<'a, TSOptionalType<'a>>), - TSRestType(Box<'a, TSRestType<'a>>), + // `TSType` variants added here by `inherit_variants!` macro + @inherit TSType + // Discriminants start at 64, so that `TSTupleElement::is_ts_type` is a single + // bitwise AND operation on the discriminant (`discriminant & 63 != 0`). + TSOptionalType(Box<'a, TSOptionalType<'a>>) = 64, + TSRestType(Box<'a, TSRestType<'a>>) = 65, +} } #[derive(Debug, Hash)] @@ -421,13 +475,23 @@ pub struct TSTypeReference<'a> { /// TypeName: /// IdentifierReference /// NamespaceName . IdentifierReference +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum TSTypeName<'a> { - IdentifierReference(Box<'a, IdentifierReference<'a>>), - QualifiedName(Box<'a, TSQualifiedName<'a>>), + IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0, + QualifiedName(Box<'a, TSQualifiedName<'a>>) = 1, +} + +/// Macro for matching `TSTypeName`'s variants. +#[macro_export] +macro_rules! match_ts_type_name { + ($ty:ident) => { + $ty::IdentifierReference(_) | $ty::QualifiedName(_) + }; } +pub use match_ts_type_name; impl<'a> TSTypeName<'a> { pub fn get_first_name(name: &TSTypeName<'a>) -> IdentifierReference<'a> { @@ -782,12 +846,16 @@ pub struct TSTypeQuery<'a> { pub type_parameters: Option>>, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum TSTypeQueryExprName<'a> { - TSTypeName(TSTypeName<'a>), - TSImportType(Box<'a, TSImportType<'a>>), + // `TSTypeName` variants added here by `inherit_variants!` macro + @inherit TSTypeName + TSImportType(Box<'a, TSImportType<'a>>) = 2, +} } #[derive(Debug, Hash)] @@ -929,12 +997,16 @@ pub struct TSImportEqualsDeclaration<'a> { pub import_kind: ImportOrExportKind, } +inherit_variants! { +#[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))] pub enum TSModuleReference<'a> { - TypeName(TSTypeName<'a>), - ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>), + // `TSTypeName` variants added here by `inherit_variants!` macro + @inherit TSTypeName + ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 2, +} } #[derive(Debug, Hash)] @@ -976,7 +1048,9 @@ impl<'a> Decorator<'a> { pub fn name(&self) -> Option<&str> { match &self.expression { Expression::Identifier(ident) => Some(&ident.name), - Expression::MemberExpression(member) => member.static_property_name(), + expr @ match_member_expression!(Expression) => { + expr.to_member_expression().static_property_name() + } Expression::CallExpression(call) => { call.callee.get_member_expr().map(|member| member.static_property_name())? } diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index 050867bd377c1..524aa6caae01d 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -281,11 +281,7 @@ impl<'a> AstBuilder<'a> { declarations: Vec<'a, VariableDeclarator<'a>>, is_await: bool, ) -> Statement<'a> { - Statement::Declaration(Declaration::UsingDeclaration(self.alloc(UsingDeclaration { - span, - is_await, - declarations, - }))) + Statement::UsingDeclaration(self.alloc(UsingDeclaration { span, is_await, declarations })) } pub fn do_while_statement( @@ -487,27 +483,21 @@ impl<'a> AstBuilder<'a> { &self, array: ArrayAssignmentTarget<'a>, ) -> AssignmentTarget<'a> { - AssignmentTarget::AssignmentTargetPattern(AssignmentTargetPattern::ArrayAssignmentTarget( - self.alloc(array), - )) + AssignmentTarget::ArrayAssignmentTarget(self.alloc(array)) } pub fn array_assignment_target_maybe_default( &self, array: ArrayAssignmentTarget<'a>, ) -> AssignmentTargetMaybeDefault<'a> { - AssignmentTargetMaybeDefault::AssignmentTarget(AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ArrayAssignmentTarget(self.alloc(array)), - )) + AssignmentTargetMaybeDefault::ArrayAssignmentTarget(self.alloc(array)) } pub fn object_assignment_target( &self, array: ObjectAssignmentTarget<'a>, ) -> AssignmentTarget<'a> { - AssignmentTarget::AssignmentTargetPattern(AssignmentTargetPattern::ObjectAssignmentTarget( - self.alloc(array), - )) + AssignmentTarget::ObjectAssignmentTarget(self.alloc(array)) } pub fn assignment_target_property_property( @@ -525,18 +515,14 @@ impl<'a> AstBuilder<'a> { &self, ident: IdentifierReference<'a>, ) -> AssignmentTarget<'a> { - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(self.alloc(ident)), - ) + AssignmentTarget::AssignmentTargetIdentifier(self.alloc(ident)) } pub fn simple_assignment_target_member_expression( &self, expr: MemberExpression<'a>, ) -> AssignmentTarget<'a> { - AssignmentTarget::SimpleAssignmentTarget(SimpleAssignmentTarget::MemberAssignmentTarget( - self.alloc(expr), - )) + AssignmentTarget::from(SimpleAssignmentTarget::from(expr)) } pub fn await_expression(&self, span: Span, argument: Expression<'a>) -> Expression<'a> { @@ -617,7 +603,7 @@ impl<'a> AstBuilder<'a> { } pub fn member_expression(&self, expr: MemberExpression<'a>) -> Expression<'a> { - Expression::MemberExpression(self.alloc(expr)) + Expression::from(expr) } pub fn computed_member( @@ -627,12 +613,12 @@ impl<'a> AstBuilder<'a> { expression: Expression<'a>, optional: bool, // for optional chaining ) -> MemberExpression<'a> { - MemberExpression::ComputedMemberExpression(ComputedMemberExpression { + MemberExpression::ComputedMemberExpression(self.alloc(ComputedMemberExpression { span, object, expression, optional, - }) + })) } pub fn computed_member_expression( @@ -652,12 +638,12 @@ impl<'a> AstBuilder<'a> { property: IdentifierName<'a>, optional: bool, // for optional chaining ) -> MemberExpression<'a> { - MemberExpression::StaticMemberExpression(StaticMemberExpression { + MemberExpression::StaticMemberExpression(self.alloc(StaticMemberExpression { span, object, property, optional, - }) + })) } pub fn static_member_expression( @@ -677,12 +663,12 @@ impl<'a> AstBuilder<'a> { field: PrivateIdentifier<'a>, optional: bool, ) -> MemberExpression<'a> { - MemberExpression::PrivateFieldExpression(PrivateFieldExpression { + MemberExpression::PrivateFieldExpression(self.alloc(PrivateFieldExpression { span, object, field, optional, - }) + })) } pub fn private_in_expression( @@ -830,7 +816,7 @@ impl<'a> AstBuilder<'a> { /* ---------- Functions ---------- */ pub fn function_declaration(&self, func: Box<'a, Function<'a>>) -> Statement<'a> { - Statement::Declaration(Declaration::FunctionDeclaration(func)) + Statement::FunctionDeclaration(func) } pub fn formal_parameters( @@ -971,7 +957,7 @@ impl<'a> AstBuilder<'a> { } pub fn class_declaration(&self, class: Box<'a, Class<'a>>) -> Statement<'a> { - Statement::Declaration(Declaration::ClassDeclaration(class)) + Statement::ClassDeclaration(class) } pub fn static_block(&self, span: Span, body: Vec<'a, Statement<'a>>) -> ClassElement<'a> { @@ -1153,17 +1139,17 @@ impl<'a> AstBuilder<'a> { } pub fn property_key_identifier(&self, ident: IdentifierName<'a>) -> PropertyKey<'a> { - PropertyKey::Identifier(self.alloc(ident)) + PropertyKey::StaticIdentifier(self.alloc(ident)) } pub fn property_key_expression(&self, expr: Expression<'a>) -> PropertyKey<'a> { - PropertyKey::Expression(expr) + PropertyKey::from(expr) } /* ---------- Modules ---------- */ pub fn module_declaration(&self, decl: ModuleDeclaration<'a>) -> Statement<'a> { - Statement::ModuleDeclaration(self.alloc(decl)) + Statement::from(decl) } pub fn import_declaration( @@ -1889,28 +1875,28 @@ impl<'a> AstBuilder<'a> { &self, ident: IdentifierName<'a>, ) -> TSEnumMemberName<'a> { - TSEnumMemberName::Identifier(self.alloc(ident)) + TSEnumMemberName::StaticIdentifier(self.alloc(ident)) } pub fn ts_enum_member_name_string_literal( &self, lit: StringLiteral<'a>, ) -> TSEnumMemberName<'a> { - TSEnumMemberName::StringLiteral(self.alloc(lit)) + TSEnumMemberName::StaticStringLiteral(self.alloc(lit)) } pub fn ts_enum_member_name_computed_property_name( &self, expr: Expression<'a>, ) -> TSEnumMemberName<'a> { - TSEnumMemberName::ComputedPropertyName(expr) + TSEnumMemberName::from(expr) } pub fn ts_enum_member_name_number_literal( &self, lit: NumericLiteral<'a>, ) -> TSEnumMemberName<'a> { - TSEnumMemberName::NumericLiteral(self.alloc(lit)) + TSEnumMemberName::StaticNumericLiteral(self.alloc(lit)) } pub fn ts_module_reference_external_module_reference( @@ -1924,7 +1910,7 @@ impl<'a> AstBuilder<'a> { &self, reference: TSTypeName<'a>, ) -> TSModuleReference<'a> { - TSModuleReference::TypeName(reference) + TSModuleReference::from(reference) } pub fn ts_type_predicate_name_this(&self, ty: TSThisType) -> TSTypePredicateName<'a> { @@ -1946,7 +1932,7 @@ impl<'a> AstBuilder<'a> { } pub fn ts_type_query_expr_name_type_name(&self, ty: TSTypeName<'a>) -> TSTypeQueryExprName<'a> { - TSTypeQueryExprName::TSTypeName(ty) + TSTypeQueryExprName::from(ty) } /* JSDoc */ diff --git a/crates/oxc_ast/src/ast_kind.rs b/crates/oxc_ast/src/ast_kind.rs index 513d16a7ed07b..551ee88b61a06 100644 --- a/crates/oxc_ast/src/ast_kind.rs +++ b/crates/oxc_ast/src/ast_kind.rs @@ -330,7 +330,9 @@ impl<'a> AstKind<'a> { Expression::FunctionExpression(e) => Self::Function(e), Expression::ImportExpression(e) => Self::ImportExpression(e), Expression::LogicalExpression(e) => Self::LogicalExpression(e), - Expression::MemberExpression(e) => Self::MemberExpression(e), + match_member_expression!(Expression) => { + Self::MemberExpression(e.to_member_expression()) + } Expression::NewExpression(e) => Self::NewExpression(e), Expression::ObjectExpression(e) => Self::ObjectExpression(e), Expression::ParenthesizedExpression(e) => Self::ParenthesizedExpression(e), diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index 8fa60dfd3e420..680f983508556 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -66,7 +66,7 @@ fn size_asserts() { assert_eq_size!(ast::ClassElement, [u8; 16]); assert_eq_size!(ast::ExportDefaultDeclarationKind, [u8; 16]); assert_eq_size!(ast::AssignmentTargetPattern, [u8; 16]); - assert_eq_size!(ast::AssignmentTargetMaybeDefault, [u8; 24]); + assert_eq_size!(ast::AssignmentTargetMaybeDefault, [u8; 16]); assert_eq_size!(ast::AssignmentTargetProperty, [u8; 16]); assert_eq_size!(ast::TSLiteral, [u8; 16]); assert_eq_size!(ast::TSType, [u8; 16]); diff --git a/crates/oxc_ast/src/precedence.rs b/crates/oxc_ast/src/precedence.rs index 443b6ea1ac4b9..49ac0183f0a38 100644 --- a/crates/oxc_ast/src/precedence.rs +++ b/crates/oxc_ast/src/precedence.rs @@ -1,10 +1,10 @@ use oxc_syntax::precedence::{GetPrecedence, Precedence}; use crate::ast::{ - ArrowFunctionExpression, AssignmentExpression, AwaitExpression, BinaryExpression, - CallExpression, ConditionalExpression, Expression, ImportExpression, LogicalExpression, - MemberExpression, NewExpression, SequenceExpression, UnaryExpression, UpdateExpression, - YieldExpression, + match_member_expression, ArrowFunctionExpression, AssignmentExpression, AwaitExpression, + BinaryExpression, CallExpression, ConditionalExpression, Expression, ImportExpression, + LogicalExpression, MemberExpression, NewExpression, SequenceExpression, UnaryExpression, + UpdateExpression, YieldExpression, }; impl<'a> GetPrecedence for Expression<'a> { @@ -22,7 +22,7 @@ impl<'a> GetPrecedence for Expression<'a> { Self::AwaitExpression(expr) => expr.precedence(), Self::NewExpression(expr) => expr.precedence(), Self::CallExpression(expr) => expr.precedence(), - Self::MemberExpression(expr) => expr.precedence(), + match_member_expression!(Self) => self.to_member_expression().precedence(), _ => panic!("All cases should be covered"), } } diff --git a/crates/oxc_ast/src/span.rs b/crates/oxc_ast/src/span.rs index c636b95f21f50..bc4fb1994ede2 100644 --- a/crates/oxc_ast/src/span.rs +++ b/crates/oxc_ast/src/span.rs @@ -23,8 +23,23 @@ impl<'a> GetSpan for Statement<'a> { Self::TryStatement(stmt) => stmt.span, Self::WhileStatement(stmt) => stmt.span, Self::WithStatement(stmt) => stmt.span, - Self::ModuleDeclaration(decl) => decl.span(), - Self::Declaration(decl) => decl.span(), + // `ModuleDeclaration` + Self::ImportDeclaration(decl) => decl.span, + Self::ExportAllDeclaration(decl) => decl.span, + Self::ExportDefaultDeclaration(decl) => decl.span, + Self::ExportNamedDeclaration(decl) => decl.span, + Self::TSExportAssignment(decl) => decl.span, + Self::TSNamespaceExportDeclaration(decl) => decl.span, + // `Declaration` + Self::VariableDeclaration(decl) => decl.span, + Self::FunctionDeclaration(decl) => decl.span, + Self::ClassDeclaration(decl) => decl.span, + Self::UsingDeclaration(decl) => decl.span, + Self::TSTypeAliasDeclaration(decl) => decl.span, + Self::TSInterfaceDeclaration(decl) => decl.span, + Self::TSEnumDeclaration(decl) => decl.span, + Self::TSModuleDeclaration(decl) => decl.span, + Self::TSImportEqualsDeclaration(decl) => decl.span, } } } @@ -55,7 +70,6 @@ impl<'a> GetSpan for Expression<'a> { Self::FunctionExpression(e) => e.span, Self::ImportExpression(e) => e.span, Self::LogicalExpression(e) => e.span, - Self::MemberExpression(e) => e.span(), Self::NewExpression(e) => e.span, Self::ObjectExpression(e) => e.span, Self::ParenthesizedExpression(e) => e.span, @@ -72,6 +86,10 @@ impl<'a> GetSpan for Expression<'a> { Self::TSTypeAssertion(e) => e.span, Self::TSNonNullExpression(e) => e.span, Self::TSInstantiationExpression(e) => e.span, + // `MemberExpression` + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } @@ -125,9 +143,51 @@ impl<'a> GetSpan for ClassElement<'a> { impl<'a> GetSpan for PropertyKey<'a> { fn span(&self) -> Span { match self { - Self::Identifier(ident) => ident.span, + Self::StaticIdentifier(ident) => ident.span, Self::PrivateIdentifier(ident) => ident.span, - Self::Expression(expr) => expr.span(), + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } @@ -210,8 +270,18 @@ impl<'a> GetSpan for ObjectPropertyKind<'a> { impl<'a> GetSpan for AssignmentTarget<'a> { fn span(&self) -> Span { match self { - Self::SimpleAssignmentTarget(target) => target.span(), - Self::AssignmentTargetPattern(pat) => pat.span(), + // `SimpleAssignmentTarget` + Self::AssignmentTargetIdentifier(ident) => ident.span, + Self::TSAsExpression(expr) => expr.span, + Self::TSSatisfiesExpression(expr) => expr.span, + Self::TSNonNullExpression(expr) => expr.span, + Self::TSTypeAssertion(expr) => expr.span, + // `AssignmentTargetPattern` + Self::ComputedMemberExpression(expr) => expr.span, + Self::StaticMemberExpression(expr) => expr.span, + Self::PrivateFieldExpression(expr) => expr.span, + Self::ArrayAssignmentTarget(pat) => pat.span, + Self::ObjectAssignmentTarget(pat) => pat.span, } } } @@ -229,7 +299,49 @@ impl<'a> GetSpan for Argument<'a> { fn span(&self) -> Span { match self { Self::SpreadElement(e) => e.span, - Self::Expression(expr) => expr.span(), + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } @@ -238,7 +350,50 @@ impl<'a> GetSpan for ArrayExpressionElement<'a> { fn span(&self) -> Span { match self { Self::SpreadElement(e) => e.span, - Self::Expression(expr) => expr.span(), + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, + Self::Elision(elision) => elision.span, } } @@ -248,8 +403,50 @@ impl<'a> GetSpan for ForStatementInit<'a> { fn span(&self) -> Span { match self { Self::VariableDeclaration(x) => x.span, - Self::Expression(x) => x.span(), Self::UsingDeclaration(x) => x.span, + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } @@ -258,7 +455,17 @@ impl<'a> GetSpan for ForStatementLeft<'a> { fn span(&self) -> Span { match self { Self::VariableDeclaration(x) => x.span, - Self::AssignmentTarget(x) => x.span(), + // `AssignmentTarget` + Self::AssignmentTargetIdentifier(x) => x.span, + Self::ComputedMemberExpression(x) => x.span, + Self::StaticMemberExpression(x) => x.span, + Self::PrivateFieldExpression(x) => x.span, + Self::TSAsExpression(x) => x.span, + Self::TSSatisfiesExpression(x) => x.span, + Self::TSNonNullExpression(x) => x.span, + Self::TSTypeAssertion(x) => x.span, + Self::ArrayAssignmentTarget(x) => x.span, + Self::ObjectAssignmentTarget(x) => x.span, Self::UsingDeclaration(x) => x.span, } } @@ -268,11 +475,14 @@ impl<'a> GetSpan for SimpleAssignmentTarget<'a> { fn span(&self) -> Span { match self { Self::AssignmentTargetIdentifier(ident) => ident.span, - Self::MemberAssignmentTarget(expr) => expr.span(), Self::TSAsExpression(expr) => expr.span, Self::TSSatisfiesExpression(expr) => expr.span, Self::TSNonNullExpression(expr) => expr.span, Self::TSTypeAssertion(expr) => expr.span, + // `MemberExpression` + Self::ComputedMemberExpression(expr) => expr.span, + Self::StaticMemberExpression(expr) => expr.span, + Self::PrivateFieldExpression(expr) => expr.span, } } } @@ -351,10 +561,52 @@ impl<'a> GetSpan for ExportDefaultDeclarationKind<'a> { fn span(&self) -> Span { match self { Self::ClassDeclaration(x) => x.span, - Self::Expression(x) => x.span(), Self::FunctionDeclaration(x) => x.span, Self::TSEnumDeclaration(x) => x.span, Self::TSInterfaceDeclaration(x) => x.span, + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } @@ -402,8 +654,50 @@ impl<'a> GetSpan for JSXAttributeItem<'a> { impl<'a> GetSpan for JSXExpression<'a> { fn span(&self) -> Span { match &self { - JSXExpression::Expression(expr) => expr.span(), - JSXExpression::EmptyExpression(exmpty_expr) => exmpty_expr.span, + Self::EmptyExpression(empty_expr) => empty_expr.span, + // `Expression` + Self::BooleanLiteral(e) => e.span, + Self::NullLiteral(e) => e.span, + Self::NumericLiteral(e) => e.span, + Self::BigintLiteral(e) => e.span, + Self::RegExpLiteral(e) => e.span, + Self::StringLiteral(e) => e.span, + Self::TemplateLiteral(e) => e.span, + Self::Identifier(e) => e.span, + Self::MetaProperty(e) => e.span, + Self::Super(e) => e.span, + Self::ArrayExpression(e) => e.span, + Self::ArrowFunctionExpression(e) => e.span, + Self::AssignmentExpression(e) => e.span, + Self::AwaitExpression(e) => e.span, + Self::BinaryExpression(e) => e.span, + Self::CallExpression(e) => e.span, + Self::ChainExpression(e) => e.span, + Self::ClassExpression(e) => e.span, + Self::ConditionalExpression(e) => e.span, + Self::FunctionExpression(e) => e.span, + Self::ImportExpression(e) => e.span, + Self::LogicalExpression(e) => e.span, + Self::NewExpression(e) => e.span, + Self::ObjectExpression(e) => e.span, + Self::ParenthesizedExpression(e) => e.span, + Self::SequenceExpression(e) => e.span, + Self::TaggedTemplateExpression(e) => e.span, + Self::ThisExpression(e) => e.span, + Self::UnaryExpression(e) => e.span, + Self::UpdateExpression(e) => e.span, + Self::YieldExpression(e) => e.span, + Self::PrivateInExpression(e) => e.span, + Self::JSXElement(e) => e.span, + Self::JSXFragment(e) => e.span, + Self::TSAsExpression(e) => e.span, + Self::TSSatisfiesExpression(e) => e.span, + Self::TSTypeAssertion(e) => e.span, + Self::TSNonNullExpression(e) => e.span, + Self::TSInstantiationExpression(e) => e.span, + Self::ComputedMemberExpression(e) => e.span, + Self::StaticMemberExpression(e) => e.span, + Self::PrivateFieldExpression(e) => e.span, } } } diff --git a/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs b/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs index 49e5b358f8feb..3fb1758468e1b 100644 --- a/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs +++ b/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs @@ -10,7 +10,7 @@ impl<'a> GatherNodeParts<'a> for Expression<'a> { fn gather)>(&self, f: &mut F) { match self { Self::Identifier(ident) => f(ident.name.clone()), - Self::MemberExpression(expr) => expr.gather(f), + match_member_expression!(Self) => self.to_member_expression().gather(f), Self::AssignmentExpression(expr) => expr.left.gather(f), Self::UpdateExpression(expr) => expr.argument.gather(f), Self::StringLiteral(lit) => lit.gather(f), @@ -41,8 +41,10 @@ impl<'a> GatherNodeParts<'a> for MemberExpression<'a> { impl<'a> GatherNodeParts<'a> for AssignmentTarget<'a> { fn gather)>(&self, f: &mut F) { match self { - AssignmentTarget::SimpleAssignmentTarget(t) => t.gather(f), - AssignmentTarget::AssignmentTargetPattern(_) => {} + match_simple_assignment_target!(Self) => { + self.to_simple_assignment_target().gather(f); + } + match_assignment_target_pattern!(Self) => {} } } } @@ -51,7 +53,7 @@ impl<'a> GatherNodeParts<'a> for SimpleAssignmentTarget<'a> { fn gather)>(&self, f: &mut F) { match self { Self::AssignmentTargetIdentifier(ident) => ident.gather(f), - Self::MemberAssignmentTarget(expr) => expr.gather(f), + match_member_expression!(Self) => self.to_member_expression().gather(f), _ => {} } } diff --git a/crates/oxc_ast/src/syntax_directed_operations/prop_name.rs b/crates/oxc_ast/src/syntax_directed_operations/prop_name.rs index 04f00ab949c91..0b56af7261372 100644 --- a/crates/oxc_ast/src/syntax_directed_operations/prop_name.rs +++ b/crates/oxc_ast/src/syntax_directed_operations/prop_name.rs @@ -28,13 +28,10 @@ impl<'a> PropName for ObjectProperty<'a> { impl<'a> PropName for PropertyKey<'a> { fn prop_name(&self) -> Option<(&str, Span)> { match self { + PropertyKey::StaticIdentifier(ident) => Some((&ident.name, ident.span)), PropertyKey::Identifier(ident) => Some((&ident.name, ident.span)), - PropertyKey::PrivateIdentifier(_) => None, - PropertyKey::Expression(expr) => match &expr { - Expression::Identifier(ident) => Some((&ident.name, ident.span)), - Expression::StringLiteral(lit) => Some((&lit.value, lit.span)), - _ => None, - }, + PropertyKey::StringLiteral(lit) => Some((&lit.value, lit.span)), + _ => None, } } } diff --git a/crates/oxc_ast/src/visit/visit.rs b/crates/oxc_ast/src/visit/visit.rs index 03d4425591ee4..f8b7b1eee07c6 100644 --- a/crates/oxc_ast/src/visit/visit.rs +++ b/crates/oxc_ast/src/visit/visit.rs @@ -925,8 +925,10 @@ pub mod walk { Statement::WhileStatement(stmt) => visitor.visit_while_statement(stmt), Statement::WithStatement(stmt) => visitor.visit_with_statement(stmt), - Statement::ModuleDeclaration(decl) => visitor.visit_module_declaration(decl), - Statement::Declaration(decl) => visitor.visit_declaration(decl), + match_module_declaration!(Statement) => { + visitor.visit_module_declaration(stmt.to_module_declaration()); + } + match_declaration!(Statement) => visitor.visit_declaration(stmt.to_declaration()), } } @@ -1024,7 +1026,7 @@ pub mod walk { ForStatementInit::VariableDeclaration(decl) => { visitor.visit_variable_declaration(decl); } - ForStatementInit::Expression(expr) => visitor.visit_expression(expr), + match_expression!(ForStatementInit) => visitor.visit_expression(init.to_expression()), } visitor.leave_node(kind); } @@ -1066,7 +1068,9 @@ pub mod walk { ForStatementLeft::VariableDeclaration(decl) => { visitor.visit_variable_declaration(decl); } - ForStatementLeft::AssignmentTarget(target) => visitor.visit_assignment_target(target), + match_assignment_target!(ForStatementLeft) => { + visitor.visit_assignment_target(left.to_assignment_target()); + } ForStatementLeft::UsingDeclaration(decl) => { visitor.visit_using_declaration(decl); } @@ -1446,7 +1450,9 @@ pub mod walk { Expression::FunctionExpression(expr) => visitor.visit_function(expr, None), Expression::ImportExpression(expr) => visitor.visit_import_expression(expr), Expression::LogicalExpression(expr) => visitor.visit_logical_expression(expr), - Expression::MemberExpression(expr) => visitor.visit_member_expression(expr), + match_member_expression!(Expression) => { + visitor.visit_member_expression(expr.to_member_expression()); + } Expression::NewExpression(expr) => visitor.visit_new_expression(expr), Expression::ObjectExpression(expr) => visitor.visit_object_expression(expr), Expression::ParenthesizedExpression(expr) => { @@ -1498,8 +1504,8 @@ pub mod walk { visitor.enter_node(kind); match arg { ArrayExpressionElement::SpreadElement(spread) => visitor.visit_spread_element(spread), - ArrayExpressionElement::Expression(expr) => { - visitor.visit_expression_array_element(expr); + match_expression!(ArrayExpressionElement) => { + visitor.visit_expression_array_element(arg.to_expression()); } ArrayExpressionElement::Elision(elision) => visitor.visit_elision(elision), } @@ -1511,7 +1517,7 @@ pub mod walk { visitor.enter_node(kind); match arg { Argument::SpreadElement(spread) => visitor.visit_spread_element(spread), - Argument::Expression(expr) => visitor.visit_expression(expr), + match_expression!(Argument) => visitor.visit_expression(arg.to_expression()), } visitor.leave_node(kind); } @@ -1601,7 +1607,9 @@ pub mod walk { pub fn walk_chain_element<'a, V: Visit<'a>>(visitor: &mut V, elem: &ChainElement<'a>) { match elem { ChainElement::CallExpression(expr) => visitor.visit_call_expression(expr), - ChainElement::MemberExpression(expr) => visitor.visit_member_expression(expr), + match_member_expression!(ChainElement) => { + visitor.visit_member_expression(elem.to_member_expression()); + } } } @@ -1723,9 +1731,11 @@ pub mod walk { let kind = AstKind::PropertyKey(visitor.alloc(key)); visitor.enter_node(kind); match key { - PropertyKey::Identifier(ident) => visitor.visit_identifier_name(ident), + PropertyKey::StaticIdentifier(ident) => visitor.visit_identifier_name(ident), PropertyKey::PrivateIdentifier(ident) => visitor.visit_private_identifier(ident), - PropertyKey::Expression(expr) => visitor.visit_expression(expr), + match_expression!(PropertyKey) => { + visitor.visit_expression(key.to_expression()); + } } visitor.leave_node(kind); } @@ -1816,11 +1826,11 @@ pub mod walk { let kind = AstKind::AssignmentTarget(visitor.alloc(target)); visitor.enter_node(kind); match target { - AssignmentTarget::SimpleAssignmentTarget(target) => { - visitor.visit_simple_assignment_target(target); + match_simple_assignment_target!(AssignmentTarget) => { + visitor.visit_simple_assignment_target(target.to_simple_assignment_target()); } - AssignmentTarget::AssignmentTargetPattern(pat) => { - visitor.visit_assignment_target_pattern(pat); + match_assignment_target_pattern!(AssignmentTarget) => { + visitor.visit_assignment_target_pattern(target.to_assignment_target_pattern()); } } visitor.leave_node(kind); @@ -1836,8 +1846,8 @@ pub mod walk { SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { visitor.visit_identifier_reference(ident); } - SimpleAssignmentTarget::MemberAssignmentTarget(expr) => { - visitor.visit_member_expression(expr); + match_member_expression!(SimpleAssignmentTarget) => { + visitor.visit_member_expression(target.to_member_expression()); } SimpleAssignmentTarget::TSAsExpression(expr) => { visitor.visit_expression(&expr.expression); @@ -1886,8 +1896,8 @@ pub mod walk { target: &AssignmentTargetMaybeDefault<'a>, ) { match target { - AssignmentTargetMaybeDefault::AssignmentTarget(target) => { - visitor.visit_assignment_target(target); + match_assignment_target!(AssignmentTargetMaybeDefault) => { + visitor.visit_assignment_target(target.to_assignment_target()); } AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(target) => { visitor.visit_assignment_target_with_default(target); @@ -2100,7 +2110,7 @@ pub mod walk { pub fn walk_jsx_expression<'a, V: Visit<'a>>(visitor: &mut V, expr: &JSXExpression<'a>) { match expr { - JSXExpression::Expression(expr) => visitor.visit_expression(expr), + match_expression!(JSXExpression) => visitor.visit_expression(expr.to_expression()), JSXExpression::EmptyExpression(_) => {} } } @@ -2423,7 +2433,9 @@ pub mod walk { let kind = AstKind::ExportDefaultDeclaration(visitor.alloc(decl)); visitor.enter_node(kind); match &decl.declaration { - ExportDefaultDeclarationKind::Expression(expr) => visitor.visit_expression(expr), + declaration @ match_expression!(ExportDefaultDeclarationKind) => { + visitor.visit_expression(declaration.to_expression()); + } ExportDefaultDeclarationKind::FunctionDeclaration(func) => { visitor.visit_function(func, None); } @@ -2514,7 +2526,9 @@ pub mod walk { reference: &TSModuleReference<'a>, ) { match reference { - TSModuleReference::TypeName(name) => visitor.visit_ts_type_name(name), + match_ts_type_name!(TSModuleReference) => { + visitor.visit_ts_type_name(reference.to_ts_type_name()); + } TSModuleReference::ExternalModuleReference(reference) => { visitor.visit_ts_external_module_reference(reference); } @@ -2745,7 +2759,7 @@ pub mod walk { pub fn walk_ts_tuple_element<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSTupleElement<'a>) { match ty { - TSTupleElement::TSType(ty) => visitor.visit_ts_type(ty), + match_ts_type!(TSTupleElement) => visitor.visit_ts_type(ty.to_ts_type()), TSTupleElement::TSOptionalType(ty) => visitor.visit_ts_type(&ty.type_annotation), TSTupleElement::TSRestType(ty) => visitor.visit_ts_type(&ty.type_annotation), }; @@ -2984,7 +2998,9 @@ pub mod walk { let kind = AstKind::TSTypeQuery(visitor.alloc(ty)); visitor.enter_node(kind); match &ty.expr_name { - TSTypeQueryExprName::TSTypeName(name) => visitor.visit_ts_type_name(name), + name @ match_ts_type_name!(TSTypeQueryExprName) => { + visitor.visit_ts_type_name(name.to_ts_type_name()); + } TSTypeQueryExprName::TSImportType(import) => visitor.visit_ts_import_type(import), } if let Some(type_parameters) = &ty.type_parameters { diff --git a/crates/oxc_ast/src/visit/visit_mut.rs b/crates/oxc_ast/src/visit/visit_mut.rs index 04d11a9e9b935..52b03ac0f5e1d 100644 --- a/crates/oxc_ast/src/visit/visit_mut.rs +++ b/crates/oxc_ast/src/visit/visit_mut.rs @@ -879,8 +879,10 @@ pub mod walk_mut { Statement::WhileStatement(stmt) => visitor.visit_while_statement(stmt), Statement::WithStatement(stmt) => visitor.visit_with_statement(stmt), - Statement::ModuleDeclaration(decl) => visitor.visit_module_declaration(decl), - Statement::Declaration(decl) => visitor.visit_declaration(decl), + match_module_declaration!(Statement) => { + visitor.visit_module_declaration(stmt.to_module_declaration_mut()); + } + match_declaration!(Statement) => visitor.visit_declaration(stmt.to_declaration_mut()), } } @@ -996,7 +998,9 @@ pub mod walk_mut { ForStatementInit::VariableDeclaration(decl) => { visitor.visit_variable_declaration(decl); } - ForStatementInit::Expression(expr) => visitor.visit_expression(expr), + match_expression!(ForStatementInit) => { + visitor.visit_expression(init.to_expression_mut()); + } ForStatementInit::UsingDeclaration(decl) => { visitor.visit_using_declaration(decl); } @@ -1050,7 +1054,9 @@ pub mod walk_mut { ForStatementLeft::VariableDeclaration(decl) => { visitor.visit_variable_declaration(decl); } - ForStatementLeft::AssignmentTarget(target) => visitor.visit_assignment_target(target), + match_assignment_target!(ForStatementLeft) => { + visitor.visit_assignment_target(left.to_assignment_target_mut()); + } ForStatementLeft::UsingDeclaration(decl) => { visitor.visit_using_declaration(decl); } @@ -1474,7 +1480,9 @@ pub mod walk_mut { Expression::FunctionExpression(expr) => visitor.visit_function(expr, None), Expression::ImportExpression(expr) => visitor.visit_import_expression(expr), Expression::LogicalExpression(expr) => visitor.visit_logical_expression(expr), - Expression::MemberExpression(expr) => visitor.visit_member_expression(expr), + match_member_expression!(Expression) => { + visitor.visit_member_expression(expr.to_member_expression_mut()); + } Expression::NewExpression(expr) => visitor.visit_new_expression(expr), Expression::ObjectExpression(expr) => visitor.visit_object_expression(expr), Expression::ParenthesizedExpression(expr) => { @@ -1532,8 +1540,8 @@ pub mod walk_mut { visitor.enter_node(kind); match arg { ArrayExpressionElement::SpreadElement(spread) => visitor.visit_spread_element(spread), - ArrayExpressionElement::Expression(expr) => { - visitor.visit_expression_array_element(expr); + match_expression!(ArrayExpressionElement) => { + visitor.visit_expression_array_element(arg.to_expression_mut()); } ArrayExpressionElement::Elision(elision) => visitor.visit_elision(elision), } @@ -1545,7 +1553,7 @@ pub mod walk_mut { visitor.enter_node(kind); match arg { Argument::SpreadElement(spread) => visitor.visit_spread_element(spread), - Argument::Expression(expr) => visitor.visit_expression(expr), + match_expression!(Argument) => visitor.visit_expression(arg.to_expression_mut()), } visitor.leave_node(kind); } @@ -1656,7 +1664,9 @@ pub mod walk_mut { ) { match elem { ChainElement::CallExpression(expr) => visitor.visit_call_expression(expr), - ChainElement::MemberExpression(expr) => visitor.visit_member_expression(expr), + match_member_expression!(ChainElement) => { + visitor.visit_member_expression(elem.to_member_expression_mut()); + } } } @@ -1793,9 +1803,9 @@ pub mod walk_mut { let kind = AstType::PropertyKey; visitor.enter_node(kind); match key { - PropertyKey::Identifier(ident) => visitor.visit_identifier_name(ident), + PropertyKey::StaticIdentifier(ident) => visitor.visit_identifier_name(ident), PropertyKey::PrivateIdentifier(ident) => visitor.visit_private_identifier(ident), - PropertyKey::Expression(expr) => visitor.visit_expression(expr), + match_expression!(PropertyKey) => visitor.visit_expression(key.to_expression_mut()), } visitor.leave_node(kind); } @@ -1895,11 +1905,11 @@ pub mod walk_mut { let kind = AstType::AssignmentTarget; visitor.enter_node(kind); match target { - AssignmentTarget::SimpleAssignmentTarget(target) => { - visitor.visit_simple_assignment_target(target); + match_simple_assignment_target!(AssignmentTarget) => { + visitor.visit_simple_assignment_target(target.to_simple_assignment_target_mut()); } - AssignmentTarget::AssignmentTargetPattern(pat) => { - visitor.visit_assignment_target_pattern(pat); + match_assignment_target_pattern!(AssignmentTarget) => { + visitor.visit_assignment_target_pattern(target.to_assignment_target_pattern_mut()); } } visitor.leave_node(kind); @@ -1915,8 +1925,8 @@ pub mod walk_mut { SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { visitor.visit_identifier_reference(ident); } - SimpleAssignmentTarget::MemberAssignmentTarget(expr) => { - visitor.visit_member_expression(expr); + match_member_expression!(SimpleAssignmentTarget) => { + visitor.visit_member_expression(target.to_member_expression_mut()); } SimpleAssignmentTarget::TSAsExpression(expr) => { visitor.visit_expression(&mut expr.expression); @@ -1965,8 +1975,8 @@ pub mod walk_mut { target: &mut AssignmentTargetMaybeDefault<'a>, ) { match target { - AssignmentTargetMaybeDefault::AssignmentTarget(target) => { - visitor.visit_assignment_target(target); + match_assignment_target!(AssignmentTargetMaybeDefault) => { + visitor.visit_assignment_target(target.to_assignment_target_mut()); } AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(target) => { visitor.visit_assignment_target_with_default(target); @@ -2195,7 +2205,7 @@ pub mod walk_mut { expr: &mut JSXExpression<'a>, ) { match expr { - JSXExpression::Expression(expr) => visitor.visit_expression(expr), + match_expression!(JSXExpression) => visitor.visit_expression(expr.to_expression_mut()), JSXExpression::EmptyExpression(_) => {} } } @@ -2571,7 +2581,9 @@ pub mod walk_mut { let kind = AstType::ExportDefaultDeclaration; visitor.enter_node(kind); match &mut decl.declaration { - ExportDefaultDeclarationKind::Expression(expr) => visitor.visit_expression(expr), + declaration @ match_expression!(ExportDefaultDeclarationKind) => { + visitor.visit_expression(declaration.to_expression_mut()); + } ExportDefaultDeclarationKind::FunctionDeclaration(func) => { visitor.visit_function(func, None); } @@ -2660,7 +2672,9 @@ pub mod walk_mut { reference: &mut TSModuleReference<'a>, ) { match reference { - TSModuleReference::TypeName(name) => visitor.visit_ts_type_name(name), + name @ match_ts_type_name!(TSModuleReference) => { + visitor.visit_ts_type_name(name.to_ts_type_name_mut()); + } TSModuleReference::ExternalModuleReference(reference) => { visitor.visit_ts_external_module_reference(reference); } @@ -2897,7 +2911,7 @@ pub mod walk_mut { ty: &mut TSTupleElement<'a>, ) { match ty { - TSTupleElement::TSType(ty) => visitor.visit_ts_type(ty), + match_ts_type!(TSTupleElement) => visitor.visit_ts_type(ty.to_ts_type_mut()), TSTupleElement::TSOptionalType(ty) => visitor.visit_ts_type(&mut ty.type_annotation), TSTupleElement::TSRestType(ty) => visitor.visit_ts_type(&mut ty.type_annotation), }; @@ -3160,7 +3174,9 @@ pub mod walk_mut { let kind = AstType::TSTypeQuery; visitor.enter_node(kind); match &mut ty.expr_name { - TSTypeQueryExprName::TSTypeName(name) => visitor.visit_ts_type_name(name), + name @ match_ts_type_name!(TSTypeQueryExprName) => { + visitor.visit_ts_type_name(name.to_ts_type_name_mut()); + } TSTypeQueryExprName::TSImportType(import) => visitor.visit_ts_import_type(import), } if let Some(type_parameters) = &mut ty.type_parameters { diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index ff037fcaf95d8..6f0f65bb42cff 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -88,7 +88,6 @@ impl<'a, const MINIFY: bool> Gen for Statement<'a> { Self::BreakStatement(stmt) => stmt.gen(p, ctx), Self::ContinueStatement(stmt) => stmt.gen(p, ctx), Self::DebuggerStatement(stmt) => stmt.gen(p, ctx), - Self::Declaration(decl) => decl.gen(p, ctx), Self::DoWhileStatement(stmt) => stmt.gen(p, ctx), Self::EmptyStatement(stmt) => stmt.gen(p, ctx), Self::ExpressionStatement(stmt) => stmt.gen(p, ctx), @@ -97,13 +96,14 @@ impl<'a, const MINIFY: bool> Gen for Statement<'a> { Self::ForStatement(stmt) => stmt.gen(p, ctx), Self::IfStatement(stmt) => stmt.gen(p, ctx), Self::LabeledStatement(stmt) => stmt.gen(p, ctx), - Self::ModuleDeclaration(decl) => decl.gen(p, ctx), Self::ReturnStatement(stmt) => stmt.gen(p, ctx), Self::SwitchStatement(stmt) => stmt.gen(p, ctx), Self::ThrowStatement(stmt) => stmt.gen(p, ctx), Self::TryStatement(stmt) => stmt.gen(p, ctx), Self::WhileStatement(stmt) => stmt.gen(p, ctx), Self::WithStatement(stmt) => stmt.gen(p, ctx), + match_module_declaration!(Self) => self.to_module_declaration().gen(p, ctx), + match_declaration!(Self) => self.to_declaration().gen(p, ctx), } } } @@ -226,8 +226,8 @@ impl<'a, const MINIFY: bool> Gen for ForStatement<'a> { let ctx = Context::empty(); match init { ForStatementInit::UsingDeclaration(decl) => decl.gen(p, ctx), - ForStatementInit::Expression(expr) => { - expr.gen_expr(p, Precedence::lowest(), ctx); + match_expression!(ForStatementInit) => { + init.to_expression().gen_expr(p, Precedence::lowest(), ctx); } ForStatementInit::VariableDeclaration(var) => var.gen(p, ctx), } @@ -295,17 +295,15 @@ impl<'a, const MINIFY: bool> Gen for ForOfStatement<'a> { impl<'a, const MINIFY: bool> Gen for ForStatementLeft<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { - match &self { + match self { ForStatementLeft::UsingDeclaration(var) => var.gen(p, ctx), ForStatementLeft::VariableDeclaration(var) => var.gen(p, ctx), - ForStatementLeft::AssignmentTarget(target) => { - let wrap = matches!( - target, - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(identifier) - ) if identifier.name == "async" - ); - p.wrap(wrap, |p| target.gen(p, ctx)); + ForStatementLeft::AssignmentTargetIdentifier(identifier) => { + let wrap = identifier.name == "async"; + p.wrap(wrap, |p| self.to_assignment_target().gen(p, ctx)); + } + match_assignment_target!(ForStatementLeft) => { + p.wrap(false, |p| self.to_assignment_target().gen(p, ctx)); } } } @@ -939,9 +937,9 @@ impl<'a, const MINIFY: bool> Gen for ExportDefaultDeclaration<'a> { impl<'a, const MINIFY: bool> Gen for ExportDefaultDeclarationKind<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::Expression(expr) => { + match_expression!(Self) => { p.start_of_default_export = p.code_len(); - expr.gen_expr(p, Precedence::Assign, Context::default()); + self.to_expression().gen_expr(p, Precedence::Assign, Context::default()); p.print_semicolon_after_statement(); } Self::FunctionDeclaration(fun) => fun.gen(p, ctx), @@ -966,7 +964,9 @@ impl<'a, const MINIFY: bool> GenExpr for Expression<'a> { Self::StringLiteral(lit) => lit.gen(p, ctx), Self::Identifier(ident) => ident.gen(p, ctx), Self::ThisExpression(expr) => expr.gen(p, ctx), - Self::MemberExpression(expr) => expr.gen_expr(p, precedence, ctx), + match_member_expression!(Self) => { + self.to_member_expression().gen_expr(p, precedence, ctx); + } Self::CallExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::ArrayExpression(expr) => expr.gen(p, ctx), Self::ObjectExpression(expr) => expr.gen_expr(p, precedence, ctx), @@ -1383,7 +1383,9 @@ impl<'a, const MINIFY: bool> Gen for Argument<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::SpreadElement(elem) => elem.gen(p, ctx), - Self::Expression(elem) => elem.gen_expr(p, Precedence::Assign, Context::default()), + match_expression!(Self) => { + self.to_expression().gen_expr(p, Precedence::Assign, Context::default()); + } } } } @@ -1391,7 +1393,9 @@ impl<'a, const MINIFY: bool> Gen for Argument<'a> { impl<'a, const MINIFY: bool> Gen for ArrayExpressionElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::Expression(expr) => expr.gen_expr(p, Precedence::Assign, Context::default()), + match_expression!(Self) => { + self.to_expression().gen_expr(p, Precedence::Assign, Context::default()); + } Self::SpreadElement(elem) => elem.gen(p, ctx), Self::Elision(_span) => {} } @@ -1521,9 +1525,11 @@ impl<'a, const MINIFY: bool> Gen for ObjectProperty<'a> { impl<'a, const MINIFY: bool> Gen for PropertyKey<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::Identifier(ident) => ident.gen(p, ctx), + Self::StaticIdentifier(ident) => ident.gen(p, ctx), Self::PrivateIdentifier(ident) => ident.gen(p, ctx), - Self::Expression(expr) => expr.gen_expr(p, Precedence::Assign, Context::default()), + match_expression!(Self) => { + self.to_expression().gen_expr(p, Precedence::Assign, Context::default()); + } } } } @@ -1727,39 +1733,24 @@ impl<'a, const MINIFY: bool> GenExpr for AssignmentExpression<'a> { let n = p.code_len(); let identifier_is_keyword = match &self.left { - AssignmentTarget::SimpleAssignmentTarget(assignment) => match assignment { - SimpleAssignmentTarget::AssignmentTargetIdentifier(target) => { - is_keyword(target.name.as_str()) - } - SimpleAssignmentTarget::MemberAssignmentTarget(target) => { - let target = &**target; - match target { - MemberExpression::ComputedMemberExpression(expression) => { - match &expression.object { - Expression::Identifier(ident) => is_keyword(ident.name.as_str()), - _ => false, - } - } - MemberExpression::StaticMemberExpression(expression) => { - is_keyword(expression.property.name.as_str()) - } - MemberExpression::PrivateFieldExpression(expression) => { - is_keyword(expression.field.name.as_str()) - } - } - } + AssignmentTarget::AssignmentTargetIdentifier(target) => { + is_keyword(target.name.as_str()) + } + AssignmentTarget::ComputedMemberExpression(expression) => match &expression.object { + Expression::Identifier(ident) => is_keyword(ident.name.as_str()), _ => false, }, - AssignmentTarget::AssignmentTargetPattern(_) => false, + AssignmentTarget::StaticMemberExpression(expression) => { + is_keyword(expression.property.name.as_str()) + } + AssignmentTarget::PrivateFieldExpression(expression) => { + is_keyword(expression.field.name.as_str()) + } + _ => false, }; let wrap = ((p.start_of_stmt == n || p.start_of_arrow_expr == n) - && matches!( - self.left, - AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ObjectAssignmentTarget(_) - ) - )) + && matches!(self.left, AssignmentTarget::ObjectAssignmentTarget(_))) || identifier_is_keyword; p.wrap(wrap || precedence > self.precedence(), |p| { self.left.gen(p, ctx); @@ -1774,10 +1765,16 @@ impl<'a, const MINIFY: bool> GenExpr for AssignmentExpression<'a> { impl<'a, const MINIFY: bool> Gen for AssignmentTarget<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::SimpleAssignmentTarget(target) => { - target.gen_expr(p, Precedence::Assign, Context::default()); + match_simple_assignment_target!(Self) => { + self.to_simple_assignment_target().gen_expr( + p, + Precedence::Assign, + Context::default(), + ); + } + match_assignment_target_pattern!(Self) => { + self.to_assignment_target_pattern().gen(p, ctx); } - Self::AssignmentTargetPattern(pat) => pat.gen(p, ctx), } } } @@ -1786,8 +1783,8 @@ impl<'a, const MINIFY: bool> GenExpr for SimpleAssignmentTarget<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { match self { Self::AssignmentTargetIdentifier(ident) => ident.gen(p, ctx), - Self::MemberAssignmentTarget(member_expr) => { - member_expr.gen_expr(p, precedence, ctx); + match_member_expression!(Self) => { + self.to_member_expression().gen_expr(p, precedence, ctx); } Self::TSAsExpression(e) => e.gen_expr(p, precedence, ctx), Self::TSSatisfiesExpression(e) => e.expression.gen_expr(p, precedence, ctx), @@ -1854,7 +1851,7 @@ impl<'a, const MINIFY: bool> Gen for ObjectAssignmentTarget<'a> { impl<'a, const MINIFY: bool> Gen for AssignmentTargetMaybeDefault<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::AssignmentTarget(target) => target.gen(p, ctx), + match_assignment_target!(Self) => self.to_assignment_target().gen(p, ctx), Self::AssignmentTargetWithDefault(target) => target.gen(p, ctx), } } @@ -1890,15 +1887,15 @@ impl<'a, const MINIFY: bool> Gen for AssignmentTargetPropertyIdentifier< impl<'a, const MINIFY: bool> Gen for AssignmentTargetPropertyProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self.name { - PropertyKey::Identifier(ident) => { + PropertyKey::StaticIdentifier(ident) => { ident.gen(p, ctx); } PropertyKey::PrivateIdentifier(ident) => { ident.gen(p, ctx); } - PropertyKey::Expression(expr) => { + key @ match_expression!(PropertyKey) => { p.print(b'['); - expr.gen_expr(p, Precedence::Assign, Context::default()); + key.to_expression().gen_expr(p, Precedence::Assign, Context::default()); p.print(b']'); } } @@ -1988,7 +1985,9 @@ impl<'a, const MINIFY: bool> GenExpr for ChainExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { match &self.expression { ChainElement::CallExpression(expr) => expr.gen_expr(p, precedence, ctx), - ChainElement::MemberExpression(expr) => expr.gen_expr(p, precedence, ctx), + match_member_expression!(ChainElement) => { + self.expression.to_member_expression().gen_expr(p, precedence, ctx); + } } } } @@ -2142,7 +2141,7 @@ impl Gen for JSXEmptyExpression { impl<'a, const MINIFY: bool> Gen for JSXExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::Expression(expr) => p.print_expression(expr), + match_expression!(Self) => p.print_expression(self.to_expression()), Self::EmptyExpression(expr) => expr.gen(p, ctx), } } @@ -2532,15 +2531,15 @@ impl<'a, const MINIFY: bool> Gen for Decorator<'a> { fn need_wrap(expr: &Expression) -> bool { match expr { // "@foo" - Expression::Identifier(_) => false, - Expression::MemberExpression(member_expr) => { - // "@foo.bar" - // "@(foo['bar'])" - matches!(&**member_expr, MemberExpression::ComputedMemberExpression(_)) - } + // "@foo.bar" + // "@foo.#bar" + Expression::Identifier(_) + | Expression::StaticMemberExpression(_) + | Expression::PrivateFieldExpression(_) => false, Expression::CallExpression(call_expr) => need_wrap(&call_expr.callee), // "@(foo + bar)" // "@(() => {})" + // "@(foo['bar'])" _ => true, } } diff --git a/crates/oxc_codegen/src/gen_ts.rs b/crates/oxc_codegen/src/gen_ts.rs index 769f3f086ec9c..2ce94c0b329e4 100644 --- a/crates/oxc_codegen/src/gen_ts.rs +++ b/crates/oxc_codegen/src/gen_ts.rs @@ -358,14 +358,14 @@ impl<'a, const MINIFY: bool> Gen for TSSignature<'a> { p.print(b']'); } else { match &signature.key { - PropertyKey::Identifier(key) => { + PropertyKey::StaticIdentifier(key) => { key.gen(p, ctx); } PropertyKey::PrivateIdentifier(key) => { p.print_str(key.name.as_bytes()); } - PropertyKey::Expression(key) => { - key.gen_expr(p, Precedence::Assign, ctx); + key @ match_expression!(PropertyKey) => { + key.to_expression().gen_expr(p, Precedence::Assign, ctx); } } } @@ -411,14 +411,14 @@ impl<'a, const MINIFY: bool> Gen for TSSignature<'a> { p.print(b']'); } else { match &signature.key { - PropertyKey::Identifier(key) => { + PropertyKey::StaticIdentifier(key) => { key.gen(p, ctx); } PropertyKey::PrivateIdentifier(key) => { p.print_str(key.name.as_bytes()); } - PropertyKey::Expression(key) => { - key.gen_expr(p, Precedence::Assign, ctx); + key @ match_expression!(PropertyKey) => { + key.to_expression().gen_expr(p, Precedence::Assign, ctx); } } } @@ -451,7 +451,7 @@ impl<'a, const MINIFY: bool> Gen for TSTypeQuery<'a> { impl<'a, const MINIFY: bool> Gen for TSTypeQueryExprName<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::TSTypeName(decl) => decl.gen(p, ctx), + match_ts_type_name!(Self) => self.to_ts_type_name().gen(p, ctx), Self::TSImportType(decl) => decl.gen(p, ctx), } } @@ -498,9 +498,7 @@ impl<'a, const MINIFY: bool> Gen for TSIndexSignature<'a> { impl<'a, const MINIFY: bool> Gen for TSTupleElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - TSTupleElement::TSType(ts_type) => { - ts_type.gen(p, ctx); - } + match_ts_type!(TSTupleElement) => self.to_ts_type().gen(p, ctx), TSTupleElement::TSOptionalType(ts_type) => { ts_type.type_annotation.gen(p, ctx); p.print_str(b"?"); @@ -634,14 +632,14 @@ impl<'a, const MINIFY: bool> Gen for TSEnumDeclaration<'a> { impl<'a, const MINIFY: bool> Gen for TSEnumMember<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self.id { - TSEnumMemberName::Identifier(decl) => decl.gen(p, ctx), - TSEnumMemberName::StringLiteral(decl) => decl.gen(p, ctx), - TSEnumMemberName::ComputedPropertyName(decl) => { + TSEnumMemberName::StaticIdentifier(decl) => decl.gen(p, ctx), + TSEnumMemberName::StaticStringLiteral(decl) => decl.gen(p, ctx), + TSEnumMemberName::StaticNumericLiteral(decl) => decl.gen(p, ctx), + decl @ match_expression!(TSEnumMemberName) => { p.print_str(b"["); - decl.gen_expr(p, Precedence::lowest(), ctx); + decl.to_expression().gen_expr(p, Precedence::lowest(), ctx); p.print_str(b"]"); } - TSEnumMemberName::NumericLiteral(decl) => decl.gen(p, ctx), } } } @@ -685,9 +683,7 @@ impl<'a, const MINIFY: bool> Gen for TSModuleReference<'a> { decl.expression.gen(p, ctx); p.print_str(b")"); } - Self::TypeName(decl) => { - decl.gen(p, ctx); - } + match_ts_type_name!(Self) => self.to_ts_type_name().gen(p, ctx), } } } diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index 4ecdf3a40feda..d2d0eb5abb703 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -395,7 +395,7 @@ impl Codegen { } } for stmt in statements { - if let Statement::Declaration(decl) = stmt { + if let Some(decl) = stmt.as_declaration() { if decl.is_typescript_syntax() && !self.options.enable_typescript && !matches!(decl, Declaration::TSEnumDeclaration(_)) diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index d8afca199fd1c..4c47ccc10f9c9 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -165,7 +165,7 @@ impl<'a, 'b> IsConstant<'a, 'b> for Argument<'a> { fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool { match self { Self::SpreadElement(element) => element.is_constant(in_boolean_position, ctx), - Self::Expression(expr) => expr.is_constant(in_boolean_position, ctx), + match_expression!(Self) => self.to_expression().is_constant(in_boolean_position, ctx), } } } @@ -174,7 +174,7 @@ impl<'a, 'b> IsConstant<'a, 'b> for ArrayExpressionElement<'a> { fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool { match self { Self::SpreadElement(element) => element.is_constant(in_boolean_position, ctx), - Self::Expression(expr) => expr.is_constant(in_boolean_position, ctx), + match_expression!(Self) => self.to_expression().is_constant(in_boolean_position, ctx), Self::Elision(_) => true, } } @@ -290,7 +290,7 @@ pub fn extract_regex_flags<'a>( if args.len() <= 1 { return None; } - let Argument::Expression(Expression::StringLiteral(flag_arg)) = &args[1] else { + let Argument::StringLiteral(flag_arg) = &args[1] else { return None; }; let mut flags = RegExpFlags::empty(); @@ -320,8 +320,7 @@ pub fn is_method_call<'a>( } } - let Expression::MemberExpression(member_expr) = &call_expr.callee.without_parenthesized() - else { + let Some(member_expr) = call_expr.callee.without_parenthesized().as_member_expression() else { return false; }; @@ -375,11 +374,7 @@ pub fn is_new_expression<'a>( pub fn call_expr_method_callee_info<'a>( call_expr: &'a CallExpression<'a>, ) -> Option<(Span, &'a str)> { - let Expression::MemberExpression(member_expr) = &call_expr.callee.without_parenthesized() - else { - return None; - }; - + let member_expr = call_expr.callee.without_parenthesized().as_member_expression()?; member_expr.static_property_info() } diff --git a/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs b/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs index 3b10a3ca7eaac..1d622a1c838ca 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs @@ -93,7 +93,7 @@ fn is_mistype_short_circuit(node: &AstNode) -> bool { let Expression::Identifier(left_ident) = &bin_expr.left else { return false }; - if let Expression::MemberExpression(member_expr) = &bin_expr.right { + if let Some(member_expr) = bin_expr.right.as_member_expression() { if let Expression::Identifier(ident) = member_expr.object() { return ident.name == left_ident.name; } diff --git a/crates/oxc_linter/src/rules/deepscan/bad_min_max_func.rs b/crates/oxc_linter/src/rules/deepscan/bad_min_max_func.rs index 0bc48fb693c8b..f9c2f022b86d3 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_min_max_func.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_min_max_func.rs @@ -90,7 +90,7 @@ impl BadMinMaxFunc { } let number_args = arguments.iter().filter_map(|arg| { - if let Argument::Expression(Expression::NumericLiteral(literal)) = arg { + if let Argument::NumericLiteral(literal) = arg { Some(literal.value) } else { None @@ -106,7 +106,7 @@ impl BadMinMaxFunc { let mut inner = vec![]; for expr in arguments.iter().filter_map(|arg| { - if let Argument::Expression(Expression::CallExpression(expr)) = arg { + if let Argument::CallExpression(expr) = arg { Some(&**expr) } else { None diff --git a/crates/oxc_linter/src/rules/deepscan/bad_replace_all_arg.rs b/crates/oxc_linter/src/rules/deepscan/bad_replace_all_arg.rs index 0f2e9d6d69821..f9160faf7e804 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_replace_all_arg.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_replace_all_arg.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, RegExpFlags}, + ast::{Expression, RegExpFlags}, AstKind, }; use oxc_diagnostics::{ @@ -56,7 +56,7 @@ impl Rule for BadReplaceAllArg { return; } - let Argument::Expression(regexp_argument) = &call_expr.arguments[0] else { + let Some(regexp_argument) = call_expr.arguments[0].as_expression() else { return; }; @@ -65,7 +65,7 @@ impl Rule for BadReplaceAllArg { }; if !flags.contains(RegExpFlags::G) { - let Expression::MemberExpression(call_expr_callee) = &call_expr.callee else { return }; + let Some(call_expr_callee) = call_expr.callee.as_member_expression() else { return }; let Some((replace_all_span, _)) = call_expr_callee.static_property_info() else { return; }; diff --git a/crates/oxc_linter/src/rules/deepscan/number_arg_out_of_range.rs b/crates/oxc_linter/src/rules/deepscan/number_arg_out_of_range.rs index 8e523e47f23fc..e24ea9f445490 100644 --- a/crates/oxc_linter/src/rules/deepscan/number_arg_out_of_range.rs +++ b/crates/oxc_linter/src/rules/deepscan/number_arg_out_of_range.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Argument, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -45,9 +42,7 @@ impl Rule for NumberArgOutOfRange { }; if let Some(member) = expr.callee.get_member_expr() { - if let Some(Argument::Expression(Expression::NumericLiteral(literal))) = - expr.arguments.first() - { + if let Some(Argument::NumericLiteral(literal)) = expr.arguments.first() { let value = literal.value; match member.static_property_name() { Some(name @ "toString") => { diff --git a/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs b/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs index 3eb938e993ccf..e70508d4799d0 100644 --- a/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs +++ b/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, MemberExpression}, + ast::{Argument, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -51,10 +51,7 @@ impl Rule for UninvokedArrayCallback { if new_expr.arguments.len() != 1 { return; } - if !matches!( - new_expr.arguments.first(), - Some(Argument::Expression(Expression::NumericLiteral(_))) - ) { + if !matches!(new_expr.arguments.first(), Some(Argument::NumericLiteral(_))) { return; } @@ -69,8 +66,10 @@ impl Rule for UninvokedArrayCallback { else { return; }; - if !matches!(call_expr.arguments.first(), Some(Argument::Expression(arg_expr)) if arg_expr.is_function()) - { + if !matches!( + call_expr.arguments.first(), + Some(Argument::FunctionExpression(_) | Argument::ArrowFunctionExpression(_)) + ) { return; } 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 a341f16d7b909..eafc21d02c4fc 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 @@ -1,9 +1,6 @@ pub mod return_checker; -use oxc_ast::{ - ast::{ChainElement, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -185,16 +182,12 @@ pub fn get_array_method_name<'a>( }; let callee = call.callee.get_inner_expression(); - let callee = match callee { - Expression::MemberExpression(member) => member, - Expression::ChainExpression(chain) => { - if let ChainElement::MemberExpression(member) = &chain.expression { - member - } else { - return None; - } - } - _ => return None, + let callee = if let Some(member) = callee.as_member_expression() { + member + } else if let Expression::ChainExpression(chain) = callee { + chain.expression.as_member_expression()? + } else { + return None; }; // Array.from diff --git a/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs b/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs index fb3ec528c5007..925a8c208210c 100644 --- a/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs +++ b/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs @@ -245,7 +245,7 @@ pub fn check_block_statement(block: &BlockStatement) -> StatementReturnStatus { #[cfg(test)] mod tests { use oxc_allocator::Allocator; - use oxc_ast::ast::{Declaration, Program}; + use oxc_ast::ast::Program; use oxc_parser::Parser; use oxc_span::SourceType; @@ -262,9 +262,7 @@ mod tests { let program = ret.program; let Program { body, .. } = program; let stmt = body.first().unwrap(); - let Statement::Declaration(Declaration::FunctionDeclaration(func)) = stmt else { - unreachable!() - }; + let Statement::FunctionDeclaration(func) = stmt else { unreachable!() }; let first_statement = &func.body.as_ref().unwrap().statements[0]; diff --git a/crates/oxc_linter/src/rules/eslint/for_direction.rs b/crates/oxc_linter/src/rules/eslint/for_direction.rs index b0baccff164f9..cf5591f3a2b91 100644 --- a/crates/oxc_linter/src/rules/eslint/for_direction.rs +++ b/crates/oxc_linter/src/rules/eslint/for_direction.rs @@ -105,10 +105,7 @@ fn get_update_direction(update: &Expression, counter: &IdentifierReference) -> U } // match add assign or subtract assign Expression::AssignmentExpression(assign) => { - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(id), - ) = &assign.left - { + if let AssignmentTarget::AssignmentTargetIdentifier(id) = &assign.left { if id.name != counter.name { return UNKNOWN; } diff --git a/crates/oxc_linter/src/rules/eslint/getter_return.rs b/crates/oxc_linter/src/rules/eslint/getter_return.rs index dc27bfc39bbf8..dd35cac022bbf 100644 --- a/crates/oxc_linter/src/rules/eslint/getter_return.rs +++ b/crates/oxc_linter/src/rules/eslint/getter_return.rs @@ -1,7 +1,7 @@ use oxc_ast::{ ast::{ - ChainElement, Expression, MemberExpression, MethodDefinitionKind, ObjectProperty, - PropertyKind, + match_member_expression, ChainElement, Expression, MemberExpression, MethodDefinitionKind, + ObjectProperty, PropertyKind, }, AstKind, }; @@ -90,9 +90,13 @@ impl GetterReturn { fn handle_actual_expression<'a>(callee: &'a Expression<'a>) -> bool { match callee.without_parenthesized() { - Expression::MemberExpression(me) => Self::handle_member_expression(me), + expr @ match_member_expression!(Expression) => { + Self::handle_member_expression(expr.to_member_expression()) + } Expression::ChainExpression(ce) => match &ce.expression { - ChainElement::MemberExpression(me) => Self::handle_member_expression(me), + match_member_expression!(ChainElement) => { + Self::handle_member_expression(ce.expression.to_member_expression()) + } ChainElement::CallExpression(_) => { false // todo: make a test for this } diff --git a/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs b/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs index 3672c37c5aee4..7ea55526f245e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs @@ -55,7 +55,8 @@ impl Rule for NoAsyncPromiseExecutor { if !new_expression.callee.is_specific_id("Promise") { return; } - let Some(Argument::Expression(expression)) = new_expression.arguments.first() else { + let Some(expression) = new_expression.arguments.first().and_then(Argument::as_expression) + else { return; }; let mut span = match expression.get_inner_expression() { diff --git a/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs b/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs index 8623e0be408f0..9e48ea420ad9a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Declaration, Statement, VariableDeclarationKind}, + ast::{Statement, VariableDeclarationKind}, AstKind, }; use oxc_diagnostics::{ @@ -54,30 +54,28 @@ impl Rule for NoCaseDeclarations { let consequent = &switch_case.consequent; for stmt in consequent { - if let Statement::Declaration(dcl) = stmt { - match dcl { - Declaration::FunctionDeclaration(d) => { - let start = d.span.start; - let end = start + 8; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); - } - Declaration::ClassDeclaration(d) => { - let start = d.span.start; - let end = start + 5; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); - } - Declaration::VariableDeclaration(var) if var.kind.is_lexical() => { - let start = var.span.start; - let end = match var.kind { - VariableDeclarationKind::Var => unreachable!(), - VariableDeclarationKind::Const => 5, - VariableDeclarationKind::Let => 3, - }; - let end = start + end; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); - } - _ => {} + match stmt { + Statement::FunctionDeclaration(d) => { + let start = d.span.start; + let end = start + 8; + ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); } + Statement::ClassDeclaration(d) => { + let start = d.span.start; + let end = start + 5; + ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); + } + Statement::VariableDeclaration(var) if var.kind.is_lexical() => { + let start = var.span.start; + let end = match var.kind { + VariableDeclarationKind::Var => unreachable!(), + VariableDeclarationKind::Const => 5, + VariableDeclarationKind::Let => 3, + }; + let end = start + end; + ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); + } + _ => {} }; } } diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index 1526f7d64f55f..8e7196d57844c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -73,7 +73,7 @@ impl Rule for NoConsole { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::CallExpression(call_expr) = node.kind() { - if let Expression::MemberExpression(mem) = &call_expr.callee { + if let Some(mem) = call_expr.callee.as_member_expression() { if let Expression::Identifier(ident) = mem.object() { if ctx.semantic().is_reference_to_global_variable(ident) && ident.name == "console" diff --git a/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs b/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs index 56506c1b708d3..8df5e757cf3b4 100644 --- a/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs +++ b/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs @@ -240,12 +240,7 @@ impl NoConstantBinaryExpression { | Expression::FunctionExpression(_) => true, Expression::ArrayExpression(array_expr) => { array_expr.elements.is_empty() - || array_expr - .elements - .iter() - .filter(|e| matches!(e, ArrayExpressionElement::Expression(_))) - .count() - > 1 + || array_expr.elements.iter().filter(|e| e.is_expression()).count() > 1 } Expression::UnaryExpression(unary_expr) => match unary_expr.operator { UnaryOperator::Void | UnaryOperator::Typeof => true, diff --git a/crates/oxc_linter/src/rules/eslint/no_control_regex.rs b/crates/oxc_linter/src/rules/eslint/no_control_regex.rs index fcb2fa54a1557..0bc45bc18884d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_control_regex.rs +++ b/crates/oxc_linter/src/rules/eslint/no_control_regex.rs @@ -1,6 +1,6 @@ use lazy_static::lazy_static; use oxc_ast::{ - ast::{Argument, Expression, RegExpFlags}, + ast::{Argument, RegExpFlags}, AstKind, }; use oxc_diagnostics::{ @@ -151,7 +151,8 @@ struct RegexPatternData<'a> { /// Note that flags are represented by a `u8` and therefore safely clonable /// with low performance overhead. flags: Option, - /// The pattern's span. For [`Expression::NewExpression`]s and [`Expression::CallExpression`]s, + /// The pattern's span. For [`oxc_ast::ast::Expression::NewExpression`]s + /// and [`oxc_ast::ast::Expression::CallExpression`]s, /// this will match the entire new/call expression. /// /// Note that spans are 8 bytes and safely clonable with low performance overhead @@ -185,8 +186,7 @@ fn regex_pattern<'a>(node: &AstNode<'a>) -> Option> { // where the first one is a string literal // note: improvements required for strings used via identifier // references - if let Argument::Expression(Expression::StringLiteral(pattern)) = &expr.arguments[0] - { + if let Argument::StringLiteral(pattern) = &expr.arguments[0] { // get pattern from arguments. Missing or non-string arguments // will be runtime errors, but are not covered by this rule. // Note that we're intentionally reporting the entire "new @@ -214,8 +214,7 @@ fn regex_pattern<'a>(node: &AstNode<'a>) -> Option> { // where the first one is a string literal // note: improvements required for strings used via identifier // references - if let Argument::Expression(Expression::StringLiteral(pattern)) = &expr.arguments[0] - { + if let Argument::StringLiteral(pattern) = &expr.arguments[0] { // get pattern from arguments. Missing or non-string arguments // will be runtime errors, but are not covered by this rule. // Note that we're intentionally reporting the entire "new diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index dc31a6f4f0d50..10ad075b0257c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -83,8 +83,12 @@ impl Rule for NoEval { loop { let (new_object, name) = match object { - Some(Expression::MemberExpression(member)) => { - (Some(member.object().get_inner_expression()), member.static_property_name()) + Some(object) if object.is_member_expression() => { + let member_expr = object.to_member_expression(); + ( + Some(member_expr.object().get_inner_expression()), + member_expr.static_property_name(), + ) } Some(Expression::Identifier(ident)) => (None, Some(ident.name.as_str())), Some(Expression::ThisExpression(_)) => (None, Some("this")), diff --git a/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs b/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs index e7aa6dbaed8dd..d1ce266b882f1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use oxc_ast::{ast::Argument, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -138,14 +138,14 @@ fn is_bool_fn_or_constructor_call(node: &AstNode) -> bool { fn is_first_arg(node: &AstNode, parent: &AstNode) -> bool { match parent.kind() { AstKind::CallExpression(expr) => expr.arguments.first().map_or(false, |arg| { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { expr.without_parenthesized().span() == node.kind().span() } else { false } }), AstKind::NewExpression(expr) => expr.arguments.first().map_or(false, |arg| { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { expr.without_parenthesized().span() == node.kind().span() } else { false diff --git a/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs b/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs index 9bc141a8c886e..bcce8e1a73c40 100644 --- a/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs +++ b/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, IdentifierReference, MemberExpression}, + ast::{match_member_expression, Expression, IdentifierReference, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -69,9 +69,7 @@ fn is_global_obj(s: &str) -> bool { NON_CALLABLE_GLOBALS.contains(&s) } -fn global_this_member<'a>( - expr: &'a oxc_allocator::Box<'_, MemberExpression<'_>>, -) -> Option<&'a str> { +fn global_this_member<'a>(expr: &'a MemberExpression<'_>) -> Option<&'a str> { if expr.object().is_specific_id(GLOBAL_THIS) { expr.static_property_name() } else { @@ -112,8 +110,8 @@ fn resolve_global_binding<'a, 'b: 'a>( resolve_global_binding(parent_ident, decl_scope, ctx) } // handles "let a = globalThis.JSON; let b = a; a();" - Some(Expression::MemberExpression(parent_expr)) => { - global_this_member(parent_expr) + Some(parent_expr) if parent_expr.is_member_expression() => { + global_this_member(parent_expr.to_member_expression()) } _ => None, } @@ -146,9 +144,9 @@ impl Rule for NoObjCalls { } } - Expression::MemberExpression(expr) => { + match_member_expression!(Expression) => { // handle new globalThis.Math(), globalThis.Math(), etc - if let Some(global_member) = global_this_member(expr) { + if let Some(global_member) = global_this_member(callee.to_member_expression()) { if is_global_obj(global_member) { ctx.diagnostic(NoObjCallsDiagnostic(global_member.into(), span)); } diff --git a/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs b/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs index d47d03934e467..9286dfc3ecb5c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs +++ b/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs @@ -1,6 +1,6 @@ use oxc_allocator::Vec; use oxc_ast::{ - ast::{Argument, CallExpression, Expression, NewExpression, RegExpLiteral}, + ast::{Argument, CallExpression, NewExpression, RegExpLiteral}, AstKind, }; use oxc_diagnostics::{ @@ -87,13 +87,13 @@ impl NoRegexSpaces { } fn find_expr_to_report(args: &Vec<'_, Argument<'_>>) -> Option { - if let Some(Argument::Expression(expr)) = args.get(1) { + if let Some(expr) = args.get(1).and_then(Argument::as_expression) { if !expr.is_string_literal() { return None; // skip on indeterminate flag, e.g. RegExp('a b', flags) } } - if let Some(Argument::Expression(Expression::StringLiteral(pattern))) = args.first() { + if let Some(Argument::StringLiteral(pattern)) = args.first() { if Self::has_exempted_char_class(&pattern.value) { return None; // skip spaces inside char class, e.g. RegExp('[ ]') } diff --git a/crates/oxc_linter/src/rules/eslint/no_self_assign.rs b/crates/oxc_linter/src/rules/eslint/no_self_assign.rs index 371433cc7eaf0..c75836516c727 100644 --- a/crates/oxc_linter/src/rules/eslint/no_self_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_self_assign.rs @@ -1,8 +1,8 @@ use oxc_ast::{ ast::{ - ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault, - AssignmentTargetPattern, AssignmentTargetProperty, Expression, MemberExpression, - ObjectProperty, ObjectPropertyKind, SimpleAssignmentTarget, + match_assignment_target, match_simple_assignment_target, ArrayExpressionElement, + AssignmentTarget, AssignmentTargetMaybeDefault, AssignmentTargetProperty, Expression, + MemberExpression, ObjectProperty, ObjectPropertyKind, SimpleAssignmentTarget, }, AstKind, }; @@ -84,7 +84,8 @@ impl NoSelfAssign { ctx: &LintContext<'a>, ) { match left { - AssignmentTarget::SimpleAssignmentTarget(simple_assignment_target) => { + match_simple_assignment_target!(AssignmentTarget) => { + let simple_assignment_target = left.to_simple_assignment_target(); if let Expression::Identifier(id2) = right.without_parenthesized() { let self_assign = matches!(simple_assignment_target.get_expression(), Some(Expression::Identifier(id1)) if id1.name == id2.name) || matches!(simple_assignment_target, SimpleAssignmentTarget::AssignmentTargetIdentifier(id1) if id1.name == id2.name); @@ -93,11 +94,17 @@ impl NoSelfAssign { ctx.diagnostic(NoSelfAssignDiagnostic(right.span())); } } + + if let Some(member_target) = simple_assignment_target.as_member_expression() { + if let Some(member_expr) = right.without_parenthesized().get_member_expr() { + if self.is_member_expression_same_reference(member_expr, member_target) { + ctx.diagnostic(NoSelfAssignDiagnostic(member_expr.span())); + } + } + } } - AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ArrayAssignmentTarget(array_pattern), - ) => { + AssignmentTarget::ArrayAssignmentTarget(array_pattern) => { if let Expression::ArrayExpression(array_expr) = right.without_parenthesized() { let end = std::cmp::min(array_pattern.elements.len(), array_expr.elements.len()); @@ -106,16 +113,11 @@ impl NoSelfAssign { let left = array_pattern.elements[i].as_ref(); let right = &array_expr.elements[i]; - let left_target = match left { - Some(AssignmentTargetMaybeDefault::AssignmentTarget(target)) => { - Some(target) - } - _ => None, - }; - - if let Some(left_target) = left_target { - if let ArrayExpressionElement::Expression(expr) = right { - self.each_self_assignment(left_target, expr, ctx); + if let Some(left) = left { + if let Some(left_target) = left.as_assignment_target() { + if let Some(expr) = right.as_expression() { + self.each_self_assignment(left_target, expr, ctx); + } } } @@ -129,9 +131,7 @@ impl NoSelfAssign { } } - AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ObjectAssignmentTarget(object_pattern), - ) => { + AssignmentTarget::ObjectAssignmentTarget(object_pattern) => { if let Expression::ObjectExpression(object_expr) = right.get_inner_expression() { if !object_expr.properties.is_empty() { let mut start_j = 0; @@ -163,16 +163,6 @@ impl NoSelfAssign { } } } - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(member_target), - ) = &left - { - if let Some(member_expr) = right.without_parenthesized().get_member_expr() { - if self.is_member_expression_same_reference(member_expr, member_target) { - ctx.diagnostic(NoSelfAssignDiagnostic(member_expr.span())); - } - } - } } fn is_same_reference<'a>(&self, left: &'a Expression<'a>, right: &'a Expression<'a>) -> bool { @@ -187,20 +177,14 @@ impl NoSelfAssign { return true; } - match (left, right) { - (Expression::Identifier(id1), Expression::Identifier(id2)) => id1.name == id2.name, - (Expression::MemberExpression(member1), Expression::MemberExpression(member2)) => { - self.is_member_expression_same_reference(member1, member2) - } - _ => { - if let (Some(member1), Some(member2)) = - (left.get_member_expr(), right.get_member_expr()) - { - self.is_member_expression_same_reference(member1, member2) - } else { - false - } - } + if let (Expression::Identifier(id1), Expression::Identifier(id2)) = (left, right) { + return id1.name == id2.name; + } + + if let (Some(member1), Some(member2)) = (left.get_member_expr(), right.get_member_expr()) { + self.is_member_expression_same_reference(member1, member2) + } else { + false } } @@ -265,7 +249,9 @@ impl NoSelfAssign { } AssignmentTargetProperty::AssignmentTargetPropertyProperty(property) => { let left = match &property.binding { - AssignmentTargetMaybeDefault::AssignmentTarget(target) => target, + binding @ match_assignment_target!(AssignmentTargetMaybeDefault) => { + binding.to_assignment_target() + } AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(_) => { return; } diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs index e2d91a849e680..212cde835b883 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, AssignmentTarget, Expression}, + ast::{match_assignment_target_pattern, Argument, AssignmentTarget, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -82,7 +82,7 @@ impl Rule for NoUnsafeOptionalChaining { Self::check_unsafe_usage(&expr.callee, ctx); } AstKind::AssignmentExpression(expr) => { - if matches!(expr.left, AssignmentTarget::AssignmentTargetPattern(_)) { + if matches!(expr.left, match_assignment_target_pattern!(AssignmentTarget)) { Self::check_unsafe_usage(&expr.right, ctx); } if expr.operator.is_arithmetic() { @@ -123,7 +123,7 @@ impl Rule for NoUnsafeOptionalChaining { } } AstKind::AssignmentTargetWithDefault(target) => { - if matches!(target.binding, AssignmentTarget::AssignmentTargetPattern(_)) { + if matches!(target.binding, match_assignment_target_pattern!(AssignmentTarget)) { Self::check_unsafe_usage(&target.init, ctx); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs b/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs index c6a610ab1cc62..d8241bb31a6a1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs @@ -1,7 +1,5 @@ use oxc_ast::{ - ast::{ - AssignmentTarget, AssignmentTargetPattern, AssignmentTargetProperty, BindingPatternKind, - }, + ast::{AssignmentTarget, AssignmentTargetProperty, BindingPatternKind}, AstKind, }; use oxc_diagnostics::{ @@ -116,8 +114,8 @@ impl Rule for NoUselessRename { } } } - AstKind::AssignmentTarget(AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ObjectAssignmentTarget(object_assignment_target), + AstKind::AssignmentTarget(AssignmentTarget::ObjectAssignmentTarget( + object_assignment_target, )) => { if self.ignore_destructuring { return; diff --git a/crates/oxc_linter/src/rules/eslint/use_isnan.rs b/crates/oxc_linter/src/rules/eslint/use_isnan.rs index 1c587523e0485..0d5ef82dc32d5 100644 --- a/crates/oxc_linter/src/rules/eslint/use_isnan.rs +++ b/crates/oxc_linter/src/rules/eslint/use_isnan.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, ChainElement, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -106,7 +103,7 @@ impl Rule for UseIsnan { // Match target array prototype methods whose only argument is NaN if let Some(method) = is_target_callee(&call.callee) { if call.arguments.len() == 1 { - if let Some(Argument::Expression(expr)) = &call.arguments.first() { + if let Some(expr) = call.arguments[0].as_expression() { if is_nan_identifier(expr) { ctx.diagnostic(UseIsnanDiagnostic::IndexOfNaN(method, expr.span())); } @@ -145,21 +142,22 @@ fn is_nan_identifier<'a>(expr: &'a Expression<'a>) -> bool { fn is_target_callee<'a>(callee: &'a Expression<'a>) -> Option<&'static str> { const TARGET_METHODS: [&str; 2] = ["indexOf", "lastIndexOf"]; let callee = callee.get_inner_expression(); - match callee { - Expression::MemberExpression(expr) => expr.static_property_name().and_then(|property| { + + if let Some(expr) = callee.as_member_expression() { + return expr.static_property_name().and_then(|property| { TARGET_METHODS.iter().find(|method| **method == property).copied() - }), - Expression::ChainExpression(chain) => { - if let ChainElement::MemberExpression(expr) = &chain.expression { - expr.static_property_name().and_then(|property| { - TARGET_METHODS.iter().find(|method| **method == property).copied() - }) - } else { - None - } + }); + } + + if let Expression::ChainExpression(chain) = callee { + if let Some(expr) = chain.expression.as_member_expression() { + return expr.static_property_name().and_then(|property| { + TARGET_METHODS.iter().find(|method| **method == property).copied() + }); } - _ => None, } + + None } #[test] diff --git a/crates/oxc_linter/src/rules/import/no_amd.rs b/crates/oxc_linter/src/rules/import/no_amd.rs index 03e098c0041f0..89ef60a8502dd 100644 --- a/crates/oxc_linter/src/rules/import/no_amd.rs +++ b/crates/oxc_linter/src/rules/import/no_amd.rs @@ -52,8 +52,7 @@ impl Rule for NoAmd { return; } - if let Argument::Expression(Expression::ArrayExpression(_)) = call_expr.arguments[0] - { + if let Argument::ArrayExpression(_) = call_expr.arguments[0] { ctx.diagnostic(NoAmdDiagnostic( identifier.span, identifier.name.to_compact_str(), diff --git a/crates/oxc_linter/src/rules/jest/expect_expect.rs b/crates/oxc_linter/src/rules/jest/expect_expect.rs index 690dbbcb95b8b..eda55d8f35b5e 100644 --- a/crates/oxc_linter/src/rules/jest/expect_expect.rs +++ b/crates/oxc_linter/src/rules/jest/expect_expect.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, CallExpression, Expression, Statement}, + ast::{CallExpression, Expression, Statement}, AstKind, }; use oxc_diagnostics::{ @@ -119,7 +119,7 @@ fn run<'a>( &[JestFnKind::General(JestGeneralFnKind::Test)], ) || rule.additional_test_block_functions.contains(&name) { - if let Expression::MemberExpression(member_expr) = &call_expr.callee { + if let Some(member_expr) = call_expr.callee.as_member_expression() { let Some(property_name) = member_expr.static_property_name() else { return; }; @@ -143,7 +143,7 @@ fn check_arguments<'a>( ctx: &LintContext<'a>, ) -> bool { call_expr.arguments.iter().any(|argument| { - if let Argument::Expression(expr) = argument { + if let Some(expr) = argument.as_expression() { return check_assert_function_used(expr, assert_function_names, ctx); } false 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 48c43ff7fc519..2ef41084c6ada 100644 --- a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs +++ b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -113,7 +113,7 @@ fn check_parents<'a>( return in_conditional; } - if let Expression::MemberExpression(member_expr) = &call_expr.callee { + if let Some(member_expr) = call_expr.callee.as_member_expression() { if member_expr.static_property_name() == Some("catch") { return check_parents(parent_node, id_nodes_mapping, ctx, true); } diff --git a/crates/oxc_linter/src/rules/jest/no_done_callback.rs b/crates/oxc_linter/src/rules/jest/no_done_callback.rs index 81f5eae84a21d..c2ac21378de49 100644 --- a/crates/oxc_linter/src/rules/jest/no_done_callback.rs +++ b/crates/oxc_linter/src/rules/jest/no_done_callback.rs @@ -111,16 +111,14 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) return; } - let Some(Argument::Expression(expr)) = - find_argument_of_callback(call_expr, is_jest_each, kind) - else { + let Some(arg) = find_argument_of_callback(call_expr, is_jest_each, kind) else { return; }; let callback_arg_index = usize::from(is_jest_each); - match expr { - Expression::FunctionExpression(func_expr) => { + match arg { + Argument::FunctionExpression(func_expr) => { if func_expr.params.parameters_count() != 1 + callback_arg_index { return; } @@ -135,7 +133,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) ctx.diagnostic(NoDoneCallbackDiagnostic::NoDoneCallback(span)); } - Expression::ArrowFunctionExpression(arrow_expr) => { + Argument::ArrowFunctionExpression(arrow_expr) => { if arrow_expr.params.parameters_count() != 1 + callback_arg_index { return; } 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 7339df199dea1..db433bd4e9d31 100644 --- a/crates/oxc_linter/src/rules/jest/no_identical_title.rs +++ b/crates/oxc_linter/src/rules/jest/no_identical_title.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use oxc_ast::{ - ast::{Argument, CallExpression, Expression}, + ast::{Argument, CallExpression}, AstKind, }; use oxc_diagnostics::{ @@ -135,10 +135,10 @@ fn filter_and_process_jest_result<'a>( let parent_id = get_closest_block(possible_jest_node.node, ctx)?; match call_expr.arguments.first() { - Some(Argument::Expression(Expression::StringLiteral(string_lit))) => { + Some(Argument::StringLiteral(string_lit)) => { Some((string_lit.span, &string_lit.value, kind, parent_id)) } - Some(Argument::Expression(Expression::TemplateLiteral(template_lit))) => { + Some(Argument::TemplateLiteral(template_lit)) => { template_lit.quasi().map(|quasi| (template_lit.span, quasi, kind, parent_id)) } _ => None, diff --git a/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs b/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs index c16721a788b7d..017f9cdc8d442 100644 --- a/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs +++ b/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Argument, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -86,7 +83,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) // Check all since the optional 'propertyMatchers' argument might be present // `.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)` for arg in jest_fn_call.args { - if let Argument::Expression(Expression::TemplateLiteral(template_lit)) = arg { + if let Argument::TemplateLiteral(template_lit) = arg { if !template_lit.expressions.is_empty() { ctx.diagnostic(NoInterpolationInSnapshotsDiagnostic(template_lit.span)); } diff --git a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs index ef7fa70294cdb..1add8186a0567 100644 --- a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs +++ b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs @@ -1,7 +1,6 @@ use oxc_ast::{ ast::{ - AssignmentExpression, AssignmentTarget, CallExpression, Expression, MemberExpression, - SimpleAssignmentTarget, + AssignmentExpression, CallExpression, Expression, MemberExpression, SimpleAssignmentTarget, }, AstKind, }; @@ -75,9 +74,10 @@ impl Rule for NoJasmineGlobals { } fn diagnostic_assign_expr<'a>(expr: &'a AssignmentExpression<'a>, ctx: &LintContext) { - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(member_expr), - ) = &expr.left + if let Some(member_expr) = expr + .left + .as_simple_assignment_target() + .and_then(SimpleAssignmentTarget::as_member_expression) { let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { return }; @@ -100,7 +100,7 @@ fn diagnostic_assign_expr<'a>(expr: &'a AssignmentExpression<'a>, ctx: &LintCont } fn diagnostic_call_expr<'a>(expr: &'a CallExpression<'a>, ctx: &LintContext) { - if let Expression::MemberExpression(member_expr) = &expr.callee { + if let Some(member_expr) = expr.callee.as_member_expression() { let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { return }; JasmineProperty::from_str(property_name).map_or_else( 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 c49ccec4efdd6..11454c5e339e2 100644 --- a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs +++ b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs @@ -1,9 +1,6 @@ use std::path::PathBuf; -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Argument, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -65,9 +62,7 @@ impl Rule for NoMocksImport { return; }; - let Some(Argument::Expression(Expression::StringLiteral(string_literal))) = - call_expr.arguments.first() - else { + let Some(Argument::StringLiteral(string_literal)) = call_expr.arguments.first() else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_jest_methods.rs b/crates/oxc_linter/src/rules/jest/no_restricted_jest_methods.rs index 08ca043109243..2d2a4e188532b 100644 --- a/crates/oxc_linter/src/rules/jest/no_restricted_jest_methods.rs +++ b/crates/oxc_linter/src/rules/jest/no_restricted_jest_methods.rs @@ -7,7 +7,7 @@ use crate::{ }, }; -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -113,7 +113,7 @@ impl NoRestrictedJestMethods { return; } - let Expression::MemberExpression(mem_expr) = &call_expr.callee else { + let Some(mem_expr) = call_expr.callee.as_member_expression() else { return; }; let Some(property_name) = mem_expr.static_property_name() else { diff --git a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs index e940c2e3e05cb..3223bae3136e3 100644 --- a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs +++ b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs @@ -6,7 +6,7 @@ use crate::{ use oxc_allocator::Box as OBox; use oxc_ast::{ - ast::{Argument, CallExpression, Expression, FunctionBody, Statement}, + ast::{CallExpression, Expression, FunctionBody, Statement}, AstKind, }; use oxc_diagnostics::{ @@ -75,7 +75,7 @@ fn check_call_expression<'a>( } for argument in &call_expr.arguments { - let Argument::Expression(arg_expr) = argument else { + let Some(arg_expr) = argument.as_expression() else { continue; }; match arg_expr { @@ -106,7 +106,7 @@ fn check_test_return_statement<'a>(func_body: &OBox<'_, FunctionBody<'a>>, ctx: let Some(Expression::CallExpression(call_expr)) = &stmt.argument else { return; }; - let Expression::MemberExpression(mem_expr) = &call_expr.callee else { + let Some(mem_expr) = call_expr.callee.as_member_expression() else { return; }; let Expression::CallExpression(mem_call_expr) = mem_expr.object() else { diff --git a/crates/oxc_linter/src/rules/jest/no_untyped_mock_factory.rs b/crates/oxc_linter/src/rules/jest/no_untyped_mock_factory.rs index 2a730d54b0199..3943dcbe89f8a 100644 --- a/crates/oxc_linter/src/rules/jest/no_untyped_mock_factory.rs +++ b/crates/oxc_linter/src/rules/jest/no_untyped_mock_factory.rs @@ -112,7 +112,7 @@ impl NoUntypedMockFactory { let AstKind::CallExpression(call_expr) = node.kind() else { return; }; - let Expression::MemberExpression(mem_expr) = &call_expr.callee else { + let Some(mem_expr) = call_expr.callee.as_member_expression() else { return; }; let Some((property_span, property_name)) = mem_expr.static_property_info() else { @@ -135,7 +135,7 @@ impl NoUntypedMockFactory { let Some(name_node) = call_expr.arguments.first() else { return; }; - let Argument::Expression(expr) = name_node else { + let Some(expr) = name_node.as_expression() else { return; }; @@ -166,7 +166,7 @@ impl NoUntypedMockFactory { } fn has_return_type(argument: &Argument) -> bool { - let Argument::Expression(expr) = argument else { + let Some(expr) = argument.as_expression() else { return false; }; diff --git a/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs b/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs index 5e11f5bfd50f6..73b66155d196c 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs @@ -89,12 +89,12 @@ impl PreferComparisonMatcher { let Expression::CallExpression(parent_call_expr) = parent_node else { return; }; - let Some(Argument::Expression(Expression::BinaryExpression(binary_expr))) = - parent_call_expr.arguments.first() + let Some(Argument::BinaryExpression(binary_expr)) = parent_call_expr.arguments.first() else { return; }; - let Some(Argument::Expression(first_matcher_arg)) = parse_expect_jest_fn.args.first() + let Some(first_matcher_arg) = + parse_expect_jest_fn.args.first().and_then(Argument::as_expression) else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs b/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs index 405c1a00eecf6..972bf8e4abc20 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs @@ -78,7 +78,7 @@ impl PreferEqualityMatcher { return; }; - let Argument::Expression(Expression::BinaryExpression(binary_expr)) = argument else { + let Argument::BinaryExpression(binary_expr) = argument else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs b/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs index 483f43da24666..363373f9882a6 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs @@ -101,7 +101,7 @@ impl PreferExpectResolves { let Some(argument) = call_expr.arguments.first() else { return; }; - let Argument::Expression(Expression::AwaitExpression(await_expr)) = argument else { + let Argument::AwaitExpression(await_expr) = argument else { return; }; let Some(ident) = call_expr.callee.get_identifier_reference() else { @@ -122,7 +122,7 @@ impl PreferExpectResolves { ) -> String { let mut formatter = ctx.codegen(); let first = call_expr.arguments.first().unwrap(); - let Argument::Expression(Expression::AwaitExpression(await_expr)) = first else { + let Argument::AwaitExpression(await_expr) = first else { return formatter.into_source_text(); }; diff --git a/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs b/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs index c6c54efbb8c96..a06b0e6f5b09e 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Argument, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -199,11 +196,11 @@ impl PreferLowercaseTitle { return; } - let Some(Argument::Expression(expr)) = call_expr.arguments.first() else { + let Some(arg) = call_expr.arguments.first() else { return; }; - if let Expression::StringLiteral(string_expr) = expr { + if let Argument::StringLiteral(string_expr) = arg { if string_expr.value.is_empty() || self.allowed_prefixes.iter().any(|name| string_expr.value.starts_with(name)) { @@ -229,7 +226,7 @@ impl PreferLowercaseTitle { ) }, ); - } else if let Expression::TemplateLiteral(template_expr) = expr { + } else if let Argument::TemplateLiteral(template_expr) = arg { let Some(template_string) = template_expr.quasi() else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs b/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs index d7a820354c6cc..aae1739869574 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs @@ -61,7 +61,7 @@ impl Rule for PreferMockPromiseShorthand { let AstKind::CallExpression(call_expr) = node.kind() else { return; }; - let Expression::MemberExpression(mem_expr) = &call_expr.callee else { + let Some(mem_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -72,7 +72,7 @@ impl Rule for PreferMockPromiseShorthand { let Some((property_span, property_name)) = mem_expr.static_property_info() else { return; }; - let Some(Argument::Expression(expr)) = call_expr.arguments.first() else { + let Some(expr) = call_expr.arguments.first().and_then(Argument::as_expression) else { return; }; let is_once = property_name.ends_with("Once"); @@ -174,7 +174,7 @@ impl PreferMockPromiseShorthand { content.print_str(b"undefined"); } else { for argument in &call_expr.arguments { - if let Argument::Expression(expr) = argument { + if let Some(expr) = argument.as_expression() { content.print_expression(expr); } } diff --git a/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs b/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs index 3688073b73de5..d5ca2709dbae3 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs @@ -1,7 +1,7 @@ use oxc_ast::{ ast::{ - Argument, AssignmentExpression, AssignmentTarget, CallExpression, Expression, - MemberExpression, SimpleAssignmentTarget, + Argument, AssignmentExpression, CallExpression, Expression, MemberExpression, + SimpleAssignmentTarget, }, AstKind, }; @@ -66,9 +66,9 @@ impl Rule for PreferSpyOn { let left = &assign_expr.left; let right = &assign_expr.right; - let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(left_assign), - ) = left + let Some(left_assign) = left + .as_simple_assignment_target() + .and_then(SimpleAssignmentTarget::as_member_expression) else { return; }; @@ -77,13 +77,14 @@ impl Rule for PreferSpyOn { Expression::CallExpression(call_expr) => { Self::check_and_fix(assign_expr, call_expr, left_assign, node, ctx); } - Expression::MemberExpression(mem_expr) => { - let Expression::CallExpression(call_expr) = mem_expr.object() else { - return; - }; - Self::check_and_fix(assign_expr, call_expr, left_assign, node, ctx); + _ => { + if let Some(mem_expr) = right.as_member_expression() { + let Expression::CallExpression(call_expr) = mem_expr.object() else { + return; + }; + Self::check_and_fix(assign_expr, call_expr, left_assign, node, ctx); + } } - _ => (), } } } @@ -167,7 +168,7 @@ impl PreferSpyOn { formatter.print_str(b".mockImplementation("); - if let Some(Argument::Expression(expr)) = Self::get_jest_fn_call(call_expr) { + if let Some(expr) = Self::get_jest_fn_call(call_expr) { formatter.print_expression(expr); } @@ -175,15 +176,16 @@ impl PreferSpyOn { formatter.into_source_text() } - fn get_jest_fn_call<'a>(call_expr: &'a CallExpression<'a>) -> Option<&'a Argument<'a>> { + fn get_jest_fn_call<'a>(call_expr: &'a CallExpression<'a>) -> Option<&'a Expression<'a>> { let is_jest_fn = get_node_name(&call_expr.callee) == "jest.fn"; if is_jest_fn { - return call_expr.arguments.first(); + return call_expr.arguments.first().and_then(Argument::as_expression); } match &call_expr.callee { - Expression::MemberExpression(mem_expr) => { + expr if expr.is_member_expression() => { + let mem_expr = expr.to_member_expression(); if let Some(call_expr) = Self::find_mem_expr(mem_expr) { return Self::get_jest_fn_call(call_expr); } @@ -194,11 +196,17 @@ impl PreferSpyOn { } } - fn find_mem_expr<'a>(mem_expr: &'a MemberExpression<'a>) -> Option<&'a CallExpression<'a>> { - match mem_expr.object() { - Expression::CallExpression(call_expr) => Some(call_expr), - Expression::MemberExpression(mem_expr) => Self::find_mem_expr(mem_expr), - _ => None, + fn find_mem_expr<'a>(mut mem_expr: &'a MemberExpression<'a>) -> Option<&'a CallExpression<'a>> { + loop { + let object = mem_expr.object(); + if let Expression::CallExpression(call_expr) = object { + return Some(call_expr); + } + if let Some(object_mem_expr) = object.as_member_expression() { + mem_expr = object_mem_expr; + } else { + return None; + } } } } diff --git a/crates/oxc_linter/src/rules/jest/prefer_to_be.rs b/crates/oxc_linter/src/rules/jest/prefer_to_be.rs index 8ea069f7adf37..61139dbd46f43 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_be.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_be.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, CallExpression, Expression, MemberExpression}, + ast::{Argument, CallExpression, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -132,7 +132,8 @@ impl PreferToBe { return; } - let Some(Argument::Expression(arg_expr)) = jest_expect_fn_call.args.first() else { + let Some(arg_expr) = jest_expect_fn_call.args.first().and_then(Argument::as_expression) + else { return; }; let first_matcher_arg = arg_expr.get_inner_expression(); @@ -210,11 +211,15 @@ impl PreferToBe { ) { let span = matcher.span; let end = call_expr.span.end; - let Some(Expression::MemberExpression(mem_expr)) = matcher.parent else { - return; + + let is_cmp_mem_expr = match matcher.parent { + Some(Expression::ComputedMemberExpression(_)) => true, + Some(Expression::StaticMemberExpression(_) | Expression::PrivateFieldExpression(_)) => { + false + } + _ => return, }; - let is_cmp_mem_expr = matches!(&**mem_expr, MemberExpression::ComputedMemberExpression(_)); let modifiers = jest_expect_fn_call.modifiers(); let maybe_not_modifier = modifiers.iter().find(|modifier| modifier.is_name_equal("not")); diff --git a/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs b/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs index e346fe9191a0e..e35e799dcba51 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs @@ -90,7 +90,8 @@ impl PreferToContain { return; } - let Some(Argument::Expression(jest_expect_first_arg)) = jest_expect_fn_call.args.first() + let Some(jest_expect_first_arg) = + jest_expect_fn_call.args.first().and_then(Argument::as_expression) else { return; }; @@ -111,8 +112,7 @@ impl PreferToContain { let Some(first_argument) = expect_call_expr.arguments.first() else { return; }; - let Argument::Expression(Expression::CallExpression(includes_call_expr)) = first_argument - else { + let Argument::CallExpression(includes_call_expr) = first_argument else { return; }; @@ -126,7 +126,7 @@ impl PreferToContain { } fn is_fixable_includes_call_expression(call_expr: &CallExpression) -> bool { - let Expression::MemberExpression(mem_expr) = &call_expr.callee else { + let Some(mem_expr) = call_expr.callee.as_member_expression() else { return false; }; @@ -136,7 +136,7 @@ impl PreferToContain { // handle "expect(a.includes(b,c))" && call_expr.arguments.len() == 1 // handle "expect(a.includes(...[]))" - && matches!(call_expr.arguments.first(), Some(Argument::Expression(_))) + && call_expr.arguments[0].is_expression() } } diff --git a/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs b/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs index 116e65bc8e965..e5b3c5cbfa889 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, CallExpression, Expression, MemberExpression}, + ast::{match_member_expression, CallExpression, Expression, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -76,16 +76,17 @@ impl PreferToHaveLength { else { return; }; - let Expression::MemberExpression(static_expr) = &call_expr.callee else { + let Some(static_expr) = call_expr.callee.as_member_expression() else { return; }; match static_expr.object() { - Expression::MemberExpression(mem_expr) => { + expr @ match_member_expression!(Expression) => { + let mem_expr = expr.to_member_expression(); let Expression::CallExpression(expr_call_expr) = mem_expr.object() else { return; }; - match &**mem_expr { + match mem_expr { MemberExpression::ComputedMemberExpression(_) => Self::check_and_fix( call_expr, expr_call_expr, @@ -130,7 +131,7 @@ impl PreferToHaveLength { let Some(argument) = expr_call_expr.arguments.first() else { return; }; - let Argument::Expression(Expression::MemberExpression(static_mem_expr)) = argument else { + let Some(static_mem_expr) = argument.as_member_expression() else { return; }; // Get property `name` field from expect(file.NAME) call diff --git a/crates/oxc_linter/src/rules/jest/prefer_todo.rs b/crates/oxc_linter/src/rules/jest/prefer_todo.rs index 2391b9fa89c68..9dda938ce62a7 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_todo.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_todo.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, CallExpression, Expression, MemberExpression}, + ast::{Argument, CallExpression, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -95,7 +95,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) } fn filter_todo_case(expr: &CallExpression) -> bool { - if let Expression::MemberExpression(mem_expr) = &expr.callee { + if let Some(mem_expr) = expr.callee.as_member_expression() { if let Some(name) = mem_expr.static_property_name() { return name == "todo"; } @@ -112,16 +112,13 @@ fn should_filter_case(expr: &CallExpression) -> bool { } fn is_string_type(arg: &Argument) -> bool { - matches!( - arg, - Argument::Expression(Expression::StringLiteral(_) | Expression::TemplateLiteral(_)) - ) + matches!(arg, Argument::StringLiteral(_) | Argument::TemplateLiteral(_)) } fn is_empty_function(expr: &CallExpression) -> bool { match &expr.arguments[1] { - Argument::Expression(Expression::ArrowFunctionExpression(arrow)) => arrow.body.is_empty(), - Argument::Expression(Expression::FunctionExpression(func)) => { + Argument::ArrowFunctionExpression(arrow) => arrow.body.is_empty(), + Argument::FunctionExpression(func) => { let Some(func_body) = &func.body else { return false; }; @@ -132,51 +129,49 @@ fn is_empty_function(expr: &CallExpression) -> bool { } fn get_fix_content<'a>(expr: &'a CallExpression<'a>) -> (&'a str, Span) { - match &expr.callee { - Expression::Identifier(ident) => (".todo", Span::new(ident.span.end, ident.span.end)), - Expression::MemberExpression(mem_expr) => { - if let Some((span, _)) = mem_expr.static_property_info() { - return ("todo", span); - } - ("", expr.span) + if let Expression::Identifier(ident) = &expr.callee { + return (".todo", Span::new(ident.span.end, ident.span.end)); + } + if let Some(mem_expr) = expr.callee.as_member_expression() { + if let Some((span, _)) = mem_expr.static_property_info() { + return ("todo", span); } - _ => ("", expr.span), } + ("", expr.span) } fn build_code(expr: &CallExpression, ctx: &LintContext) -> (String, Span) { let mut formatter = ctx.codegen(); - if let Expression::Identifier(ident) = &expr.callee { - formatter.print_str(ident.name.as_bytes()); - formatter.print_str(b".todo("); - } else if let Expression::MemberExpression(mem_expr) = &expr.callee { - match &**mem_expr { - MemberExpression::ComputedMemberExpression(expr) => { - if let Expression::Identifier(ident) = &expr.object { - formatter.print_str(ident.name.as_bytes()); - formatter.print_str(b"["); - formatter.print_str(b"'todo'"); - formatter.print_str(b"]("); - } + match &expr.callee { + Expression::Identifier(ident) => { + formatter.print_str(ident.name.as_bytes()); + formatter.print_str(b".todo("); + } + Expression::ComputedMemberExpression(expr) => { + if let Expression::Identifier(ident) = &expr.object { + formatter.print_str(ident.name.as_bytes()); + formatter.print_str(b"["); + formatter.print_str(b"'todo'"); + formatter.print_str(b"]("); } - MemberExpression::StaticMemberExpression(expr) => { - if let Expression::Identifier(ident) = &expr.object { - formatter.print_str(ident.name.as_bytes()); - formatter.print_str(b".todo("); - } + } + Expression::StaticMemberExpression(expr) => { + if let Expression::Identifier(ident) = &expr.object { + formatter.print_str(ident.name.as_bytes()); + formatter.print_str(b".todo("); } - MemberExpression::PrivateFieldExpression(_) => {} } + _ => {} } - if let Argument::Expression(Expression::StringLiteral(ident)) = &expr.arguments[0] { + if let Argument::StringLiteral(ident) = &expr.arguments[0] { // Todo: this punctuation should read from the config formatter.print(b'\''); formatter.print_str(ident.value.as_bytes()); formatter.print(b'\''); formatter.print(b')'); - } else if let Argument::Expression(Expression::TemplateLiteral(temp)) = &expr.arguments[0] { + } else if let Argument::TemplateLiteral(temp) = &expr.arguments[0] { formatter.print(b'`'); for q in &temp.quasis { formatter.print_str(q.value.raw.as_bytes()); diff --git a/crates/oxc_linter/src/rules/jest/require_hook.rs b/crates/oxc_linter/src/rules/jest/require_hook.rs index 87d83c4401ed5..54d01690012cf 100644 --- a/crates/oxc_linter/src/rules/jest/require_hook.rs +++ b/crates/oxc_linter/src/rules/jest/require_hook.rs @@ -1,6 +1,6 @@ use oxc_allocator::Vec as OxcVec; use oxc_ast::{ - ast::{Argument, Declaration, Expression, Statement, VariableDeclarationKind}, + ast::{Argument, Expression, Statement, VariableDeclarationKind}, AstKind, }; use oxc_diagnostics::{ @@ -179,17 +179,13 @@ impl Rule for RequireHook { return; } - let Some(Argument::Expression(second_arg_expr)) = call_expr.arguments.get(1) else { - return; - }; - - match second_arg_expr { - Expression::FunctionExpression(func_expr) => { + match &call_expr.arguments[1] { + Argument::FunctionExpression(func_expr) => { if let Some(func_body) = &func_expr.body { self.check_block_body(node, &func_body.statements, ctx); }; } - Expression::ArrowFunctionExpression(arrow_func_expr) => { + Argument::ArrowFunctionExpression(arrow_func_expr) => { if !arrow_func_expr.expression { self.check_block_body(node, &arrow_func_expr.body.statements, ctx); } @@ -215,7 +211,7 @@ impl RequireHook { fn check<'a>(&self, node: &AstNode<'a>, stmt: &'a Statement<'_>, ctx: &LintContext<'a>) { if let Statement::ExpressionStatement(expr_stmt) = stmt { self.check_should_report_in_hook(node, &expr_stmt.expression, ctx); - } else if let Statement::Declaration(Declaration::VariableDeclaration(var_decl)) = stmt { + } else if let Statement::VariableDeclaration(var_decl) = stmt { if var_decl.kind != VariableDeclarationKind::Const && var_decl.declarations.iter().any(|decl| { let Some(init_call) = &decl.init else { diff --git a/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs b/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs index 123b4075eb6ae..b5a43e68cb273 100644 --- a/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs +++ b/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs @@ -93,55 +93,49 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) return; } - let callback = &call_expr.arguments[1]; - match callback { - Argument::Expression(expr) => match expr { - Expression::FunctionExpression(fn_expr) => { - if fn_expr.r#async { - diagnostic(ctx, fn_expr.span, Message::NoAsyncDescribeCallback); - } - let no_each_fields = - jest_fn_call.members.iter().all(|member| member.is_name_unequal("each")); - if no_each_fields && fn_expr.params.parameters_count() > 0 { - diagnostic(ctx, fn_expr.span, Message::UnexpectedDescribeArgument); - } + match &call_expr.arguments[1] { + Argument::FunctionExpression(fn_expr) => { + if fn_expr.r#async { + diagnostic(ctx, fn_expr.span, Message::NoAsyncDescribeCallback); + } + let no_each_fields = + jest_fn_call.members.iter().all(|member| member.is_name_unequal("each")); + if no_each_fields && fn_expr.params.parameters_count() > 0 { + diagnostic(ctx, fn_expr.span, Message::UnexpectedDescribeArgument); + } + + let Some(ref body) = fn_expr.body else { + return; + }; + if let Some(span) = find_first_return_stmt_span(body) { + diagnostic(ctx, span, Message::UnexpectedReturnInDescribe); + } + } + Argument::ArrowFunctionExpression(arrow_expr) => { + if arrow_expr.r#async { + diagnostic(ctx, arrow_expr.span, Message::NoAsyncDescribeCallback); + } + let no_each_fields = + jest_fn_call.members.iter().all(|member| member.is_name_unequal("each")); + if no_each_fields && arrow_expr.params.parameters_count() > 0 { + diagnostic(ctx, arrow_expr.span, Message::UnexpectedDescribeArgument); + } - let Some(ref body) = fn_expr.body else { + if arrow_expr.expression && arrow_expr.body.statements.len() > 0 { + let stmt = &arrow_expr.body.statements[0]; + let Statement::ExpressionStatement(expr_stmt) = stmt else { return; }; - if let Some(span) = find_first_return_stmt_span(body) { - diagnostic(ctx, span, Message::UnexpectedReturnInDescribe); + if let Expression::CallExpression(call_expr) = &expr_stmt.expression { + diagnostic(ctx, call_expr.span, Message::UnexpectedReturnInDescribe); } } - Expression::ArrowFunctionExpression(arrow_expr) => { - if arrow_expr.r#async { - diagnostic(ctx, arrow_expr.span, Message::NoAsyncDescribeCallback); - } - let no_each_fields = - jest_fn_call.members.iter().all(|member| member.is_name_unequal("each")); - if no_each_fields && arrow_expr.params.parameters_count() > 0 { - diagnostic(ctx, arrow_expr.span, Message::UnexpectedDescribeArgument); - } - if arrow_expr.expression && arrow_expr.body.statements.len() > 0 { - let stmt = &arrow_expr.body.statements[0]; - let Statement::ExpressionStatement(expr_stmt) = stmt else { - return; - }; - if let Expression::CallExpression(call_expr) = &expr_stmt.expression { - diagnostic(ctx, call_expr.span, Message::UnexpectedReturnInDescribe); - } - } - - if let Some(span) = find_first_return_stmt_span(&arrow_expr.body) { - diagnostic(ctx, span, Message::UnexpectedReturnInDescribe); - } + if let Some(span) = find_first_return_stmt_span(&arrow_expr.body) { + diagnostic(ctx, span, Message::UnexpectedReturnInDescribe); } - _ => diagnostic(ctx, expr.span(), Message::SecondArgumentMustBeFunction), - }, - Argument::SpreadElement(spreed_element) => { - diagnostic(ctx, spreed_element.span, Message::SecondArgumentMustBeFunction); } + callback => diagnostic(ctx, callback.span(), Message::SecondArgumentMustBeFunction), } } diff --git a/crates/oxc_linter/src/rules/jest/valid_expect.rs b/crates/oxc_linter/src/rules/jest/valid_expect.rs index 9ef75e90a0c74..1ec09d8da2a85 100644 --- a/crates/oxc_linter/src/rules/jest/valid_expect.rs +++ b/crates/oxc_linter/src/rules/jest/valid_expect.rs @@ -314,7 +314,7 @@ fn find_promise_call_expression_node<'a, 'b>( } if let AstKind::CallExpression(call_expr) = parent.kind() { - if let Expression::MemberExpression(member_expr) = &call_expr.callee { + 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() @@ -340,7 +340,7 @@ fn get_parent_if_thenable<'a, 'b>( let Some(grandparent) = grandparent else { return node }; let AstKind::CallExpression(call_expr) = grandparent.kind() else { return node }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { return node }; + let Some(member_expr) = call_expr.callee.as_member_expression() else { return node }; let Some(name) = member_expr.static_property_name() else { return node }; if ["then", "catch"].contains(&name) { diff --git a/crates/oxc_linter/src/rules/jest/valid_title.rs b/crates/oxc_linter/src/rules/jest/valid_title.rs index ec03f6d6ea4b8..bcfce5f9cb7de 100644 --- a/crates/oxc_linter/src/rules/jest/valid_title.rs +++ b/crates/oxc_linter/src/rules/jest/valid_title.rs @@ -133,15 +133,15 @@ impl ValidTitle { return; } - let Some(Argument::Expression(expr)) = call_expr.arguments.first() else { + let Some(arg) = call_expr.arguments.first() else { return; }; let need_report_describe_name = !(self.ignore_type_of_describe_name && matches!(jest_fn_call.kind, JestFnKind::General(JestGeneralFnKind::Describe))); - match expr { - Expression::StringLiteral(string_literal) => { + match arg { + Argument::StringLiteral(string_literal) => { validate_title( &string_literal.value, string_literal.span, @@ -150,7 +150,7 @@ impl ValidTitle { ctx, ); } - Expression::TemplateLiteral(template_literal) => { + Argument::TemplateLiteral(template_literal) => { if !template_literal.is_no_substitution_template() { return; } @@ -164,17 +164,17 @@ impl ValidTitle { ); } } - Expression::BinaryExpression(binary_expr) => { + Argument::BinaryExpression(binary_expr) => { if does_binary_expression_contain_string_node(binary_expr) { return; } if need_report_describe_name { - Message::TitleMustBeString.diagnostic(ctx, expr.span()); + Message::TitleMustBeString.diagnostic(ctx, arg.span()); } } _ => { if need_report_describe_name { - Message::TitleMustBeString.diagnostic(ctx, expr.span()); + Message::TitleMustBeString.diagnostic(ctx, arg.span()); } } } 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 0c8a067193c18..c956dc447d1f7 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{JSXAttributeItem, JSXAttributeValue, JSXElement, JSXExpression, JSXOpeningElement}, + ast::{JSXAttributeItem, JSXAttributeValue, JSXElement, JSXOpeningElement}, AstKind, }; use oxc_diagnostics::{ @@ -210,7 +210,7 @@ fn is_valid_alt_prop(item: &JSXAttributeItem<'_>) -> bool { match get_prop_value(item) { None => false, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { !expr.is_null_or_undefined() } else { true @@ -230,11 +230,7 @@ fn aria_label_has_value<'a>(item: &'a JSXAttributeItem<'a>) -> bool { None => false, Some(JSXAttributeValue::StringLiteral(s)) if s.value.is_empty() => false, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - !expr.is_undefined() - } else { - true - } + !container.expression.is_expression() || !container.expression.is_undefined() } _ => true, } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs index 4c17de4da985b..9d41f2d80555b 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, + ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, AstKind, }; use oxc_diagnostics::{ @@ -191,24 +191,18 @@ fn check_value_is_empty(value: &JSXAttributeValue, valid_hrefs: &[String]) -> bo || href_value == "javascript:void(0)" || !valid_hrefs.contains(&href_value) } - JSXAttributeValue::ExpressionContainer(exp) => { - if let JSXExpression::Expression(jsexp) = &exp.expression { - if let Expression::Identifier(ident) = jsexp { - if ident.name == "undefined" { - return true; - } - } else if let Expression::NullLiteral(_) = jsexp { - return true; - } else if let Expression::StringLiteral(str_lit) = jsexp { - let href_value = str_lit.value.to_string(); // Assuming Atom implements ToString - return href_value.is_empty() - || href_value == "#" - || href_value == "javascript:void(0)" - || !valid_hrefs.contains(&href_value); - } - }; - false - } + JSXAttributeValue::ExpressionContainer(exp) => match &exp.expression { + JSXExpression::Identifier(ident) => ident.name == "undefined", + JSXExpression::NullLiteral(_) => true, + JSXExpression::StringLiteral(str_lit) => { + let href_value = str_lit.value.to_string(); + href_value.is_empty() + || href_value == "#" + || href_value == "javascript:void(0)" + || !valid_hrefs.contains(&href_value) + } + _ => false, + }, JSXAttributeValue::Fragment(_) => true, } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs index f21d2c38be0cc..cf06243b04f80 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs @@ -135,10 +135,9 @@ impl Rule for AriaRole { match get_prop_value(aria_role) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - if expr.is_undefined() || expr.is_null() { - ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new())); - } + let jsexp = &container.expression; + if matches!(jsexp, JSXExpression::NullLiteral(_)) || jsexp.is_undefined() { + ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new())); } } Some(JSXAttributeValue::StringLiteral(str)) => { diff --git a/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs b/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs index 20396ea7ce768..52adea4466fb5 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, + ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName}, AstKind, }; use oxc_diagnostics::{ @@ -86,11 +86,7 @@ impl Rule for HtmlHasLang { fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool { match get_prop_value(item) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - !expr.is_undefined() - } else { - true - } + !container.expression.is_expression() || !container.expression.is_undefined() } Some(JSXAttributeValue::StringLiteral(str)) => !str.value.as_str().is_empty(), _ => true, diff --git a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs index 158d49c9ec833..9a8cb56af516e 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeValue, JSXElementName, JSXExpression}, + ast::{JSXAttributeValue, JSXElementName, JSXExpression}, AstKind, }; use oxc_diagnostics::{ @@ -89,26 +89,26 @@ impl Rule for IframeHasTitle { } } Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - if expr.is_string_literal() { - if let Expression::StringLiteral(str) = expr { - if !str.value.as_str().is_empty() { - return; - } + match &container.expression { + JSXExpression::StringLiteral(str) => { + if !str.value.is_empty() { + return; } - if let Expression::TemplateLiteral(tmpl) = expr { - if !tmpl.quasis.is_empty() - & !tmpl.expressions.is_empty() - & tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty()) - { - return; - } + } + JSXExpression::TemplateLiteral(tmpl) => { + if !tmpl.quasis.is_empty() + & !tmpl.expressions.is_empty() + & tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty()) + { + return; } } - - if expr.is_identifier_reference() & !expr.is_undefined() { - return; + expr @ JSXExpression::Identifier(_) => { + if !expr.is_undefined() { + return; + } } + _ => {} } } _ => {} diff --git a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs index 262b5c1d604ec..86ce0db6c9bd1 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs @@ -1,7 +1,7 @@ use regex::Regex; use oxc_ast::{ - ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression}, + ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression}, AstKind, }; use oxc_diagnostics::{ @@ -147,14 +147,14 @@ impl Rule for ImgRedundantAlt { } } JSXAttributeValue::ExpressionContainer(container) => match &container.expression { - JSXExpression::Expression(Expression::StringLiteral(lit)) => { + JSXExpression::StringLiteral(lit) => { let alt_text = lit.value.as_str(); if is_redundant_alt_text(alt_text, &self.redundant_words) { ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); } } - JSXExpression::Expression(Expression::TemplateLiteral(lit)) => { + JSXExpression::TemplateLiteral(lit) => { for quasi in &lit.quasis { let alt_text = quasi.value.raw.as_str(); diff --git a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs index c36775c734f50..bb1271b347437 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs @@ -1,6 +1,6 @@ use language_tags::LanguageTag; use oxc_ast::{ - ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, + ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName}, AstKind, }; use oxc_diagnostics::{ @@ -93,11 +93,7 @@ impl Rule for Lang { fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool { match get_prop_value(item) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - !expr.is_undefined() - } else { - true - } + !container.expression.is_expression() || !container.expression.is_undefined() } Some(JSXAttributeValue::StringLiteral(str)) => { let language_tag = LanguageTag::parse(str.value.as_str()).unwrap(); 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 e286de2e730fe..0af2afa973001 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 @@ -1,7 +1,5 @@ use oxc_ast::{ - ast::{ - Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXChild, JSXExpression, - }, + ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXChild, JSXExpression}, AstKind, }; use oxc_diagnostics::{ @@ -115,9 +113,7 @@ impl Rule for MediaHasCaption { return match &attr.value { Some(JSXAttributeValue::ExpressionContainer(exp)) => { match &exp.expression { - JSXExpression::Expression(Expression::BooleanLiteral( - boolean, - )) => boolean.value, + JSXExpression::BooleanLiteral(boolean) => boolean.value, _ => false, } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs b/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs index dc1f8cf090bfe..00f6aa90e9028 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{JSXAttributeValue, JSXExpression}, - AstKind, -}; +use oxc_ast::{ast::JSXAttributeValue, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -120,7 +117,7 @@ impl Rule for MouseEventsHaveKeyEvents { match has_jsx_prop(jsx_opening_el, "onFocus").and_then(get_prop_value) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { if expr.is_undefined() { ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnFocus( jsx_attr.span(), @@ -150,13 +147,11 @@ impl Rule for MouseEventsHaveKeyEvents { match has_jsx_prop(jsx_opening_el, "onBlur").and_then(get_prop_value) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - if expr.is_undefined() { - ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur( - jsx_attr.span(), - String::from(handler), - )); - } + if container.expression.is_undefined() { + ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur( + jsx_attr.span(), + String::from(handler), + )); } } None => { diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs index 75d99a7b7998a..ae7811b782318 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression}, + ast::{JSXAttributeItem, JSXAttributeValue}, AstKind, }; use oxc_diagnostics::{ @@ -49,10 +49,8 @@ impl Rule for NoAccessKey { ctx.diagnostic(NoAccessKeyDiagnostic(attr.span)); } Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { - if expr.is_identifier_reference() & expr.is_undefined() { - return; - } + if container.expression.is_expression() && !container.expression.is_undefined() + { ctx.diagnostic(NoAccessKeyDiagnostic(attr.span)); } } 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 b6de30d3394e5..267b958abcf3b 100644 --- a/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs +++ b/crates/oxc_linter/src/rules/nextjs/inline_script_id.rs @@ -79,7 +79,7 @@ impl Rule for InlineScriptId { { for prop in &obj_expr.properties { if let ObjectPropertyKind::ObjectProperty(obj_prop) = prop { - if let PropertyKey::Identifier(ident) = &obj_prop.key { + if let PropertyKey::StaticIdentifier(ident) = &obj_prop.key { prop_names_hash_set.insert(ident.name.clone()); } } diff --git a/crates/oxc_linter/src/rules/nextjs/next_script_for_ga.rs b/crates/oxc_linter/src/rules/nextjs/next_script_for_ga.rs index 813ec387f10e9..b7faf9f74754a 100644 --- a/crates/oxc_linter/src/rules/nextjs/next_script_for_ga.rs +++ b/crates/oxc_linter/src/rules/nextjs/next_script_for_ga.rs @@ -97,15 +97,13 @@ fn get_dangerously_set_inner_html_prop_value<'a>( else { return None; }; - let JSXExpression::Expression(Expression::ObjectExpression(object_expr)) = - &object_expr.expression - else { + let JSXExpression::ObjectExpression(object_expr) = &object_expr.expression else { return None; }; if let Some(html_prop) = object_expr.properties.iter().find_map(|prop| { if let ObjectPropertyKind::ObjectProperty(html_prop) = prop { - if let PropertyKey::Identifier(html_prop_ident) = &html_prop.key { + if let PropertyKey::StaticIdentifier(html_prop_ident) = &html_prop.key { if html_prop_ident.name == "__html" { Some(html_prop) } else { diff --git a/crates/oxc_linter/src/rules/nextjs/no_async_client_component.rs b/crates/oxc_linter/src/rules/nextjs/no_async_client_component.rs index f1db16dbe2ee8..36ec34564eb53 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_async_client_component.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_async_client_component.rs @@ -1,7 +1,5 @@ use oxc_ast::{ - ast::{ - BindingPatternKind, ExportDefaultDeclarationKind, Expression, ModuleDeclaration, Statement, - }, + ast::{BindingPatternKind, ExportDefaultDeclarationKind, Expression, Statement}, AstKind, }; use oxc_diagnostics::{ @@ -45,11 +43,7 @@ impl Rule for NoAsyncClientComponent { if program.directives.iter().any(|directive| directive.directive.as_str() == "use client") { for node in &program.body { - let Statement::ModuleDeclaration(mod_decl) = &node else { - continue; - }; - let ModuleDeclaration::ExportDefaultDeclaration(export_default_decl) = &**mod_decl - else { + let Statement::ExportDefaultDeclaration(export_default_decl) = &node else { continue; }; @@ -71,9 +65,8 @@ impl Rule for NoAsyncClientComponent { } // async function MyComponent() {...}; export default MyComponent; - if let ExportDefaultDeclarationKind::Expression(Expression::Identifier( - export_default_id, - )) = &export_default_decl.declaration + if let ExportDefaultDeclarationKind::Identifier(export_default_id) = + &export_default_decl.declaration { let Some(decl) = get_declaration_of_variable(export_default_id, ctx) else { continue; diff --git a/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs b/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs index e6b8479db5e26..5c36f2a0a8bb0 100644 --- a/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs +++ b/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs @@ -1,6 +1,6 @@ // Based on https://github.com/rust-lang/rust-clippy//blob/c9a43b18f11219fa70fe632b29518581fcd589c8/clippy_lints/src/operators/misrefactored_assign_op.rs use oxc_ast::{ - ast::{AssignmentTarget, Expression}, + ast::{match_member_expression, AssignmentTarget, Expression, SimpleAssignmentTarget}, AstKind, }; use oxc_diagnostics::{ @@ -98,32 +98,33 @@ fn assignment_target_eq_expr<'a>( right_expr: &Expression<'_>, ctx: &LintContext<'a>, ) -> bool { - if let AssignmentTarget::SimpleAssignmentTarget(simple_assignment_target) = assignment_target { + if let Some(simple_assignment_target) = assignment_target.as_simple_assignment_target() { return match simple_assignment_target { - oxc_ast::ast::SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { + SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { if let Expression::Identifier(right_ident) = right_expr { ident.name == right_ident.name } else { false } } - oxc_ast::ast::SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => { - if let Expression::MemberExpression(right_member_expr) = right_expr { + match_member_expression!(SimpleAssignmentTarget) => { + let member_expr = simple_assignment_target.to_member_expression(); + if let Some(right_member_expr) = right_expr.as_member_expression() { is_same_member_expression(member_expr, right_member_expr, ctx) } else { false } } - oxc_ast::ast::SimpleAssignmentTarget::TSAsExpression(ts_expr) => { + SimpleAssignmentTarget::TSAsExpression(ts_expr) => { is_same_reference(&ts_expr.expression, right_expr, ctx) } - oxc_ast::ast::SimpleAssignmentTarget::TSSatisfiesExpression(ts_expr) => { + SimpleAssignmentTarget::TSSatisfiesExpression(ts_expr) => { is_same_reference(&ts_expr.expression, right_expr, ctx) } - oxc_ast::ast::SimpleAssignmentTarget::TSNonNullExpression(ts_expr) => { + SimpleAssignmentTarget::TSNonNullExpression(ts_expr) => { is_same_reference(&ts_expr.expression, right_expr, ctx) } - oxc_ast::ast::SimpleAssignmentTarget::TSTypeAssertion(ts_expr) => { + SimpleAssignmentTarget::TSTypeAssertion(ts_expr) => { is_same_reference(&ts_expr.expression, right_expr, ctx) } }; 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 e0b4a6c08a46d..22b5b038ff279 100644 --- a/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs +++ b/crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs @@ -133,7 +133,7 @@ fn get_diagnostic<'a>( // unwrap is safe because we already checked that this is a reduce call let (reduce_call_span, _) = call_expr_method_callee_info(call_expr).unwrap(); - if let Some(Argument::Expression(second_arg)) = call_expr.arguments.get(1) { + if let Some(second_arg) = call_expr.arguments.get(1).and_then(Argument::as_expression) { let second_arg = second_arg.without_parenthesized(); let second_arg = if let Expression::TSAsExpression(as_expr) = second_arg.without_parenthesized() { diff --git a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs index 728b207db23b8..233349f85360f 100644 --- a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs +++ b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs @@ -1,4 +1,4 @@ -use oxc_ast::{ast::Statement, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -60,10 +60,11 @@ impl Rule for NoBarrelFile { let AstKind::Program(program) = root.kind() else { unreachable!() }; - let declarations = program.body.iter().fold(0, |acc, node| match node { - Statement::Declaration(_) => acc + 1, - _ => acc, - }); + let declarations = + program + .body + .iter() + .fold(0, |acc, node| if node.is_declaration() { acc + 1 } else { acc }); let exports = module_record.star_export_entries.len() + module_record.indirect_export_entries.len(); diff --git a/crates/oxc_linter/src/rules/react/button_has_type.rs b/crates/oxc_linter/src/rules/react/button_has_type.rs index cf4915cd76bcf..b8cac5d83451c 100644 --- a/crates/oxc_linter/src/rules/react/button_has_type.rs +++ b/crates/oxc_linter/src/rules/react/button_has_type.rs @@ -1,6 +1,6 @@ use oxc_ast::{ ast::{ - Argument, Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, + Argument, Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, ObjectPropertyKind, }, AstKind, @@ -94,9 +94,7 @@ impl Rule for ButtonHasType { } AstKind::CallExpression(call_expr) => { if is_create_element_call(call_expr) { - let Some(Argument::Expression(Expression::StringLiteral(str))) = - call_expr.arguments.first() - else { + let Some(Argument::StringLiteral(str)) = call_expr.arguments.first() else { return; }; @@ -104,9 +102,7 @@ impl Rule for ButtonHasType { return; } - if let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - call_expr.arguments.get(1) - { + if let Some(Argument::ObjectExpression(obj_expr)) = call_expr.arguments.get(1) { obj_expr .properties .iter() @@ -164,7 +160,7 @@ impl ButtonHasType { fn is_valid_button_type_prop(&self, item: &JSXAttributeItem) -> bool { match get_prop_value(item) { Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { self.is_valid_button_type_prop_expression(expr) } else { false diff --git a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs index fe7ccf9a8136b..c9b10e43c4e56 100644 --- a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs +++ b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, JSXAttributeItem, ObjectPropertyKind}, + ast::{Argument, JSXAttributeItem, ObjectPropertyKind}, AstKind, }; use oxc_diagnostics::{ @@ -125,8 +125,7 @@ impl Rule for CheckedRequiresOnchangeOrReadonly { return; } - let Some(Argument::Expression(Expression::StringLiteral(element_name))) = - call_expr.arguments.first() + let Some(Argument::StringLiteral(element_name)) = call_expr.arguments.first() else { return; }; @@ -135,9 +134,7 @@ impl Rule for CheckedRequiresOnchangeOrReadonly { return; } - let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - call_expr.arguments.get(1) - else { + let Some(Argument::ObjectExpression(obj_expr)) = call_expr.arguments.get(1) else { return; }; diff --git a/crates/oxc_linter/src/rules/react/jsx_key.rs b/crates/oxc_linter/src/rules/react/jsx_key.rs index 51b441c844fb0..be93eae7b4a4a 100644 --- a/crates/oxc_linter/src/rules/react/jsx_key.rs +++ b/crates/oxc_linter/src/rules/react/jsx_key.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXElement, JSXFragment, Statement}, + ast::{JSXAttributeItem, JSXAttributeName, JSXElement, JSXFragment, Statement}, AstKind, }; use oxc_diagnostics::{ @@ -128,7 +128,7 @@ fn is_in_array_or_iter<'a, 'b>( AstKind::CallExpression(v) => { let callee = &v.callee.without_parenthesized(); - if let Expression::MemberExpression(v) = callee { + if let Some(v) = callee.as_member_expression() { if let Some((span, ident)) = v.static_property_info() { if TARGET_METHODS.contains(ident) { return Some(InsideArrayOrIterator::Iterator(span)); diff --git a/crates/oxc_linter/src/rules/react/jsx_no_target_blank.rs b/crates/oxc_linter/src/rules/react/jsx_no_target_blank.rs index 077ed76f4ae01..9e8c6ea347db2 100644 --- a/crates/oxc_linter/src/rules/react/jsx_no_target_blank.rs +++ b/crates/oxc_linter/src/rules/react/jsx_no_target_blank.rs @@ -1,7 +1,7 @@ use oxc_ast::{ ast::{ - Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName, - JSXExpression, StringLiteral, + match_expression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, + JSXElementName, JSXExpression, StringLiteral, }, AstKind, }; @@ -279,7 +279,7 @@ fn check_href( is_external_link = check_is_external_link(&str.value); } JSXAttributeValue::ExpressionContainer(expr) => { - if let JSXExpression::Expression(expr) = &expr.expression { + if let Some(expr) = expr.expression.as_expression() { match_href_expression(expr, &mut is_external_link, &mut is_dynamic_link); } } @@ -351,8 +351,10 @@ fn check_rel<'a>( (check_rel_val(str, allow_referrer), "", false, false) } JSXAttributeValue::ExpressionContainer(expr) => match &expr.expression { - JSXExpression::Expression(expr) => match_rel_expression(expr, allow_referrer), JSXExpression::EmptyExpression(_) => default, + expr @ match_expression!(JSXExpression) => { + match_rel_expression(expr.to_expression(), allow_referrer) + } }, _ => default, } @@ -388,7 +390,7 @@ fn check_target<'a>(attribute_value: &'a JSXAttributeValue<'a>) -> (bool, &'a st (str.value.as_str().to_lowercase() == "_blank", "", false, false) } JSXAttributeValue::ExpressionContainer(expr) => { - if let JSXExpression::Expression(expr) = &expr.expression { + if let Some(expr) = expr.expression.as_expression() { match_target_expression(expr) } else { default 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 8ec05c1e9ec50..d1ba17c51abe9 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 @@ -1,7 +1,7 @@ use oxc_ast::{ ast::{ - Expression, JSXAttributeItem, JSXAttributeName, JSXChild, JSXElement, JSXElementName, - JSXExpression, JSXFragment, JSXMemberExpressionObject, JSXOpeningElement, + JSXAttributeItem, JSXAttributeName, JSXChild, JSXElement, JSXElementName, JSXExpression, + JSXFragment, JSXMemberExpressionObject, JSXOpeningElement, }, AstKind, }; @@ -180,7 +180,7 @@ fn has_less_than_two_children(children: &oxc_allocator::Vec<'_, JSXChild<'_>>) - if non_padding_children.len() < 2 { return !non_padding_children.iter().any(|v| { if let JSXChild::ExpressionContainer(v) = v { - if let JSXExpression::Expression(Expression::CallExpression(_)) = v.expression { + if let JSXExpression::CallExpression(_) = v.expression { return true; } return false; diff --git a/crates/oxc_linter/src/rules/react/no_children_prop.rs b/crates/oxc_linter/src/rules/react/no_children_prop.rs index f9850b8ce2e87..42bcd530b4986 100644 --- a/crates/oxc_linter/src/rules/react/no_children_prop.rs +++ b/crates/oxc_linter/src/rules/react/no_children_prop.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, JSXAttributeItem, JSXAttributeName, ObjectPropertyKind}, + ast::{Argument, JSXAttributeItem, JSXAttributeName, ObjectPropertyKind}, AstKind, }; use oxc_diagnostics::{ @@ -70,9 +70,7 @@ impl Rule for NoChildrenProp { } AstKind::CallExpression(call_expr) => { if is_create_element_call(call_expr) { - if let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - call_expr.arguments.get(1) - { + if let Some(Argument::ObjectExpression(obj_expr)) = call_expr.arguments.get(1) { if let Some(span) = obj_expr.properties.iter().find_map(|prop| { if let ObjectPropertyKind::ObjectProperty(prop) = prop { if prop.key.is_specific_static_name("children") { diff --git a/crates/oxc_linter/src/rules/react/no_danger.rs b/crates/oxc_linter/src/rules/react/no_danger.rs index d7564deb2de2d..fe5c83238f8d3 100644 --- a/crates/oxc_linter/src/rules/react/no_danger.rs +++ b/crates/oxc_linter/src/rules/react/no_danger.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, JSXAttributeItem, ObjectPropertyKind}, + ast::{Argument, JSXAttributeItem, ObjectPropertyKind}, AstKind, }; use oxc_diagnostics::{ @@ -56,9 +56,9 @@ impl Rule for NoDanger { return; } - let Some(Argument::Expression(props)) = call_expr.arguments.get(1) else { return }; + let Some(props) = call_expr.arguments.get(1) else { return }; - let Expression::ObjectExpression(obj_expr) = props else { return }; + let Argument::ObjectExpression(obj_expr) = props else { return }; for prop in &obj_expr.properties { if let ObjectPropertyKind::ObjectProperty(obj_prop) = prop { diff --git a/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs b/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs index b2f7a751aa425..d6182348ad6f9 100644 --- a/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs +++ b/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs @@ -1,8 +1,5 @@ use oxc_ast::{ - ast::{ - AssignmentTarget, Expression, MemberExpression, MethodDefinitionKind, - SimpleAssignmentTarget, StaticMemberExpression, - }, + ast::{Expression, MethodDefinitionKind, SimpleAssignmentTarget, StaticMemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -103,32 +100,27 @@ fn is_state_member_expression(expression: &StaticMemberExpression<'_>) -> bool { fn get_outer_member_expression<'a, 'b>( assignment: &'b SimpleAssignmentTarget<'a>, ) -> Option<&'b StaticMemberExpression<'a>> { - if let SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) = assignment { - match &**member_expr { - MemberExpression::StaticMemberExpression(expr) => { - let mut node = expr; - loop { - if node.object.is_null() { - return Some(node); - } + match assignment { + SimpleAssignmentTarget::StaticMemberExpression(expr) => { + let mut node = &**expr; + loop { + if node.object.is_null() { + return Some(node); + } - if let Some(object) = get_static_member_expression_obj(&node.object) { - if !object.property.name.is_empty() { - node = object; + if let Some(object) = get_static_member_expression_obj(&node.object) { + if !object.property.name.is_empty() { + node = object; - continue; - } + continue; } - - return Some(node); } + + return Some(node); } - MemberExpression::PrivateFieldExpression(_) - | MemberExpression::ComputedMemberExpression(_) => {} } + _ => None, } - - None } // Because node.object is of type &Expression<'_> @@ -137,10 +129,7 @@ fn get_static_member_expression_obj<'a, 'b>( expression: &'b Expression<'a>, ) -> Option<&'b StaticMemberExpression<'a>> { match expression { - Expression::MemberExpression(member_expr) => match &**member_expr { - MemberExpression::StaticMemberExpression(expr) => Some(expr), - _ => None, - }, + Expression::StaticMemberExpression(expr) => Some(expr), _ => None, } } @@ -177,8 +166,7 @@ impl Rule for NoDirectMutationState { return; } - if let AssignmentTarget::SimpleAssignmentTarget(assignment) = &assignment_expr.left - { + if let Some(assignment) = assignment_expr.left.as_simple_assignment_target() { if let Some(outer_member_expression) = get_outer_member_expression(assignment) { if is_state_member_expression(outer_member_expression) { ctx.diagnostic(NoDirectMutationStateDiagnostic( diff --git a/crates/oxc_linter/src/rules/react/no_is_mounted.rs b/crates/oxc_linter/src/rules/react/no_is_mounted.rs index d36ab2263a766..06895173872de 100644 --- a/crates/oxc_linter/src/rules/react/no_is_mounted.rs +++ b/crates/oxc_linter/src/rules/react/no_is_mounted.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{CallExpression, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -48,12 +45,10 @@ declare_oxc_lint!( impl Rule for NoIsMounted { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(CallExpression { - callee: Expression::MemberExpression(member_expr), - span, - .. - }) = node.kind() - else { + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -68,7 +63,7 @@ impl Rule for NoIsMounted { ctx.nodes().kind(ancestor), AstKind::ObjectProperty(_) | AstKind::MethodDefinition(_) ) { - ctx.diagnostic(NoIsMountedDiagnostic(*span)); + ctx.diagnostic(NoIsMountedDiagnostic(call_expr.span)); break; } } 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 8b8ab508e4620..0304bc664c647 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 @@ -39,7 +39,7 @@ declare_oxc_lint!( impl Rule for NoRenderReturnValue { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::CallExpression(call_expr) = node.kind() else { return }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { return }; + let Some(member_expr) = call_expr.callee.as_member_expression() else { return }; let Expression::Identifier(ident) = member_expr.object() else { return }; if ident.name == "ReactDOM" { if let Some((property_span, property_name)) = member_expr.static_property_info() { diff --git a/crates/oxc_linter/src/rules/react/no_string_refs.rs b/crates/oxc_linter/src/rules/react/no_string_refs.rs index e34211c2c61db..a3fdfb55ac318 100644 --- a/crates/oxc_linter/src/rules/react/no_string_refs.rs +++ b/crates/oxc_linter/src/rules/react/no_string_refs.rs @@ -78,12 +78,9 @@ fn contains_string_literal( expr_container: &JSXExpressionContainer, no_template_literals: bool, ) -> bool { - if let JSXExpression::Expression(expr) = &expr_container.expression { - matches!(expr, Expression::StringLiteral(_)) - || (no_template_literals && matches!(expr, Expression::TemplateLiteral(_))) - } else { - false - } + let expr = &expr_container.expression; + matches!(expr, JSXExpression::StringLiteral(_)) + || (no_template_literals && matches!(expr, JSXExpression::TemplateLiteral(_))) } fn is_literal_ref_attribute(attr: &JSXAttribute, no_template_literals: bool) -> bool { 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 113cf03f02fd5..fa2e479b2f75e 100644 --- a/crates/oxc_linter/src/rules/react/require_render_return.rs +++ b/crates/oxc_linter/src/rules/react/require_render_return.rs @@ -89,9 +89,7 @@ impl Rule for RequireRenderReturn { } } AstKind::CallExpression(ce) => { - if let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - ce.arguments.first() - { + if let Some(Argument::ObjectExpression(obj_expr)) = ce.arguments.first() { if let Some(fn_body) = obj_expr .properties .iter() diff --git a/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs b/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs index c08d0f5afb843..931c476844107 100644 --- a/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs +++ b/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs @@ -1,7 +1,7 @@ use oxc_ast::{ ast::{ - Argument, Expression, JSXAttributeItem, JSXAttributeName, JSXElementName, - ObjectPropertyKind, PropertyKey, + Argument, JSXAttributeItem, JSXAttributeName, JSXElementName, ObjectPropertyKind, + PropertyKey, }, AstKind, }; @@ -99,8 +99,7 @@ impl Rule for VoidDomElementsNoChildren { return; } - let Some(Argument::Expression(Expression::StringLiteral(element_name))) = - call_expr.arguments.first() + let Some(Argument::StringLiteral(element_name)) = call_expr.arguments.first() else { return; }; @@ -113,16 +112,14 @@ impl Rule for VoidDomElementsNoChildren { return; } - let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - call_expr.arguments.get(1) - else { + let Some(Argument::ObjectExpression(obj_expr)) = call_expr.arguments.get(1) else { return; }; let has_children_prop_or_danger = obj_expr.properties.iter().any(|property| match property { ObjectPropertyKind::ObjectProperty(prop) => match &prop.key { - PropertyKey::Identifier(iden) => { + PropertyKey::StaticIdentifier(iden) => { iden.name == "children" || iden.name == "dangerouslySetInnerHTML" } _ => false, diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs index fece8ae75dbe9..852d1290804e7 100644 --- a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs +++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression}, + ast::{Expression, JSXAttributeValue, JSXElement}, AstKind, }; use oxc_diagnostics::{ @@ -53,7 +53,7 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { match get_prop_value(item) { None => return, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { if let Some(span) = check_expression(expr) { ctx.diagnostic(JsxNoJsxAsPropDiagnostic(span)); } diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_array_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_array_as_prop.rs index a3671b24142d1..c3be839b4030d 100644 --- a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_array_as_prop.rs +++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_array_as_prop.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression}, + ast::{Expression, JSXAttributeValue, JSXElement}, AstKind, }; use oxc_diagnostics::{ @@ -59,7 +59,7 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { match get_prop_value(item) { None => return, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { if let Some(span) = check_expression(expr) { ctx.diagnostic(JsxNoNewArrayAsPropDiagnostic(span)); } diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_function_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_function_as_prop.rs index 82594809824b4..c6a9348e70620 100644 --- a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_function_as_prop.rs +++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_function_as_prop.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, MemberExpression}, + ast::{Expression, JSXAttributeValue, JSXElement, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -54,7 +54,7 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { match get_prop_value(item) { None => return, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { if let Some(span) = check_expression(expr) { ctx.diagnostic(JsxNoNewFunctionAsPropDiagnostic(span)); } @@ -74,12 +74,8 @@ fn check_expression(expr: &Expression) -> Option { return Some(expr.span); } - let Expression::MemberExpression(member_expr) = &expr.callee else { - return None; - }; - + let member_expr = expr.callee.as_member_expression()?; let property_name = MemberExpression::static_property_name(member_expr); - if property_name == Some("bind") { Some(expr.span) } else { diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_object_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_object_as_prop.rs index f7985ade1a4d1..4af883a058997 100644 --- a/crates/oxc_linter/src/rules/react_perf/jsx_no_new_object_as_prop.rs +++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_new_object_as_prop.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression}, + ast::{Expression, JSXAttributeValue, JSXElement}, AstKind, }; use oxc_diagnostics::{ @@ -58,7 +58,7 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { match get_prop_value(item) { None => return, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { if let Some(span) = check_expression(expr) { ctx.diagnostic(JsxNoNewObjectAsPropDiagnostic(span)); } diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs index b24112dbb570e..4739173f6c797 100644 --- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs +++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs @@ -2,14 +2,15 @@ use std::cell::{Cell, RefCell}; use oxc_ast::{ ast::{ - Argument, ArrayExpressionElement, ArrowFunctionExpression, AssignmentTarget, - BinaryExpression, BindingIdentifier, BindingPattern, BindingPatternKind, CallExpression, - Class, ClassBody, ClassElement, ComputedMemberExpression, ConditionalExpression, - Declaration, ExportDefaultDeclarationKind, ExportSpecifier, Expression, ForStatementInit, - ForStatementLeft, FormalParameter, Function, IdentifierReference, MemberExpression, - ModuleDeclaration, ModuleExportName, NewExpression, ParenthesizedExpression, - PrivateFieldExpression, Program, PropertyKey, SimpleAssignmentTarget, Statement, - StaticMemberExpression, ThisExpression, VariableDeclarator, + match_declaration, match_expression, match_member_expression, + match_simple_assignment_target, Argument, ArrayExpressionElement, ArrowFunctionExpression, + AssignmentTarget, BinaryExpression, BindingIdentifier, BindingPattern, BindingPatternKind, + CallExpression, Class, ClassBody, ClassElement, ComputedMemberExpression, + ConditionalExpression, Declaration, ExportSpecifier, Expression, ForStatementInit, + FormalParameter, Function, IdentifierReference, MemberExpression, ModuleExportName, + NewExpression, ParenthesizedExpression, PrivateFieldExpression, Program, PropertyKey, + SimpleAssignmentTarget, Statement, StaticMemberExpression, ThisExpression, + VariableDeclarator, }, AstKind, }; @@ -73,41 +74,36 @@ impl<'a> ListenerMap for Statement<'a> { Self::ExpressionStatement(expr_stmt) => { expr_stmt.expression.report_effects(options); } + #[allow(clippy::match_same_arms)] Self::BreakStatement(_) | Self::ContinueStatement(_) | Self::EmptyStatement(_) => { no_effects(); } - Self::Declaration(decl) => { - decl.report_effects(options); - } + match_declaration!(Self) => self.to_declaration().report_effects(options), Self::ReturnStatement(stmt) => { if let Some(arg) = &stmt.argument { arg.report_effects(options); } } - Self::ModuleDeclaration(decl) => match &**decl { - ModuleDeclaration::ExportAllDeclaration(_) - | ModuleDeclaration::ImportDeclaration(_) => { - no_effects(); - } - ModuleDeclaration::ExportDefaultDeclaration(stmt) => { - if let ExportDefaultDeclarationKind::Expression(expr) = &stmt.declaration { - if has_comment_about_side_effect_check(expr.span(), options.ctx) { - expr.report_effects_when_called(options); - } - expr.report_effects(options); + Self::ExportAllDeclaration(_) | Self::ImportDeclaration(_) => { + no_effects(); + } + Self::ExportDefaultDeclaration(stmt) => { + if let Some(expr) = &stmt.declaration.as_expression() { + if has_comment_about_side_effect_check(expr.span(), options.ctx) { + expr.report_effects_when_called(options); } + expr.report_effects(options); } - ModuleDeclaration::ExportNamedDeclaration(stmt) => { - stmt.specifiers.iter().for_each(|specifier| { - specifier.report_effects(options); - }); + } + Self::ExportNamedDeclaration(stmt) => { + stmt.specifiers.iter().for_each(|specifier| { + specifier.report_effects(options); + }); - if let Some(decl) = &stmt.declaration { - decl.report_effects(options); - } + if let Some(decl) = &stmt.declaration { + decl.report_effects(options); } - _ => {} - }, + } Self::TryStatement(stmt) => { stmt.block.body.iter().for_each(|stmt| stmt.report_effects(options)); stmt.handler.iter().for_each(|handler| { @@ -165,14 +161,14 @@ impl<'a> ListenerMap for Statement<'a> { stmt.body.report_effects(options); } Self::ForInStatement(stmt) => { - if let ForStatementLeft::AssignmentTarget(assign) = &stmt.left { + if let Some(assign) = stmt.left.as_assignment_target() { assign.report_effects_when_assigned(options); } stmt.right.report_effects(options); stmt.body.report_effects(options); } Self::ForOfStatement(stmt) => { - if let ForStatementLeft::AssignmentTarget(assign) = &stmt.left { + if let Some(assign) = stmt.left.as_assignment_target() { assign.report_effects_when_assigned(options); } stmt.right.report_effects(options); @@ -186,9 +182,7 @@ impl<'a> ListenerMap for Statement<'a> { impl<'a> ListenerMap for ForStatementInit<'a> { fn report_effects(&self, options: &NodeListenerOptions) { match self { - Self::Expression(expr) => { - expr.report_effects(options); - } + match_expression!(Self) => self.to_expression().report_effects(options), Self::UsingDeclaration(decl) => { decl.declarations.iter().for_each(|decl| decl.report_effects(options)); } @@ -375,11 +369,9 @@ impl<'a> ListenerMap for ClassElement<'a> { impl<'a> ListenerMap for PropertyKey<'a> { fn report_effects(&self, options: &NodeListenerOptions) { - match self { - Self::Expression(expr) => { - expr.report_effects(options); - } - _ => no_effects(), + match self.as_expression() { + Some(expr) => expr.report_effects(options), + None => no_effects(), } } } @@ -567,7 +559,9 @@ fn defined_custom_report_effects_when_called(expr: &Expression) -> bool { | Expression::ConditionalExpression(_) | Expression::FunctionExpression(_) | Expression::Identifier(_) - | Expression::MemberExpression(_) + | Expression::ComputedMemberExpression(_) + | Expression::StaticMemberExpression(_) + | Expression::PrivateFieldExpression(_) ) } @@ -711,7 +705,7 @@ impl<'a> ListenerMap for CallExpression<'a> { impl<'a> ListenerMap for Argument<'a> { fn report_effects(&self, options: &NodeListenerOptions) { match self { - Self::Expression(expr) => expr.report_effects(options), + match_expression!(Self) => self.to_expression().report_effects(options), Self::SpreadElement(spread) => { spread.argument.report_effects(options); } @@ -722,10 +716,10 @@ impl<'a> ListenerMap for Argument<'a> { impl<'a> ListenerMap for AssignmentTarget<'a> { fn report_effects_when_assigned(&self, options: &NodeListenerOptions) { match self { - Self::SimpleAssignmentTarget(target) => { - target.report_effects_when_assigned(options); + match_simple_assignment_target!(Self) => { + self.to_simple_assignment_target().report_effects_when_assigned(options); } - Self::AssignmentTargetPattern(_pattern) => {} + Self::ArrayAssignmentTarget(_) | Self::ObjectAssignmentTarget(_) => {} } } } @@ -736,8 +730,8 @@ impl<'a> ListenerMap for SimpleAssignmentTarget<'a> { Self::AssignmentTargetIdentifier(ident) => { ident.report_effects_when_assigned(options); } - Self::MemberAssignmentTarget(member) => { - member.report_effects_when_assigned(options); + match_member_expression!(Self) => { + self.to_member_expression().report_effects_when_assigned(options); } _ => { // For remain TypeScript AST, just visit its expression @@ -862,7 +856,7 @@ impl<'a> ListenerMap for PrivateFieldExpression<'a> { impl<'a> ListenerMap for ArrayExpressionElement<'a> { fn report_effects(&self, options: &NodeListenerOptions) { match self { - Self::Expression(expr) => expr.report_effects(options), + match_expression!(Self) => self.to_expression().report_effects(options), Self::SpreadElement(spreed) => { spreed.argument.report_effects(options); } diff --git a/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs b/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs index dbffaf0ccffdf..09166515efb26 100644 --- a/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs +++ b/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs @@ -1,6 +1,6 @@ use oxc_ast::{ ast::{ - ClassElement, Declaration, ExportDefaultDeclarationKind, Expression, FunctionType, + match_expression, ClassElement, Declaration, ExportDefaultDeclarationKind, FunctionType, ModuleDeclaration, PropertyKey, Statement, TSSignature, }, AstKind, @@ -87,18 +87,17 @@ enum MethodKind { } fn get_kind_from_key(key: &PropertyKey) -> MethodKind { + #[allow(clippy::match_same_arms)] match key { - PropertyKey::Identifier(_) => MethodKind::Normal, + PropertyKey::StaticIdentifier(_) => MethodKind::Normal, PropertyKey::PrivateIdentifier(_) => MethodKind::Private, - PropertyKey::Expression(expr) => match expr { - Expression::StringLiteral(_) => MethodKind::Normal, - Expression::NumericLiteral(_) - | Expression::BigintLiteral(_) - | Expression::TemplateLiteral(_) - | Expression::RegExpLiteral(_) - | Expression::NullLiteral(_) => MethodKind::Quoted, - _ => MethodKind::Expression, - }, + PropertyKey::StringLiteral(_) => MethodKind::Normal, + PropertyKey::NumericLiteral(_) + | PropertyKey::BigintLiteral(_) + | PropertyKey::TemplateLiteral(_) + | PropertyKey::RegExpLiteral(_) + | PropertyKey::NullLiteral(_) => MethodKind::Quoted, + match_expression!(PropertyKey) => MethodKind::Expression, } } @@ -239,10 +238,12 @@ impl GetMethod for Declaration<'_> { impl GetMethod for Statement<'_> { fn get_method(&self) -> Option { - match self { - Statement::ModuleDeclaration(decl) => decl.get_method(), - Statement::Declaration(decl) => decl.get_method(), - _ => None, + if let Some(decl) = self.as_module_declaration() { + decl.get_method() + } else if let Some(decl) = self.as_declaration() { + decl.get_method() + } else { + None } } } diff --git a/crates/oxc_linter/src/rules/typescript/no_misused_new.rs b/crates/oxc_linter/src/rules/typescript/no_misused_new.rs index 7cebe5f46fbf7..73ce6f8fe8bf8 100644 --- a/crates/oxc_linter/src/rules/typescript/no_misused_new.rs +++ b/crates/oxc_linter/src/rules/typescript/no_misused_new.rs @@ -81,7 +81,7 @@ impl Rule for NoMisusedNew { } } AstKind::TSMethodSignature(method_sig) => { - if let PropertyKey::Identifier(id) = &method_sig.key { + if let PropertyKey::StaticIdentifier(id) = &method_sig.key { if id.name == "constructor" { ctx.diagnostic(NoMisusedNewInterfaceDiagnostic(method_sig.key.span())); } 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 6323b87aabe36..ff34b8e363367 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 @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{ChainElement, Expression}, + ast::{match_member_expression, ChainElement, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -51,8 +51,14 @@ impl Rule for NoNonNullAssertedOptionalChain { if let AstKind::TSNonNullExpression(non_null_expr) = node.kind() { let chain_span = match non_null_expr.expression.get_inner_expression() { Expression::ChainExpression(chain) => match &chain.expression { - ChainElement::MemberExpression(member) if member.optional() => { - Some(member.object().span()) + ChainElement::ComputedMemberExpression(member) if member.optional => { + Some(member.object.span()) + } + ChainElement::StaticMemberExpression(member) if member.optional => { + Some(member.object.span()) + } + ChainElement::PrivateFieldExpression(member) if member.optional => { + Some(member.object.span()) } ChainElement::CallExpression(call) if call.optional => Some(call.callee.span()), _ => None, @@ -60,7 +66,7 @@ impl Rule for NoNonNullAssertedOptionalChain { Expression::CallExpression(call) => { if call.optional && !is_parent_member_or_call(node, ctx) { Some(call.callee.span()) - } else if let Expression::MemberExpression(member) = &call.callee { + } else if let Some(member) = call.callee.as_member_expression() { if member.optional() && !is_parent_member_or_call(node, ctx) { Some(member.object().span()) } else { @@ -70,10 +76,13 @@ impl Rule for NoNonNullAssertedOptionalChain { None } } - Expression::MemberExpression(member) - if member.optional() && !is_parent_member_or_call(node, ctx) => - { - Some(member.object().span()) + expr @ match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); + if member_expr.optional() && !is_parent_member_or_call(node, ctx) { + Some(member_expr.object().span()) + } else { + None + } } _ => None, }; diff --git a/crates/oxc_linter/src/rules/typescript/no_this_alias.rs b/crates/oxc_linter/src/rules/typescript/no_this_alias.rs index 6f3b7ca2aaef8..e3d62deb2500f 100644 --- a/crates/oxc_linter/src/rules/typescript/no_this_alias.rs +++ b/crates/oxc_linter/src/rules/typescript/no_this_alias.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{AssignmentTarget, BindingPatternKind, Expression, SimpleAssignmentTarget}, + ast::{match_simple_assignment_target, AssignmentTarget, BindingPatternKind, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -127,32 +127,29 @@ impl Rule for NoThisAlias { return; } match &assignment.left { - AssignmentTarget::AssignmentTargetPattern(pat) => { + left @ (AssignmentTarget::ArrayAssignmentTarget(_) + | AssignmentTarget::ObjectAssignmentTarget(_)) => { if self.allow_destructuring { return; } - ctx.diagnostic(NoThisDestructureDiagnostic(pat.span())); + ctx.diagnostic(NoThisDestructureDiagnostic(left.span())); } - AssignmentTarget::SimpleAssignmentTarget(pat) => match pat { - SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => { - if !self.allow_names.iter().any(|s| s.as_str() == id.name.as_str()) { - ctx.diagnostic(NoThisAliasDiagnostic(id.span)); - } + AssignmentTarget::AssignmentTargetIdentifier(id) => { + if !self.allow_names.iter().any(|s| s.as_str() == id.name.as_str()) { + ctx.diagnostic(NoThisAliasDiagnostic(id.span)); } - _ => { - if let Some(expr) = pat.get_expression() { - if let Some(id) = expr.get_identifier_reference() { - if !self - .allow_names - .iter() - .any(|s| s.as_str() == id.name.as_str()) - { - ctx.diagnostic(NoThisAliasDiagnostic(id.span)); - } + } + left @ match_simple_assignment_target!(AssignmentTarget) => { + let pat = left.to_simple_assignment_target(); + if let Some(expr) = pat.get_expression() { + if let Some(id) = expr.get_identifier_reference() { + if !self.allow_names.iter().any(|s| s.as_str() == id.name.as_str()) + { + ctx.diagnostic(NoThisAliasDiagnostic(id.span)); } } } - }, + } } } _ => {} diff --git a/crates/oxc_linter/src/rules/typescript/prefer_enum_initializers.rs b/crates/oxc_linter/src/rules/typescript/prefer_enum_initializers.rs index c986df5b9cb23..f44e70635a51b 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_enum_initializers.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_enum_initializers.rs @@ -41,7 +41,7 @@ impl Rule for PreferEnumInitializers { for (index, member) in decl.members.iter().enumerate() { if member.initializer.is_none() { - if let TSEnumMemberName::Identifier(i) = &member.id { + if let TSEnumMemberName::StaticIdentifier(i) = &member.id { ctx.diagnostic(PreferEnumInitializersDiagnostic( i.name.to_compact_str(), index + 1, 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 da43b176beb16..f2f30487129f1 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs @@ -2,7 +2,7 @@ use oxc_ast::ast::{ AssignmentTarget, BindingPatternKind, Expression, ForStatementInit, SimpleAssignmentTarget, VariableDeclarationKind, }; -use oxc_ast::AstKind; +use oxc_ast::{ast::match_member_expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -72,9 +72,7 @@ impl<'a> ExpressionExt for Expression<'a> { }, Expression::AssignmentExpression(expr) => { if !matches!(&expr.left, - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(id) - ) + AssignmentTarget::AssignmentTargetIdentifier(id) if id.name == var_name ) { return false; @@ -145,18 +143,20 @@ impl Rule for PreferForOf { } let array_name = { - let Expression::MemberExpression(mem_expr) = &test_expr.right else { return }; + let Some(mem_expr) = test_expr.right.as_member_expression() else { return }; if !matches!(mem_expr.static_property_name(), Some(prop_name) if prop_name == "length") { return; } - match &mem_expr.object() { + match mem_expr.object() { Expression::Identifier(id) => id.name.as_str(), - Expression::MemberExpression(mem_expr) => match mem_expr.static_property_name() { - Some(array_name) => array_name, - None => return, - }, + expr @ match_member_expression!(Expression) => { + match expr.to_member_expression().static_property_name() { + Some(array_name) => array_name, + None => return, + } + } _ => return, } }; @@ -198,10 +198,12 @@ impl Rule for PreferForOf { let AstKind::MemberExpression(mem_expr) = parent_kind else { return true }; match mem_expr.object() { Expression::Identifier(id) => id.name.as_str() != array_name, - Expression::MemberExpression(mem_expr) => match mem_expr.static_property_name() { - Some(prop_name) => prop_name != array_name, - None => true, - }, + expr if expr.is_member_expression() => { + match expr.to_member_expression().static_property_name() { + Some(prop_name) => prop_name != array_name, + None => true, + } + } _ => true, } }) { diff --git a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs index 5993a5c48aad1..c23e905f01a8f 100644 --- a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs +++ b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use oxc_ast::{ - ast::{Declaration, ModuleDeclaration, Statement, TSModuleReference}, + ast::{Statement, TSModuleReference}, AstKind, }; use oxc_diagnostics::{ @@ -133,30 +133,26 @@ impl Rule for TripleSlashReference { if !refs_for_import.is_empty() { for stmt in &program.body { match stmt { - Statement::Declaration(Declaration::TSImportEqualsDeclaration(decl)) => { - match decl.module_reference { - TSModuleReference::ExternalModuleReference(ref mod_ref) => { - if let Some(v) = - refs_for_import.get(mod_ref.expression.value.as_str()) - { - ctx.diagnostic(TripleSlashReferenceDiagnostic( - mod_ref.expression.value.to_string(), - *v, - )); - } - } - TSModuleReference::TypeName(_) => {} - } - } - Statement::ModuleDeclaration(st) => { - if let ModuleDeclaration::ImportDeclaration(ref decl) = **st { - if let Some(v) = refs_for_import.get(decl.source.value.as_str()) { + Statement::TSImportEqualsDeclaration(decl) => match decl.module_reference { + TSModuleReference::ExternalModuleReference(ref mod_ref) => { + if let Some(v) = refs_for_import.get(mod_ref.expression.value.as_str()) + { ctx.diagnostic(TripleSlashReferenceDiagnostic( - decl.source.value.to_string(), + mod_ref.expression.value.to_string(), *v, )); } } + TSModuleReference::IdentifierReference(_) + | TSModuleReference::QualifiedName(_) => {} + }, + Statement::ImportDeclaration(decl) => { + if let Some(v) = refs_for_import.get(decl.source.value.as_str()) { + ctx.diagnostic(TripleSlashReferenceDiagnostic( + decl.source.value.to_string(), + *v, + )); + } } _ => {} } diff --git a/crates/oxc_linter/src/rules/unicorn/catch_error_name.rs b/crates/oxc_linter/src/rules/unicorn/catch_error_name.rs index 17230c302d79c..ff1e35178b517 100644 --- a/crates/oxc_linter/src/rules/unicorn/catch_error_name.rs +++ b/crates/oxc_linter/src/rules/unicorn/catch_error_name.rs @@ -112,7 +112,7 @@ impl Rule for CatchErrorName { } if let AstKind::CallExpression(call_expr) = node.kind() { - if let Expression::MemberExpression(member_expr) = &call_expr.callee { + if let Some(member_expr) = call_expr.callee.as_member_expression() { if member_expr.static_property_name() == Some("catch") { if let Some(arg0) = call_expr.arguments.first() { if let Some(diagnostic) = self.check_function_arguments(arg0, ctx) { @@ -142,8 +142,7 @@ impl CatchErrorName { arg0: &Argument, ctx: &LintContext, ) -> Option { - let Argument::Expression(expr) = arg0 else { return None }; - + let expr = arg0.as_expression()?; let expr = expr.without_parenthesized(); if let Expression::ArrowFunctionExpression(arrow_expr) = expr { diff --git a/crates/oxc_linter/src/rules/unicorn/error_message.rs b/crates/oxc_linter/src/rules/unicorn/error_message.rs index 6ae019376c1a5..77216bc770c48 100644 --- a/crates/oxc_linter/src/rules/unicorn/error_message.rs +++ b/crates/oxc_linter/src/rules/unicorn/error_message.rs @@ -72,13 +72,11 @@ impl Rule for ErrorMessage { let message_argument_idx = usize::from(constructor_name.as_str() == "AggregateError"); // If message is `SpreadElement` or there is `SpreadElement` before message - if args.iter().enumerate().any(|(i, arg)| { - i <= message_argument_idx - && match arg { - Argument::Expression(_) => false, - Argument::SpreadElement(_) => true, - } - }) { + if args + .iter() + .enumerate() + .any(|(i, arg)| i <= message_argument_idx && matches!(arg, Argument::SpreadElement(_))) + { return; } @@ -91,28 +89,21 @@ impl Rule for ErrorMessage { )); }; - let arg = match arg { - Argument::Expression(v) => v, - Argument::SpreadElement(_) => { - return; - } - }; - match arg { - Expression::StringLiteral(lit) => { + Argument::StringLiteral(lit) => { if lit.value.is_empty() { ctx.diagnostic(ErrorMessageDiagnostic::EmptyMessage(lit.span)); } } - Expression::TemplateLiteral(template_lit) => { + Argument::TemplateLiteral(template_lit) => { if template_lit.span.source_text(ctx.source_text()).len() == 2 { ctx.diagnostic(ErrorMessageDiagnostic::EmptyMessage(template_lit.span)); } } - Expression::ObjectExpression(object_expr) => { + Argument::ObjectExpression(object_expr) => { ctx.diagnostic(ErrorMessageDiagnostic::NotString(object_expr.span)); } - Expression::ArrayExpression(array_expr) => { + Argument::ArrayExpression(array_expr) => { ctx.diagnostic(ErrorMessageDiagnostic::NotString(array_expr.span)); } _ => {} 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 3a5488e3124d2..94d3f3a8b9036 100644 --- a/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs +++ b/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs @@ -248,9 +248,10 @@ impl ExplicitLengthCheck { impl Rule for ExplicitLengthCheck { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::MemberExpression(MemberExpression::StaticMemberExpression( - static_member_expr @ StaticMemberExpression { object, property, .. }, + static_member_expr, )) = node.kind() { + let StaticMemberExpression { object, property, .. } = &**static_member_expr; if property.name != "length" && property.name != "size" { return; } 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 6e46ca5904284..e6eb936184450 100644 --- a/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs +++ b/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs @@ -1,4 +1,7 @@ -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::{ + ast::{match_member_expression, Expression}, + AstKind, +}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -108,7 +111,8 @@ fn is_expr_global_builtin<'a, 'b>( } Some(ident.name.as_str()) } - Expression::MemberExpression(member_expr) => { + match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); let Expression::Identifier(ident) = member_expr.object() else { return None }; if !GLOBAL_OBJECT_NAMES.contains(ident.name.as_str()) { diff --git a/crates/oxc_linter/src/rules/unicorn/no_array_for_each.rs b/crates/oxc_linter/src/rules/unicorn/no_array_for_each.rs index 10a63c8f9bef6..3f153251745dc 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_array_for_each.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_array_for_each.rs @@ -1,4 +1,7 @@ -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::{ + ast::{match_member_expression, Expression}, + AstKind, +}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -72,8 +75,8 @@ impl Rule for NoArrayForEach { return; } } - Expression::MemberExpression(v) => { - if let Some(name) = v.static_property_name() { + match_member_expression!(Expression) => { + if let Some(name) = object.to_member_expression().static_property_name() { if IGNORED_OBJECTS.contains(name) { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/no_array_reduce.rs b/crates/oxc_linter/src/rules/unicorn/no_array_reduce.rs index 60a021f747ca7..9367a24fa5877 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_array_reduce.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_array_reduce.rs @@ -83,7 +83,7 @@ impl Rule for NoArrayReduce { ctx.diagnostic(NoArrayReduceDiagnostic(span)); } - if let Expression::MemberExpression(member_expr_obj) = member_expr.object() { + if let Some(member_expr_obj) = member_expr.object().as_member_expression() { if is_method_call(call_expr, None, Some(&["call", "apply"]), None, None) && !member_expr.optional() && !member_expr.is_computed() @@ -99,13 +99,13 @@ impl Rule for NoArrayReduce { } fn is_simple_operation(node: &CallExpression) -> bool { - let Some(Argument::Expression(callback_arg)) = node.arguments.first() else { + let Some(callback_arg) = node.arguments.first() else { return false; }; let function_body = match callback_arg { // `array.reduce((accumulator, element) => accumulator + element)` - Expression::ArrowFunctionExpression(callback) => &callback.body, - Expression::FunctionExpression(callback) => { + Argument::ArrowFunctionExpression(callback) => &callback.body, + Argument::FunctionExpression(callback) => { let Some(body) = &callback.body else { return false; }; diff --git a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs index 4371569a221b0..4d537a208e979 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, ArrayExpressionElement, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -66,7 +63,7 @@ impl Rule for NoAwaitInPromiseMethods { return; } - let Some(Argument::Expression(first_argument)) = call_expr.arguments.first() else { + let Some(first_argument) = call_expr.arguments[0].as_expression() else { return; }; let first_argument = first_argument.without_parenthesized(); @@ -75,7 +72,7 @@ impl Rule for NoAwaitInPromiseMethods { }; for element in &first_argument_array_expr.elements { - if let ArrayExpressionElement::Expression(element_expr) = element { + if let Some(element_expr) = element.as_expression() { if let Expression::AwaitExpression(await_expr) = element_expr.without_parenthesized() { diff --git a/crates/oxc_linter/src/rules/unicorn/no_console_spaces.rs b/crates/oxc_linter/src/rules/unicorn/no_console_spaces.rs index 086eb5d18979b..54c7ffcc65d4e 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_console_spaces.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_console_spaces.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -66,7 +63,7 @@ impl Rule for NoConsoleSpaces { let call_expr_arg_len = call_expr.arguments.len(); for (i, arg) in call_expr.arguments.iter().enumerate() { - if let Argument::Expression(expression_arg) = &arg { + if let Some(expression_arg) = arg.as_expression() { let (literal_raw, is_template_lit) = match expression_arg { Expression::StringLiteral(string_lit) => { let literal_raw = string_lit.value.as_str(); diff --git a/crates/oxc_linter/src/rules/unicorn/no_document_cookie.rs b/crates/oxc_linter/src/rules/unicorn/no_document_cookie.rs index 7daf02e38baeb..37fc882115503 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_document_cookie.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_document_cookie.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{AssignmentTarget, Expression, SimpleAssignmentTarget}, + ast::{match_member_expression, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -59,10 +59,7 @@ impl Rule for NoDocumentCookie { return; }; - let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(ident), - ) = &assignment_expr.left - else { + let Some(ident) = assignment_expr.left.as_member_expression() else { return; }; @@ -105,7 +102,8 @@ fn is_document_cookie_reference<'a, 'b>( } true } - Expression::MemberExpression(member_expr) => { + match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); let Some(static_prop_name) = member_expr.static_property_name() else { return false }; if static_prop_name != "document" { return false; diff --git a/crates/oxc_linter/src/rules/unicorn/no_invalid_remove_event_listener.rs b/crates/oxc_linter/src/rules/unicorn/no_invalid_remove_event_listener.rs index 0254b88de93f9..e413b49c1f3e4 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_invalid_remove_event_listener.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_invalid_remove_event_listener.rs @@ -1,4 +1,3 @@ -use oxc_ast::ast::Expression; use oxc_ast::{ ast::{Argument, MemberExpression}, AstKind, @@ -76,18 +75,18 @@ impl Rule for NoInvalidRemoveEventListener { return; } - let Some(Argument::Expression(listener)) = call_expr.arguments.get(1) else { return }; + let Some(listener) = call_expr.arguments.get(1) else { return }; if !matches!( listener, - Expression::FunctionExpression(_) - | Expression::ArrowFunctionExpression(_) - | Expression::CallExpression(_) + Argument::FunctionExpression(_) + | Argument::ArrowFunctionExpression(_) + | Argument::CallExpression(_) ) { return; } - if let Expression::CallExpression(call_expr) = listener { + if let Argument::CallExpression(call_expr) = listener { match call_expr.callee.get_member_expr() { Some(MemberExpression::StaticMemberExpression(v)) => { if v.property.name != "bind" { @@ -103,13 +102,13 @@ impl Rule for NoInvalidRemoveEventListener { let listener_span = listener.span(); let listener_span = if listener_span.size() > 20 { match listener { - Expression::FunctionExpression(func_expr) => { + Argument::FunctionExpression(func_expr) => { Span::new(func_expr.span.start, func_expr.params.span.end) } - Expression::ArrowFunctionExpression(arrow_expr) => { + Argument::ArrowFunctionExpression(arrow_expr) => { Span::new(arrow_expr.span.start, arrow_expr.body.span.start) } - Expression::CallExpression(_) => listener_span, + Argument::CallExpression(_) => listener_span, _ => unreachable!(), } } else { diff --git a/crates/oxc_linter/src/rules/unicorn/no_null.rs b/crates/oxc_linter/src/rules/unicorn/no_null.rs index a7a4c92a687c0..fb5e158b0b290 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_null.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_null.rs @@ -55,7 +55,7 @@ declare_oxc_lint!( fn match_null_arg(call_expr: &CallExpression, index: usize, span: Span) -> bool { call_expr.arguments.get(index).map_or(false, |arg| { - if let Argument::Expression(Expression::NullLiteral(null_lit)) = arg { + if let Argument::NullLiteral(null_lit) = arg { return null_lit.span == span; } @@ -121,7 +121,7 @@ fn match_call_expression_pass_case(null_literal: &NullLiteral, call_expr: &CallE // `Object.create(null)`, `Object.create(null, foo)` if is_method_call(call_expr, Some(&["Object"]), Some(&["create"]), Some(1), Some(2)) && !call_expr.optional - && !matches!(&call_expr.callee, Expression::MemberExpression(member_expr) if member_expr.is_computed()) + && !matches!(&call_expr.callee, Expression::ComputedMemberExpression(_)) && match_null_arg(call_expr, 0, null_literal.span) { return true; @@ -148,7 +148,7 @@ fn match_call_expression_pass_case(null_literal: &NullLiteral, call_expr: &CallE .iter() .any(|argument| matches!(argument, Argument::SpreadElement(_))) && !call_expr.optional - && !matches!(&call_expr.callee, Expression::MemberExpression(member_expr) if member_expr.is_computed()) + && !matches!(&call_expr.callee, Expression::ComputedMemberExpression(_)) && match_null_arg(call_expr, 1, null_literal.span) { return true; diff --git a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs index 5cd8b5fdcf94d..1799e2a43d13e 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, ArrayExpressionElement, CallExpression, Expression}, + ast::{CallExpression, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -87,7 +87,7 @@ fn is_promise_method_with_single_element_array(call_expr: &CallExpression) -> bo return false; } - let Some(Argument::Expression(first_argument)) = call_expr.arguments.first() else { + let Some(first_argument) = call_expr.arguments[0].as_expression() else { return false; }; let first_argument = first_argument.without_parenthesized(); @@ -99,7 +99,7 @@ fn is_promise_method_with_single_element_array(call_expr: &CallExpression) -> bo return false; } - matches!(first_argument_array_expr.elements[0], ArrayExpressionElement::Expression(_)) + first_argument_array_expr.elements[0].is_expression() } #[test] diff --git a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs index 7be595dbf30fa..3811380b2b101 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs @@ -1,9 +1,8 @@ use oxc_ast::{ ast::{ - Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, - BindingPatternKind, CallExpression, Declaration, Expression, MemberExpression, - ModuleDeclaration, ModuleExportName, ObjectPropertyKind, PropertyKey, - SimpleAssignmentTarget, VariableDeclarator, + match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, + BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration, + ModuleExportName, ObjectPropertyKind, PropertyKey, VariableDeclarator, }, AstKind, }; @@ -122,24 +121,18 @@ impl Rule for NoThenable { } AstKind::CallExpression(expr) => check_call_expression(expr, ctx), // foo.then = ... - AstKind::AssignmentExpression(AssignmentExpression { - left: - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(target), - ), - .. - }) => match &**target { - MemberExpression::ComputedMemberExpression(expr) => { + AstKind::AssignmentExpression(AssignmentExpression { left, .. }) => match left { + AssignmentTarget::ComputedMemberExpression(expr) => { if let Some(span) = check_expression(&expr.expression, ctx) { ctx.diagnostic(NoThenableDiagnostic::Class(span)); } } - MemberExpression::StaticMemberExpression(expr) => { + AssignmentTarget::StaticMemberExpression(expr) => { if expr.property.name == "then" { ctx.diagnostic(NoThenableDiagnostic::Class(expr.span)); } } - MemberExpression::PrivateFieldExpression(_) => {} + _ => {} }, _ => {} } @@ -153,8 +146,8 @@ fn check_call_expression(expr: &CallExpression, ctx: &LintContext) { !expr.optional && expr.arguments.len() >= 3 && !matches!(expr.arguments[0], Argument::SpreadElement(_)) - && match expr.callee { - Expression::MemberExpression(ref me) => { + && match expr.callee.as_member_expression() { + Some(me) => { me.object().get_identifier_reference().map_or(false, |ident_ref| { ident_ref.name == "Reflect" || ident_ref.name == "Object" }) && me.static_property_name() == Some("defineProperty") @@ -163,7 +156,7 @@ fn check_call_expression(expr: &CallExpression, ctx: &LintContext) { _ => false, } } { - } else if let Argument::Expression(inner) = &expr.arguments[1] { + } else if let Some(inner) = expr.arguments[1].as_expression() { if let Some(span) = check_expression(inner, ctx) { ctx.diagnostic(NoThenableDiagnostic::Object(span)); } @@ -173,9 +166,9 @@ fn check_call_expression(expr: &CallExpression, ctx: &LintContext) { if !{ !expr.optional && expr.arguments.len() == 1 - && matches!(expr.arguments[0], Argument::Expression(Expression::ArrayExpression(_))) - && match expr.callee { - Expression::MemberExpression(ref me) => { + && matches!(expr.arguments[0], Argument::ArrayExpression(_)) + && match expr.callee.as_member_expression() { + Some(me) => { me.object() .get_identifier_reference() .map_or(false, |ident_ref| ident_ref.name == "Object") @@ -185,14 +178,14 @@ fn check_call_expression(expr: &CallExpression, ctx: &LintContext) { _ => false, } } { - } else if let Argument::Expression(Expression::ArrayExpression(outer)) = &expr.arguments[0] { + } else if let Argument::ArrayExpression(outer) = &expr.arguments[0] { for inner in &outer.elements { // inner item is array - if let ArrayExpressionElement::Expression(Expression::ArrayExpression(inner)) = inner { + if let ArrayExpressionElement::ArrayExpression(inner) = inner { if inner.elements.len() > 0 && !matches!(inner.elements[0], ArrayExpressionElement::SpreadElement(_)) { - if let ArrayExpressionElement::Expression(ref expr) = inner.elements[0] { + if let Some(expr) = inner.elements[0].as_expression() { if let Some(span) = check_expression(expr, ctx) { ctx.diagnostic(NoThenableDiagnostic::Object(span)); } @@ -273,8 +266,8 @@ fn check_expression(expr: &Expression, ctx: &LintContext<'_>) -> Option Option { match key { - PropertyKey::Identifier(ident) if ident.name == "then" => Some(ident.span), - PropertyKey::Expression(expr) => check_expression(expr, ctx), + PropertyKey::StaticIdentifier(ident) if ident.name == "then" => Some(ident.span), + match_expression!(PropertyKey) => check_expression(key.to_expression(), ctx), _ => None, } } diff --git a/crates/oxc_linter/src/rules/unicorn/no_this_assignment.rs b/crates/oxc_linter/src/rules/unicorn/no_this_assignment.rs index d1b7d8a61a1cc..df61126e8931f 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_this_assignment.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_this_assignment.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{AssignmentTarget, BindingPatternKind, Expression, SimpleAssignmentTarget}, + ast::{AssignmentTarget, BindingPatternKind, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -89,14 +89,7 @@ impl Rule for NoThisAssignment { return; } - let AssignmentTarget::SimpleAssignmentTarget(simple_assignment_target) = - &assignment_expr.left - else { - return; - }; - - let SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) = - simple_assignment_target + let AssignmentTarget::AssignmentTargetIdentifier(ident) = &assignment_expr.left else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/no_unreadable_array_destructuring.rs b/crates/oxc_linter/src/rules/unicorn/no_unreadable_array_destructuring.rs index 8cc9b216c8c6b..334c409f5c1d5 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_unreadable_array_destructuring.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_unreadable_array_destructuring.rs @@ -1,8 +1,5 @@ use oxc_allocator::Vec; -use oxc_ast::{ - ast::{AssignmentTarget, AssignmentTargetPattern}, - AstKind, -}; +use oxc_ast::{ast::AssignmentTarget, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -65,9 +62,8 @@ impl Rule for NoUnreadableArrayDestructuring { } } - if let AstKind::AssignmentTarget(AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ArrayAssignmentTarget(array_pattern), - )) = node.kind() + if let AstKind::AssignmentTarget(AssignmentTarget::ArrayAssignmentTarget(array_pattern)) = + node.kind() { if is_unreadable_array_destructuring(&array_pattern.elements, &array_pattern.rest) { ctx.diagnostic(NoUnreadableArrayDestructuringDiagnostic(array_pattern.span)); diff --git a/crates/oxc_linter/src/rules/unicorn/no_useless_length_check.rs b/crates/oxc_linter/src/rules/unicorn/no_useless_length_check.rs index 044dbcbec403d..40c90e72213b3 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_useless_length_check.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_useless_length_check.rs @@ -89,9 +89,7 @@ fn is_useless_check<'a>( let l = match left.without_parenthesized() { Expression::BinaryExpression(expr) => { - let Expression::MemberExpression(left_expr) = expr.left.get_inner_expression() else { - return None; - }; + let left_expr = expr.left.get_inner_expression().as_member_expression()?; array_name = left_expr.object().get_identifier_reference()?.name.as_str(); binary_expression_span = Some(expr.span); @@ -115,9 +113,7 @@ fn is_useless_check<'a>( let r = match right.without_parenthesized() { Expression::BinaryExpression(expr) => { - let Expression::MemberExpression(left_expr) = expr.left.get_inner_expression() else { - return None; - }; + let left_expr = expr.left.get_inner_expression().as_member_expression()?; let ident_name = left_expr.object().get_identifier_reference()?.name.as_str(); if binary_expression_span.is_some() { return None; diff --git a/crates/oxc_linter/src/rules/unicorn/no_useless_spread.rs b/crates/oxc_linter/src/rules/unicorn/no_useless_spread.rs index 054c1dd712b68..974b694ca0aa3 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_useless_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_useless_spread.rs @@ -1,5 +1,8 @@ use oxc_ast::{ - ast::{Argument, ArrayExpression, ArrayExpressionElement, CallExpression, Expression}, + ast::{ + match_expression, Argument, ArrayExpression, ArrayExpressionElement, CallExpression, + Expression, + }, AstKind, }; use oxc_diagnostics::{ @@ -371,7 +374,7 @@ fn is_single_array_spread(node: &ArrayExpression) -> bool { fn innermost_paren_arg_span(arg: &Argument) -> Span { match arg { - Argument::Expression(expr) => expr.without_parenthesized().span(), + match_expression!(Argument) => arg.to_expression().without_parenthesized().span(), Argument::SpreadElement(spread_elem) => spread_elem.argument.span(), } } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_add_event_listener.rs b/crates/oxc_linter/src/rules/unicorn/prefer_add_event_listener.rs index 206ab572b0c41..c834bd4cca1a6 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_add_event_listener.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_add_event_listener.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{AssignmentTarget, SimpleAssignmentTarget}, - AstKind, -}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -46,10 +43,7 @@ impl Rule for PreferAddEventListener { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::AssignmentExpression(assignment_expr) = node.kind() else { return }; - let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(member_expr), - ) = &assignment_expr.left - else { + let Some(member_expr) = assignment_expr.left.as_member_expression() else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_array_flat.rs b/crates/oxc_linter/src/rules/unicorn/prefer_array_flat.rs index 332deb69c7271..1ee370df83886 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_flat.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_flat.rs @@ -79,11 +79,7 @@ fn check_array_flat_map_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintConte return; } - let Argument::Expression(first_argument) = call_expr.arguments.first().unwrap() else { - return; - }; - - let Expression::ArrowFunctionExpression(first_argument) = first_argument else { + let Argument::ArrowFunctionExpression(first_argument) = &call_expr.arguments[0] else { return; }; @@ -112,12 +108,10 @@ fn check_array_reduce_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext if !is_method_call(call_expr, None, Some(&["reduce"]), Some(2), Some(2)) { return; } - let Argument::Expression(Expression::ArrowFunctionExpression(first_argument)) = - call_expr.arguments.first().unwrap() - else { + let Argument::ArrowFunctionExpression(first_argument) = &call_expr.arguments[0] else { return; }; - let Argument::Expression(second_argument) = call_expr.arguments.get(1).unwrap() else { + let Some(second_argument) = call_expr.arguments[1].as_expression() else { return; }; @@ -152,9 +146,7 @@ fn check_array_reduce_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext // `array.reduce((a, b) => a.concat(b), [])` if let Expression::CallExpression(concat_call_expr) = &expr_stmt.expression { if is_method_call(concat_call_expr, None, Some(&["concat"]), Some(1), Some(1)) { - if let Argument::Expression(Expression::Identifier(first_argument_ident)) = - &concat_call_expr.arguments[0] - { + if let Argument::Identifier(first_argument_ident) = &concat_call_expr.arguments[0] { if first_argument_ident.name != second_parameter { return; } @@ -229,13 +221,13 @@ fn check_array_prototype_concat_case<'a>(call_expr: &CallExpression<'a>, ctx: &L return; }; - if let Expression::MemberExpression(member_expr_obj) = member_expr.object() { + if let Some(member_expr_obj) = member_expr.object().as_member_expression() { let is_call_call = is_method_call(call_expr, None, Some(&["call"]), Some(2), Some(2)); if (is_call_call || is_method_call(call_expr, None, Some(&["apply"]), Some(2), Some(2))) && is_prototype_property(member_expr_obj, "concat", Some("Array")) { - if let Argument::Expression(first_argument) = &call_expr.arguments[0] { + if let Some(first_argument) = call_expr.arguments[0].as_expression() { if is_empty_array_expression(first_argument) && (is_call_call || !matches!(call_expr.arguments.get(1), Some(Argument::SpreadElement(_)))) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_array_flat_map.rs b/crates/oxc_linter/src/rules/unicorn/prefer_array_flat_map.rs index c4456a081ddb0..c5afa1529505a 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_flat_map.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_flat_map.rs @@ -49,7 +49,7 @@ impl Rule for PreferArrayFlatMap { if !is_method_call(flat_call_expr, None, Some(&["flat"]), None, None) { return; } - let Expression::MemberExpression(member_expr) = &flat_call_expr.callee else { return }; + let Some(member_expr) = flat_call_expr.callee.as_member_expression() else { return }; let Expression::CallExpression(call_expr) = &member_expr.object().without_parenthesized() else { return; @@ -59,7 +59,7 @@ impl Rule for PreferArrayFlatMap { } if let Some(first_arg) = flat_call_expr.arguments.first() { - if let Argument::Expression(Expression::NumericLiteral(number_lit)) = first_arg { + if let Argument::NumericLiteral(number_lit) = first_arg { if number_lit.raw != "1" { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_array_some.rs b/crates/oxc_linter/src/rules/unicorn/prefer_array_some.rs index c214f1387171e..96226ad7ccb7b 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_some.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_some.rs @@ -83,8 +83,8 @@ impl Rule for PreferArraySome { return; } - let Expression::MemberExpression(left_member_expr) = - &bin_expr.left.without_parenthesized() + let Some(left_member_expr) = + bin_expr.left.without_parenthesized().as_member_expression() else { return; }; @@ -107,8 +107,8 @@ impl Rule for PreferArraySome { return; } - let Some(Argument::Expression(first_filter_call_arg)) = - left_call_expr.arguments.first() + let Some(first_filter_call_arg) = + left_call_expr.arguments.first().and_then(Argument::as_expression) else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_blob_reading_methods.rs b/crates/oxc_linter/src/rules/unicorn/prefer_blob_reading_methods.rs index 60e5c3372a26c..88bf37c286e0a 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_blob_reading_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_blob_reading_methods.rs @@ -1,4 +1,4 @@ -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -50,7 +50,7 @@ impl Rule for PreferBlobReadingMethods { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::CallExpression(call_expr) = node.kind() else { return }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { return }; + let Some(member_expr) = call_expr.callee.as_member_expression() else { return }; if member_expr.is_computed() || member_expr.optional() diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs b/crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs index 6e691d1feba8b..2d5d47821a368 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs @@ -1,4 +1,4 @@ -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -46,7 +46,7 @@ impl Rule for PreferCodePoint { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::CallExpression(call_expr) = node.kind() else { return }; - let Expression::MemberExpression(memb_expr) = &call_expr.callee else { return }; + let Some(memb_expr) = call_expr.callee.as_member_expression() else { return }; if memb_expr.is_computed() || memb_expr.optional() || call_expr.optional { return; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs b/crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs index cfc4b516a4038..017fb2b8a74bc 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs @@ -61,8 +61,8 @@ impl Rule for PreferDateNow { match node.kind() { AstKind::CallExpression(call_expr) => { // `new Date().{getTime,valueOf}()` - if let Expression::MemberExpression(member_expr) = - &call_expr.callee.without_parenthesized() + if let Some(member_expr) = + call_expr.callee.without_parenthesized().as_member_expression() { if call_expr.arguments.is_empty() && !member_expr.is_computed() @@ -81,7 +81,9 @@ impl Rule for PreferDateNow { if matches!(ident.name.as_str(), "Number" | "BigInt") && call_expr.arguments.len() == 1 { - if let Some(Argument::Expression(expr)) = call_expr.arguments.first() { + if let Some(expr) = + call_expr.arguments.first().and_then(Argument::as_expression) + { if is_new_date(expr.without_parenthesized()) { ctx.diagnostic( PreferDateNowDiagnostic::PreferDateNowOverNumberDateObject( diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_dataset.rs b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_dataset.rs index 666b196fc1685..25a19fe303a0d 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_dataset.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_dataset.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Argument, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -91,8 +88,7 @@ impl Rule for PreferDomNodeDataset { _ => return, } - let Argument::Expression(Expression::StringLiteral(string_lit)) = &call_expr.arguments[0] - else { + let Argument::StringLiteral(string_lit) = &call_expr.arguments[0] else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_remove.rs b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_remove.rs index aa2ee522be304..b9c6a2d2f394e 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_remove.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_remove.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -62,7 +59,7 @@ impl Rule for PreferDomNodeRemove { return; } - let Argument::Expression(expr) = &call_expr.arguments[0] else { + let Some(expr) = call_expr.arguments[0].as_expression() else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs b/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs index aed9e7ea9d501..6ee6520bbe0ee 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, MemberExpression}, + ast::{Argument, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -66,11 +66,7 @@ impl Rule for PreferModernDomApis { return; }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { - return; - }; - - let MemberExpression::StaticMemberExpression(member_expr) = &**member_expr else { + let Expression::StaticMemberExpression(member_expr) = &call_expr.callee else { return; }; let method = member_expr.property.name.as_str(); @@ -84,7 +80,7 @@ impl Rule for PreferModernDomApis { ) && call_expr .arguments .iter() - .all(|argument| matches!(argument, Argument::Expression(expr) if !expr.is_undefined())) + .all(|argument| matches!(argument.as_expression(), Some(expr) if !expr.is_undefined())) && matches!(member_expr.object, Expression::Identifier(_)) && !call_expr.optional { @@ -106,7 +102,7 @@ impl Rule for PreferModernDomApis { Some(2), Some(2), ) { - if let Argument::Expression(Expression::StringLiteral(lit)) = &call_expr.arguments[0] { + if let Argument::StringLiteral(lit) = &call_expr.arguments[0] { for (position, replacer) in &POSITION_REPLACERS { if lit.value == position { ctx.diagnostic(PreferModernDomApisDiagnostic( diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs b/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs index 0b7d26dc37d8e..3e09af6e6b991 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs @@ -90,7 +90,7 @@ impl Rule for PreferModernMathApis { return; }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -98,7 +98,7 @@ impl Rule for PreferModernMathApis { return; }; - let Argument::Expression(arg) = &call_expr.arguments[0] else { return }; + let Some(arg) = call_expr.arguments[0].as_expression() else { return }; let expressions = flat_plus_expression(arg); if expressions.iter().any(|expr| !is_pow_2_expression(expr, ctx)) { @@ -135,7 +135,7 @@ fn check_prefer_log<'a>(expr: &BinaryExpression<'a>, ctx: &LintContext<'a>) { return; } - let Expression::MemberExpression(member_expr) = &call_expr.callee else { + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -143,7 +143,7 @@ fn check_prefer_log<'a>(expr: &BinaryExpression<'a>, ctx: &LintContext<'a>) { return; }; - let Expression::MemberExpression(member_expr) = &expr.right else { + let Some(member_expr) = expr.right.as_member_expression() else { return; }; @@ -194,7 +194,7 @@ fn check_multiplication<'a, 'b>( return; } - let Expression::MemberExpression(member_expr) = &call_expr.callee else { + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -202,7 +202,7 @@ fn check_multiplication<'a, 'b>( return; }; - let Expression::MemberExpression(member_expr) = right else { + let Some(member_expr) = right.as_member_expression() else { return; }; 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 90ddae059d709..68026954c828f 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 @@ -184,7 +184,7 @@ fn is_matching_native_coercion_function_call( let fn_name = NATIVE_COERCION_FUNCTION_NAMES.get_key(callee_ident.name.as_str())?; - let Argument::Expression(Expression::Identifier(arg_ident)) = &call_expr.arguments[0] else { + let Argument::Identifier(arg_ident) = &call_expr.arguments[0] else { return None; }; @@ -213,7 +213,7 @@ fn check_array_callback_methods( return false; } - let Expression::MemberExpression(callee_member_expr) = &call_expr.callee else { + let Some(callee_member_expr) = call_expr.callee.as_member_expression() else { return false; }; if callee_member_expr.optional() { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_node_protocol.rs b/crates/oxc_linter/src/rules/unicorn/prefer_node_protocol.rs index 4afc1dcb29dd7..f756d2d5cd1b5 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_node_protocol.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_node_protocol.rs @@ -80,7 +80,7 @@ fn get_static_require_arg<'a>( ) -> Option<(Atom<'a>, Span)> { let Expression::Identifier(ref id) = call.callee else { return None }; match call.arguments.as_slice() { - [Argument::Expression(Expression::StringLiteral(str))] if id.name == "require" => ctx + [Argument::StringLiteral(str)] if id.name == "require" => ctx .semantic() .scopes() .root_unresolved_references() 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 13c0f2083c9dd..cea4a69498eb0 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs @@ -1,4 +1,7 @@ -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::{ + ast::{match_member_expression, Expression}, + AstKind, +}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -122,7 +125,8 @@ impl Rule for PreferNumberProperties { fn extract_ident_from_expression<'b>(expr: &'b Expression<'_>) -> Option<&'b str> { match expr { Expression::Identifier(ident_name) => Some(ident_name.name.as_str()), - Expression::MemberExpression(member_expr) => { + match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); let Expression::Identifier(ident_name) = member_expr.object() else { return None; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_prototype_methods.rs b/crates/oxc_linter/src/rules/unicorn/prefer_prototype_methods.rs index 606175b588e23..aa3744f05c78e 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_prototype_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_prototype_methods.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -61,19 +58,18 @@ impl Rule for PreferPrototypeMethods { if call_expr.optional { return; } - if let Expression::MemberExpression(member_expr) = &call_expr.callee.without_parenthesized() - { - if member_expr.is_computed() || member_expr.optional() { - return; - } - } + match call_expr.callee.without_parenthesized() { + Expression::StaticMemberExpression(member_expr) if !member_expr.optional => {} + Expression::PrivateFieldExpression(member_expr) if !member_expr.optional => {} + _ => return, + }; let mut method_expr: Option<&Expression> = None; // `Reflect.apply([].foo, …)` // `Reflect.apply({}.foo, …)` if is_method_call(call_expr, Some(&["Reflect"]), Some(&["apply"]), Some(1), None) { - if let Argument::Expression(argument_expr) = &call_expr.arguments[0] { + if let Some(argument_expr) = call_expr.arguments[0].as_expression() { method_expr = Some(argument_expr.without_parenthesized()); } } @@ -87,7 +83,7 @@ impl Rule for PreferPrototypeMethods { } let Some(method_expr) = method_expr else { return }; - let Expression::MemberExpression(method_expr) = method_expr else { return }; + let Some(method_expr) = method_expr.as_member_expression() else { return }; let object_expr = method_expr.object().without_parenthesized(); if !is_empty_array_expression(object_expr) && !is_empty_object_expression(object_expr) { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs b/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs index 2f694acc32afe..892f93a22de97 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs @@ -1,8 +1,5 @@ use miette::diagnostic; -use oxc_ast::{ - ast::{Argument, Expression}, - AstKind, -}; +use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -60,7 +57,7 @@ impl Rule for PreferQuerySelector { return; } - let Expression::MemberExpression(member_expr) = &call_expr.callee else { + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -71,7 +68,7 @@ impl Rule for PreferQuerySelector { return; } - let Argument::Expression(argument_expr) = call_expr.arguments.first().unwrap() else { + let Some(argument_expr) = call_expr.arguments[0].as_expression() else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_reflect_apply.rs b/crates/oxc_linter/src/rules/unicorn/prefer_reflect_apply.rs index 84bee0e8016cf..169d08e370939 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_reflect_apply.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_reflect_apply.rs @@ -45,9 +45,9 @@ declare_oxc_lint!( fn is_apply_signature(first_arg: &Argument, second_arg: &Argument) -> bool { match first_arg { - Argument::Expression(Expression::ThisExpression(_) | Expression::NullLiteral(_)) => { - matches!(second_arg, Argument::Expression(Expression::ArrayExpression(_))) - || matches!(second_arg, Argument::Expression(Expression::Identifier(ident)) if ident.name == "arguments") + Argument::ThisExpression(_) | Argument::NullLiteral(_) => { + matches!(second_arg, Argument::ArrayExpression(_)) + || matches!(second_arg, Argument::Identifier(ident) if ident.name == "arguments") } _ => false, } @@ -63,7 +63,7 @@ impl Rule for PreferReflectApply { return; }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { + let Some(member_expr) = call_expr.callee.as_member_expression() else { return; }; @@ -85,11 +85,11 @@ impl Rule for PreferReflectApply { } if is_static_property_name_equal(member_expr, "call") { - let Expression::MemberExpression(member_expr_obj) = member_expr.object() else { + let Some(member_expr_obj) = member_expr.object().as_member_expression() else { return; }; if is_static_property_name_equal(member_expr_obj, "apply") { - let Expression::MemberExpression(member_expr_obj_obj) = member_expr_obj.object() + let Some(member_expr_obj_obj) = member_expr_obj.object().as_member_expression() else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_regexp_test.rs b/crates/oxc_linter/src/rules/unicorn/prefer_regexp_test.rs index d6ea656e77ef8..81790a546ff8d 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_regexp_test.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_regexp_test.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, MemberExpression}, + ast::{Expression, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -125,7 +125,7 @@ impl Rule for PreferRegexpTest { return; } - if let Argument::Expression(expr) = &call_expr.arguments[0] { + if let Some(expr) = call_expr.arguments[0].as_expression() { if expr.is_literal() && !matches!(expr, Expression::RegExpLiteral(_)) { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_spread.rs b/crates/oxc_linter/src/rules/unicorn/prefer_spread.rs index 09157e5d8737f..ccc1fcb63b35d 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_spread.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression}, + ast::{match_member_expression, Expression}, AstKind, }; use oxc_diagnostics::{ @@ -46,7 +46,7 @@ impl Rule for PreferSpread { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::CallExpression(call_expr) = node.kind() else { return }; - let Expression::MemberExpression(member_expr) = &call_expr.callee.without_parenthesized() + let Some(member_expr) = call_expr.callee.without_parenthesized().as_member_expression() else { return; }; @@ -60,7 +60,7 @@ impl Rule for PreferSpread { return; } - let Argument::Expression(expr) = &call_expr.arguments[0] else { return }; + let Some(expr) = call_expr.arguments[0].as_expression() else { return }; if matches!(expr.without_parenthesized(), Expression::ObjectExpression(_)) { return; } @@ -106,7 +106,7 @@ impl Rule for PreferSpread { } if let Some(first_arg) = call_expr.arguments.first() { - let Argument::Expression(first_arg) = first_arg else { return }; + let Some(first_arg) = first_arg.as_expression() else { return }; if let Expression::NumericLiteral(num_lit) = first_arg.without_parenthesized() { if num_lit.value != 0.0 { return; @@ -139,7 +139,7 @@ impl Rule for PreferSpread { return; } - let Argument::Expression(expr) = &call_expr.arguments[0] else { return }; + let Some(expr) = call_expr.arguments[0].as_expression() else { return }; let Expression::StringLiteral(string_lit) = expr.without_parenthesized() else { return; }; @@ -184,8 +184,7 @@ fn is_not_array(expr: &Expression) -> bool { } if let Expression::CallExpression(call_expr) = expr { - if let Expression::MemberExpression(member_expr) = &call_expr.callee.without_parenthesized() - { + if let Some(member_expr) = call_expr.callee.without_parenthesized().as_member_expression() { if Some("join") == member_expr.static_property_name() && call_expr.arguments.len() < 2 { return true; } @@ -196,8 +195,8 @@ fn is_not_array(expr: &Expression) -> bool { let ident = match expr.without_parenthesized() { Expression::Identifier(ident) => ident.name.as_str(), - Expression::MemberExpression(member_expr) => { - if let Some(v) = member_expr.static_property_name() { + expr @ match_member_expression!(Expression) => { + if let Some(v) = expr.to_member_expression().static_property_name() { v } else { return false; diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs index cc5b3c121c181..0b5a83dfca151 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{Argument, Expression, MemberExpression, RegExpFlags}, + ast::{Argument, MemberExpression, RegExpFlags}, AstKind, }; use oxc_diagnostics::{ @@ -60,8 +60,7 @@ impl Rule for PreferStringReplaceAll { return; } - let Argument::Expression(pattern) = &call_expr.arguments[0] else { return }; - + let pattern = &call_expr.arguments[0]; match method_name_str { "replaceAll" => { if let Some(k) = get_pattern_replacement(pattern) { @@ -81,12 +80,12 @@ impl Rule for PreferStringReplaceAll { } } -fn is_reg_exp_with_global_flag<'a>(expr: &'a Expression<'a>) -> bool { - if let Expression::RegExpLiteral(reg_exp_literal) = expr { +fn is_reg_exp_with_global_flag<'a>(expr: &'a Argument<'a>) -> bool { + if let Argument::RegExpLiteral(reg_exp_literal) = expr { return reg_exp_literal.regex.flags.contains(RegExpFlags::G); } - if let Expression::NewExpression(new_expr) = expr { + if let Argument::NewExpression(new_expr) = expr { if !new_expr.callee.is_specific_id("RegExp") { return false; } @@ -99,8 +98,8 @@ fn is_reg_exp_with_global_flag<'a>(expr: &'a Expression<'a>) -> bool { false } -fn get_pattern_replacement<'a>(expr: &'a Expression<'a>) -> Option { - let Expression::RegExpLiteral(reg_exp_literal) = expr else { return None }; +fn get_pattern_replacement<'a>(expr: &'a Argument<'a>) -> Option { + let Argument::RegExpLiteral(reg_exp_literal) = expr else { return None }; if !reg_exp_literal.regex.flags.contains(RegExpFlags::G) { return None; 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 dbb110ecd8550..633e453890d97 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{CallExpression, Expression, MemberExpression}, + ast::{match_member_expression, CallExpression, Expression, MemberExpression}, AstKind, }; use oxc_diagnostics::{ @@ -78,7 +78,9 @@ impl Rule for PreferTypeError { fn is_type_checking_expr(expr: &Expression) -> bool { match expr { - Expression::MemberExpression(member_expr) => is_type_checking_member_expr(member_expr), + match_member_expression!(Expression) => { + is_type_checking_member_expr(expr.to_member_expression()) + } Expression::CallExpression(call_expr) => is_typechecking_call_expr(call_expr), Expression::UnaryExpression(unary_expr) => { if unary_expr.operator == UnaryOperator::Typeof { @@ -112,8 +114,8 @@ fn is_typechecking_call_expr(call_expr: &CallExpression) -> bool { Expression::Identifier(ident) => { TYPE_CHECKING_GLOBAL_IDENTIFIERS.contains(ident.name.as_str()) } - Expression::MemberExpression(member_expr) => { - if let Some(ident) = member_expr.static_property_name() { + callee @ match_member_expression!(Expression) => { + if let Some(ident) = callee.to_member_expression().static_property_name() { return TYPE_CHECKING_IDENTIFIERS.contains(ident); } false diff --git a/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs b/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs index aa22b58af6623..63340576778d7 100644 --- a/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs +++ b/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - ast::{Expression, MemberExpression}, - AstKind, -}; +use oxc_ast::{ast::MemberExpression, AstKind}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -70,7 +67,7 @@ impl Rule for RequireArrayJoinSeparator { } // `[].join.call(foo)` and `Array.prototype.join.call(foo)` - if let Expression::MemberExpression(member_expr_obj) = member_expr.object() { + if let Some(member_expr_obj) = member_expr.object().as_member_expression() { if is_method_call(call_expr, None, Some(&["call"]), Some(1), Some(1)) && !member_expr.optional() && !call_expr.optional diff --git a/crates/oxc_linter/src/rules/unicorn/throw_new_error.rs b/crates/oxc_linter/src/rules/unicorn/throw_new_error.rs index 4c08f38999f86..8a6c80aaa8fa2 100644 --- a/crates/oxc_linter/src/rules/unicorn/throw_new_error.rs +++ b/crates/oxc_linter/src/rules/unicorn/throw_new_error.rs @@ -1,5 +1,8 @@ use lazy_static::lazy_static; -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::{ + ast::{match_member_expression, Expression}, + AstKind, +}; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, @@ -60,17 +63,18 @@ impl Rule for ThrowNewError { return; }; - match &call_expr.callee.without_parenthesized() { + match call_expr.callee.without_parenthesized() { Expression::Identifier(v) => { if !CUSTOM_ERROR_REGEX_PATTERN.is_match(&v.name) { return; } } - Expression::MemberExpression(v) => { - if v.is_computed() { + callee @ match_member_expression!(Expression) => { + let member_expr = callee.to_member_expression(); + if member_expr.is_computed() { return; } - if let Some(v) = v.static_property_name() { + if let Some(v) = member_expr.static_property_name() { if !CUSTOM_ERROR_REGEX_PATTERN.is_match(v) { return; } diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index ffed23df0bdcf..29f8f781b0121 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -2,7 +2,8 @@ use std::borrow::Cow; use oxc_ast::{ ast::{ - CallExpression, Expression, ImportDeclaration, ImportDeclarationSpecifier, TemplateLiteral, + match_member_expression, CallExpression, Expression, ImportDeclaration, + ImportDeclarationSpecifier, TemplateLiteral, }, AstKind, }; @@ -271,7 +272,8 @@ pub fn get_node_name_vec<'a>(expr: &'a Expression<'a>) -> Vec> { chain.extend(get_node_name_vec(&tagged_expr.tag)); } Expression::CallExpression(call_expr) => chain.extend(get_node_name_vec(&call_expr.callee)), - Expression::MemberExpression(member_expr) => { + match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); chain.extend(get_node_name_vec(member_expr.object())); if let Some(name) = member_expr.static_property_name() { chain.push(Cow::Borrowed(name)); 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 bc12a134fd7ce..1161224a6feff 100644 --- a/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs +++ b/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs @@ -2,7 +2,8 @@ use std::{borrow::Cow, cmp::Ordering}; use oxc_ast::{ ast::{ - Argument, CallExpression, Expression, IdentifierName, IdentifierReference, MemberExpression, + match_member_expression, Argument, CallExpression, Expression, IdentifierName, + IdentifierReference, MemberExpression, }, AstKind, }; @@ -293,7 +294,9 @@ fn resolve_to_jest_fn<'a>( fn resolve_first_ident<'a>(expr: &'a Expression<'a>) -> Option<&'a IdentifierReference<'a>> { match expr { Expression::Identifier(ident) => Some(ident), - Expression::MemberExpression(member_expr) => resolve_first_ident(member_expr.object()), + match_member_expression!(Expression) => { + resolve_first_ident(expr.to_member_expression().object()) + } Expression::CallExpression(call_expr) => resolve_first_ident(&call_expr.callee), Expression::TaggedTemplateExpression(tagged_expr) => resolve_first_ident(&tagged_expr.tag), _ => None, @@ -443,7 +446,8 @@ fn get_node_chain<'a>(params: &NodeChainParams<'a>) -> Vec { + match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); let params = NodeChainParams { expr: member_expr.object(), parent: Some(expr), diff --git a/crates/oxc_linter/src/utils/react.rs b/crates/oxc_linter/src/utils/react.rs index d92b6c468ab00..a553bafbdb8fe 100644 --- a/crates/oxc_linter/src/utils/react.rs +++ b/crates/oxc_linter/src/utils/react.rs @@ -94,7 +94,7 @@ pub fn is_hidden_from_screen_reader(ctx: &LintContext, node: &JSXOpeningElement) None => true, Some(JSXAttributeValue::StringLiteral(s)) if s.value == "true" => true, Some(JSXAttributeValue::ExpressionContainer(container)) => { - if let JSXExpression::Expression(expr) = &container.expression { + if let Some(expr) = container.expression.as_expression() { expr.get_boolean_value().unwrap_or(false) } else { false @@ -110,11 +110,8 @@ pub fn object_has_accessible_child(ctx: &LintContext, node: &JSXElement<'_>) -> JSXChild::Text(text) => !text.value.is_empty(), JSXChild::Element(el) => !is_hidden_from_screen_reader(ctx, &el.opening_element), JSXChild::ExpressionContainer(container) => { - if let JSXExpression::Expression(expr) = &container.expression { - !expr.is_undefined() && !expr.is_null() - } else { - false - } + !matches!(&container.expression, JSXExpression::NullLiteral(_)) + && !container.expression.is_undefined() } _ => false, }) || has_jsx_prop_lowercase(&node.opening_element, "dangerouslySetInnerHTML").is_some() @@ -169,7 +166,7 @@ const CREATE_CLASS: &str = "createReactClass"; pub fn is_es5_component(node: &AstNode) -> bool { let AstKind::CallExpression(call_expr) = node.kind() else { return false }; - if let Expression::MemberExpression(member_expr) = &call_expr.callee { + if let Some(member_expr) = call_expr.callee.as_member_expression() { if let Expression::Identifier(ident) = member_expr.object() { return ident.name == PRAGMA && member_expr.static_property_name() == Some(CREATE_CLASS); @@ -189,7 +186,7 @@ const PURE_COMPONENT: &str = "PureComponent"; pub fn is_es6_component(node: &AstNode) -> bool { let AstKind::Class(class_expr) = node.kind() else { return false }; if let Some(super_class) = &class_expr.super_class { - if let Expression::MemberExpression(member_expr) = super_class { + if let Some(member_expr) = super_class.as_member_expression() { if let Expression::Identifier(ident) = member_expr.object() { return ident.name == PRAGMA && member_expr @@ -258,13 +255,11 @@ pub fn parse_jsx_value(value: &JSXAttributeValue) -> Result { match value { JSXAttributeValue::StringLiteral(str) => str.value.parse().or(Err(())), JSXAttributeValue::ExpressionContainer(container) => match &container.expression { - JSXExpression::Expression(Expression::StringLiteral(str)) => { - str.value.parse().or(Err(())) - } - JSXExpression::Expression(Expression::TemplateLiteral(tmpl)) => { + JSXExpression::StringLiteral(str) => str.value.parse().or(Err(())), + JSXExpression::TemplateLiteral(tmpl) => { tmpl.quasis.first().unwrap().value.raw.parse().or(Err(())) } - JSXExpression::Expression(Expression::NumericLiteral(num)) => Ok(num.value), + JSXExpression::NumericLiteral(num) => Ok(num.value), _ => Err(()), }, _ => Err(()), diff --git a/crates/oxc_linter/src/utils/unicorn.rs b/crates/oxc_linter/src/utils/unicorn.rs index 9837b677a0bbe..89e70ca883dc1 100644 --- a/crates/oxc_linter/src/utils/unicorn.rs +++ b/crates/oxc_linter/src/utils/unicorn.rs @@ -4,8 +4,8 @@ use crate::LintContext; pub use self::boolean::*; use oxc_ast::{ ast::{ - BindingPatternKind, ChainElement, Expression, FormalParameters, FunctionBody, - LogicalExpression, MemberExpression, Statement, + BindingPatternKind, Expression, FormalParameters, FunctionBody, LogicalExpression, + MemberExpression, Statement, }, AstKind, }; @@ -52,7 +52,7 @@ pub fn is_prototype_property( } // `Object.prototype.method` or `Array.prototype.method` - if let Expression::MemberExpression(member_expr_obj) = member_expr.object() { + if let Some(member_expr_obj) = member_expr.object().as_member_expression() { if let Expression::Identifier(iden) = member_expr_obj.object() { if member_expr_obj.static_property_name().is_some_and(|name| name == "prototype") && object.is_some_and(|val| val == iden.name) @@ -148,24 +148,23 @@ pub fn get_return_identifier_name<'a>(body: &'a FunctionBody<'_>) -> Option<&'a } pub fn is_same_reference(left: &Expression, right: &Expression, ctx: &LintContext) -> bool { - match (left, right) { - ( - Expression::ChainExpression(left_chain_expr), - Expression::MemberExpression(right_member_expr), - ) => { - if let ChainElement::MemberExpression(v) = &left_chain_expr.expression { + if let Expression::ChainExpression(left_chain_expr) = left { + if let Some(right_member_expr) = right.as_member_expression() { + if let Some(v) = left_chain_expr.expression.as_member_expression() { return is_same_member_expression(v, right_member_expr, ctx); } } - ( - Expression::MemberExpression(left_chain_expr), - Expression::ChainExpression(right_member_expr), - ) => { - if let ChainElement::MemberExpression(v) = &right_member_expr.expression { + } + + if let Some(left_chain_expr) = left.as_member_expression() { + if let Expression::ChainExpression(right_member_expr) = right { + if let Some(v) = right_member_expr.expression.as_member_expression() { return is_same_member_expression(left_chain_expr, v, ctx); } } + } + match (left, right) { // super // this (Expression::Super(_), Expression::Super(_)) | (Expression::ThisExpression(_), Expression::ThisExpression(_)) @@ -193,21 +192,22 @@ pub fn is_same_reference(left: &Expression, right: &Expression, ctx: &LintContex Expression::ChainExpression(left_chain_expr), Expression::ChainExpression(right_chain_expr), ) => { - if let ChainElement::MemberExpression(left_member_expr) = &left_chain_expr.expression { - if let ChainElement::MemberExpression(right_member_expr) = - &right_chain_expr.expression + if let Some(left_member_expr) = left_chain_expr.expression.as_member_expression() { + if let Some(right_member_expr) = right_chain_expr.expression.as_member_expression() { return is_same_member_expression(left_member_expr, right_member_expr, ctx); } } } - ( - Expression::MemberExpression(left_member_expr), - Expression::MemberExpression(right_member_expr), - ) => return is_same_member_expression(left_member_expr, right_member_expr, ctx), _ => {} } + if let (Some(left_member_expr), Some(right_member_expr)) = + (left.as_member_expression(), right.as_member_expression()) + { + return is_same_member_expression(left_member_expr, right_member_expr, ctx); + } + false } diff --git a/crates/oxc_minifier/src/compressor/ast_util.rs b/crates/oxc_minifier/src/compressor/ast_util.rs index ff72e1f9e6d48..d74ea5e1f30db 100644 --- a/crates/oxc_minifier/src/compressor/ast_util.rs +++ b/crates/oxc_minifier/src/compressor/ast_util.rs @@ -6,8 +6,8 @@ use oxc_semantic::ReferenceFlag; use oxc_syntax::operator::{AssignmentOperator, LogicalOperator, UnaryOperator}; use oxc_ast::ast::{ - ArrayExpressionElement, BinaryExpression, Expression, NumericLiteral, ObjectProperty, - ObjectPropertyKind, PropertyKey, SpreadElement, UnaryExpression, + match_expression, ArrayExpressionElement, BinaryExpression, Expression, NumericLiteral, + ObjectProperty, ObjectPropertyKind, PropertyKey, SpreadElement, UnaryExpression, }; /// Code ported from [closure-compiler](https://github.com/google/closure-compiler/blob/f3ce5ed8b630428e311fe9aa2e20d36560d975e2/src/com/google/javascript/jscomp/NodeUtil.java#LL836C6-L836C6) @@ -46,7 +46,7 @@ impl<'a, 'b> IsLiteralValue<'a, 'b> for ArrayExpressionElement<'a> { fn is_literal_value(&self, include_functions: bool) -> bool { match self { Self::SpreadElement(element) => element.is_literal_value(include_functions), - Self::Expression(expr) => expr.is_literal_value(include_functions), + match_expression!(Self) => self.to_expression().is_literal_value(include_functions), Self::Elision(_) => true, } } @@ -77,8 +77,8 @@ impl<'a, 'b> IsLiteralValue<'a, 'b> for ObjectProperty<'a> { impl<'a, 'b> IsLiteralValue<'a, 'b> for PropertyKey<'a> { fn is_literal_value(&self, include_functions: bool) -> bool { match self { - Self::Identifier(_) | Self::PrivateIdentifier(_) => false, - Self::Expression(expr) => expr.is_literal_value(include_functions), + Self::StaticIdentifier(_) | Self::PrivateIdentifier(_) => false, + match_expression!(Self) => self.to_expression().is_literal_value(include_functions), } } } @@ -185,7 +185,9 @@ impl<'a, 'b> CheckForStateChange<'a, 'b> for ArrayExpressionElement<'a> { fn check_for_state_change(&self, check_for_new_objects: bool) -> bool { match self { Self::SpreadElement(element) => element.check_for_state_change(check_for_new_objects), - Self::Expression(expr) => expr.check_for_state_change(check_for_new_objects), + match_expression!(Self) => { + self.to_expression().check_for_state_change(check_for_new_objects) + } Self::Elision(_) => false, } } @@ -221,8 +223,10 @@ impl<'a, 'b> CheckForStateChange<'a, 'b> for ObjectProperty<'a> { impl<'a, 'b> CheckForStateChange<'a, 'b> for PropertyKey<'a> { fn check_for_state_change(&self, check_for_new_objects: bool) -> bool { match self { - Self::Identifier(_) | Self::PrivateIdentifier(_) => false, - Self::Expression(expr) => expr.check_for_state_change(check_for_new_objects), + Self::StaticIdentifier(_) | Self::PrivateIdentifier(_) => false, + match_expression!(Self) => { + self.to_expression().check_for_state_change(check_for_new_objects) + } } } } diff --git a/crates/oxc_minifier/src/compressor/mod.rs b/crates/oxc_minifier/src/compressor/mod.rs index f5faeafe88a0d..07cc899d17470 100644 --- a/crates/oxc_minifier/src/compressor/mod.rs +++ b/crates/oxc_minifier/src/compressor/mod.rs @@ -63,7 +63,7 @@ impl<'a> Compressor<'a> { if let Statement::BlockStatement(block) = stmt { // Avoid compressing `if (x) { var x = 1 }` to `if (x) var x = 1` due to different // semantics according to AnnexB, which lead to different semantics. - if block.body.len() == 1 && !matches!(&block.body[0], Statement::Declaration(_)) { + if block.body.len() == 1 && !block.body[0].is_declaration() { *stmt = block.body.remove(0); self.compress_block(stmt); } @@ -103,8 +103,8 @@ impl<'a> Compressor<'a> { for window in stmts.windows(2) { let [prev, cur] = window else { unreachable!() }; if let ( - Statement::Declaration(Declaration::VariableDeclaration(cur_decl)), - Statement::Declaration(Declaration::VariableDeclaration(prev_decl)), + Statement::VariableDeclaration(cur_decl), + Statement::VariableDeclaration(prev_decl), ) = (cur, prev) { if cur_decl.kind == prev_decl.kind { @@ -130,12 +130,8 @@ impl<'a> Compressor<'a> { let mut new_stmts = self.ast.new_vec_with_capacity(stmts.len() - capacity); for (i, stmt) in stmts.drain(..).enumerate() { if i > 0 && ranges.iter().any(|range| range.contains(&(i - 1)) && range.contains(&i)) { - if let Statement::Declaration(Declaration::VariableDeclaration(prev_decl)) = - new_stmts.last_mut().unwrap() - { - if let Statement::Declaration(Declaration::VariableDeclaration(mut cur_decl)) = - stmt - { + if let Statement::VariableDeclaration(prev_decl) = new_stmts.last_mut().unwrap() { + if let Statement::VariableDeclaration(mut cur_decl) = stmt { prev_decl.declarations.append(&mut cur_decl.declarations); } } diff --git a/crates/oxc_minifier/src/compressor/util.rs b/crates/oxc_minifier/src/compressor/util.rs index 177d7f15f2bd9..602d98bf29298 100644 --- a/crates/oxc_minifier/src/compressor/util.rs +++ b/crates/oxc_minifier/src/compressor/util.rs @@ -3,7 +3,7 @@ use oxc_ast::ast::Expression; pub(super) fn is_console(expr: &Expression<'_>) -> bool { // let Statement::ExpressionStatement(expr) = stmt else { return false }; let Expression::CallExpression(call_expr) = &expr else { return false }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { return false }; + let Some(member_expr) = call_expr.callee.as_member_expression() else { return false }; let obj = member_expr.object(); let Some(ident) = obj.get_identifier_reference() else { return false }; ident.name == "console" diff --git a/crates/oxc_minifier/tests/terser/mod.rs b/crates/oxc_minifier/tests/terser/mod.rs index 73307c6e902d9..c3c08d73ef779 100644 --- a/crates/oxc_minifier/tests/terser/mod.rs +++ b/crates/oxc_minifier/tests/terser/mod.rs @@ -92,9 +92,8 @@ impl TestCase { // Parse options if let Statement::ExpressionStatement(expr_stmt) = stmt { if let Expression::AssignmentExpression(assign_expr) = &expr_stmt.expression { - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident), - ) = &assign_expr.left + if let AssignmentTarget::AssignmentTargetIdentifier(ident) = + &assign_expr.left { if ident.name == "options" { if let Expression::ObjectExpression(object_expr) = diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index 699007ac9bc62..83d367d2accd1 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -114,9 +114,7 @@ impl<'a> ModuleLexer<'a> { impl<'a> Visit<'a> for ModuleLexer<'a> { fn visit_statement(&mut self, stmt: &Statement<'a>) { - if self.facade - && !matches!(stmt, Statement::ModuleDeclaration(..) | Statement::Declaration(..)) - { + if self.facade && !stmt.is_module_declaration() && !stmt.is_declaration() { self.facade = false; } @@ -246,9 +244,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { let ln = match &decl.declaration { ExportDefaultDeclarationKind::FunctionDeclaration(func) => func.id.as_ref(), ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref(), - ExportDefaultDeclarationKind::Expression(_) - | ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) - | ExportDefaultDeclarationKind::TSEnumDeclaration(_) => None, + _ => None, }; self.exports.push(ExportSpecifier { n: decl.exported.name().clone(), diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs index 7f9fccb9a3a1c..f284409113d22 100644 --- a/crates/oxc_parser/src/js/binding.rs +++ b/crates/oxc_parser/src/js/binding.rs @@ -103,7 +103,7 @@ impl<'a> ParserImpl<'a> { // let { a = b } = c // let { a } = b // ^ BindingIdentifier - if let PropertyKey::Identifier(ident) = &key { + if let PropertyKey::StaticIdentifier(ident) = &key { shorthand = true; let binding_identifier = BindingIdentifier::new(ident.span, ident.name.clone()); let identifier = self.ast.binding_pattern_identifier(binding_identifier); diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs index ccf0ecc12c6c4..e2ffb5f60786d 100644 --- a/crates/oxc_parser/src/js/declaration.rs +++ b/crates/oxc_parser/src/js/declaration.rs @@ -51,7 +51,7 @@ impl<'a> ParserImpl<'a> { self.asi()?; - Ok(Statement::Declaration(Declaration::UsingDeclaration(self.ast.alloc(using_decl)))) + Ok(Statement::UsingDeclaration(self.ast.alloc(using_decl))) } pub(crate) fn parse_variable_declaration( diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index e925329ea735c..096960ee6d8a4 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -509,8 +509,9 @@ impl<'a> ParserImpl<'a> { fn map_to_chain_expression(&mut self, span: Span, expr: Expression<'a>) -> Expression<'a> { match expr { - Expression::MemberExpression(result) => { - self.ast.chain_expression(span, ChainElement::MemberExpression(result)) + match_member_expression!(Expression) => { + let member_expr = MemberExpression::try_from(expr).unwrap(); + self.ast.chain_expression(span, ChainElement::from(member_expr)) } Expression::CallExpression(result) => { self.ast.chain_expression(span, ChainElement::CallExpression(result)) diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs index 0c300725ee97b..0c91e964a4dbb 100644 --- a/crates/oxc_parser/src/js/grammar.rs +++ b/crates/oxc_parser/src/js/grammar.rs @@ -16,18 +16,14 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for AssignmentTarget<'a> { Expression::ArrayExpression(array_expr) => { ArrayAssignmentTarget::cover(array_expr.unbox(), p) .map(|pat| p.ast.alloc(pat)) - .map(AssignmentTargetPattern::ArrayAssignmentTarget) - .map(AssignmentTarget::AssignmentTargetPattern) + .map(AssignmentTarget::ArrayAssignmentTarget) } Expression::ObjectExpression(object_expr) => { ObjectAssignmentTarget::cover(object_expr.unbox(), p) .map(|pat| p.ast.alloc(pat)) - .map(AssignmentTargetPattern::ObjectAssignmentTarget) - .map(AssignmentTarget::AssignmentTargetPattern) - } - _ => { - SimpleAssignmentTarget::cover(expr, p).map(AssignmentTarget::SimpleAssignmentTarget) + .map(AssignmentTarget::ObjectAssignmentTarget) } + _ => SimpleAssignmentTarget::cover(expr, p).map(AssignmentTarget::from), } } } @@ -39,8 +35,9 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for SimpleAssignmentTarget<'a> { Expression::Identifier(ident) => { Ok(SimpleAssignmentTarget::AssignmentTargetIdentifier(ident)) } - Expression::MemberExpression(expr) => { - Ok(SimpleAssignmentTarget::MemberAssignmentTarget(expr)) + match_member_expression!(Expression) => { + let member_expr = MemberExpression::try_from(expr).unwrap(); + Ok(SimpleAssignmentTarget::from(member_expr)) } Expression::ParenthesizedExpression(expr) => { let span = expr.span; @@ -72,7 +69,8 @@ impl<'a> CoverGrammar<'a, ArrayExpression<'a>> for ArrayAssignmentTarget<'a> { let len = expr.elements.len(); for (i, elem) in expr.elements.into_iter().enumerate() { match elem { - ArrayExpressionElement::Expression(expr) => { + match_expression!(ArrayExpressionElement) => { + let expr = Expression::try_from(elem).unwrap(); let target = AssignmentTargetMaybeDefault::cover(expr, p)?; elements.push(Some(target)); } @@ -111,7 +109,7 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for AssignmentTargetMaybeDefault<'a> { } expr => { let target = AssignmentTarget::cover(expr, p)?; - Ok(AssignmentTargetMaybeDefault::AssignmentTarget(target)) + Ok(AssignmentTargetMaybeDefault::from(target)) } } } @@ -156,7 +154,7 @@ impl<'a> CoverGrammar<'a, ObjectProperty<'a>> for AssignmentTargetProperty<'a> { fn cover(property: ObjectProperty<'a>, p: &mut ParserImpl<'a>) -> Result { if property.shorthand { let binding = match property.key { - PropertyKey::Identifier(ident) => { + PropertyKey::StaticIdentifier(ident) => { let ident = ident.unbox(); IdentifierReference::new(ident.span, ident.name) } diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs index 133897fbddd9d..d3c75956ab488 100644 --- a/crates/oxc_parser/src/js/list.rs +++ b/crates/oxc_parser/src/js/list.rs @@ -119,7 +119,7 @@ impl<'a> SeparatedList<'a> for ArrayExpressionList<'a> { let element = match p.cur_kind() { Kind::Comma => Ok(p.parse_elision()), Kind::Dot3 => p.parse_spread_element().map(ArrayExpressionElement::SpreadElement), - _ => p.parse_assignment_expression_base().map(ArrayExpressionElement::Expression), + _ => p.parse_assignment_expression_base().map(ArrayExpressionElement::from), }; if p.at(Kind::Comma) && p.peek_at(self.close()) { @@ -201,7 +201,7 @@ impl<'a> SeparatedList<'a> for CallArguments<'a> { } result } else { - p.parse_assignment_expression_base().map(Argument::Expression) + p.parse_assignment_expression_base().map(Argument::from) }; self.elements.push(element?); Ok(()) diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 62164605a0e74..53447faaa3bfe 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -43,7 +43,7 @@ impl<'a> ParserImpl<'a> { && self.nth_at(2, Kind::Eq))) { let decl = self.parse_ts_import_equals_declaration(span)?; - return Ok(Statement::Declaration(decl)); + return Ok(Statement::from(decl)); } // `import type ...` @@ -349,7 +349,7 @@ impl<'a> ParserImpl<'a> { _ => { let decl = self .parse_assignment_expression_base() - .map(ExportDefaultDeclarationKind::Expression)?; + .map(ExportDefaultDeclarationKind::from)?; self.asi()?; decl } diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs index c0115254404d0..45fa5333b5080 100644 --- a/crates/oxc_parser/src/js/object.rs +++ b/crates/oxc_parser/src/js/object.rs @@ -107,9 +107,7 @@ impl<'a> ParserImpl<'a> { // CoverInitializedName ({ foo = bar }) let init = if self.eat(Kind::Eq) { let right = self.parse_assignment_expression_base()?; - let left = AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(self.ast.alloc(identifier)), - ); + let left = AssignmentTarget::AssignmentTargetIdentifier(self.ast.alloc(identifier)); Some(self.ast.assignment_expression( self.end_span(span), AssignmentOperator::Assign, @@ -122,7 +120,7 @@ impl<'a> ParserImpl<'a> { Ok(self.ast.object_property( self.end_span(span), PropertyKind::Init, - PropertyKey::Identifier(key), + PropertyKey::StaticIdentifier(key), value, init, /* method */ false, @@ -159,18 +157,16 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_property_name(&mut self) -> Result<(PropertyKey<'a>, bool)> { let mut computed = false; let key = match self.cur_kind() { - Kind::Str => self.parse_literal_expression().map(PropertyKey::Expression)?, - kind if kind.is_number() => { - self.parse_literal_expression().map(PropertyKey::Expression)? - } + Kind::Str => self.parse_literal_expression().map(PropertyKey::from)?, + kind if kind.is_number() => self.parse_literal_expression().map(PropertyKey::from)?, // { [foo]() {} } Kind::LBrack => { computed = true; - self.parse_computed_property_name().map(PropertyKey::Expression)? + self.parse_computed_property_name().map(PropertyKey::from)? } _ => { let ident = self.parse_identifier_name()?; - PropertyKey::Identifier(self.ast.alloc(ident)) + PropertyKey::StaticIdentifier(self.ast.alloc(ident)) } }; Ok((key, computed)) diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index ebc60100d8e8d..8a4eff9e5f588 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -191,7 +191,7 @@ impl<'a> ParserImpl<'a> { self.error(diagnostics::LexicalDeclarationSingleStatement(decl.span)); } - Ok(Statement::Declaration(Declaration::VariableDeclaration(decl))) + Ok(Statement::VariableDeclaration(decl)) } /// Section 14.4 Empty Statement @@ -289,7 +289,7 @@ impl<'a> ParserImpl<'a> { if self.at(Kind::In) || self.at(Kind::Of) { let target = AssignmentTarget::cover(init_expression, self) .map_err(|_| diagnostics::UnexpectedToken(self.end_span(expr_span)))?; - let for_stmt_left = ForStatementLeft::AssignmentTarget(target); + let for_stmt_left = ForStatementLeft::from(target); if !r#await && is_async_of { self.error(diagnostics::ForLoopAsyncOf(self.end_span(expr_span))); } @@ -299,7 +299,7 @@ impl<'a> ParserImpl<'a> { return self.parse_for_in_or_of_loop(span, r#await, for_stmt_left); } - self.parse_for_loop(span, Some(ForStatementInit::Expression(init_expression)), r#await) + self.parse_for_loop(span, Some(ForStatementInit::from(init_expression)), r#await) } fn parse_variable_declaration_for_statement( diff --git a/crates/oxc_parser/src/jsx/mod.rs b/crates/oxc_parser/src/jsx/mod.rs index f7b2ee65b799a..a8a4faa649a65 100644 --- a/crates/oxc_parser/src/jsx/mod.rs +++ b/crates/oxc_parser/src/jsx/mod.rs @@ -245,7 +245,7 @@ impl<'a> ParserImpl<'a> { let expr = self.ast.jsx_empty_expression(Span::new(span.start + 1, span.end - 1)); JSXExpression::EmptyExpression(expr) } else { - let expr = self.parse_jsx_assignment_expression().map(JSXExpression::Expression)?; + let expr = self.parse_jsx_assignment_expression().map(JSXExpression::from)?; if in_jsx_child { self.expect_jsx_child(Kind::RCurly) } else { diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs index 7ca86c2c0bf30..75560eba69799 100644 --- a/crates/oxc_parser/src/ts/list.rs +++ b/crates/oxc_parser/src/ts/list.rs @@ -74,9 +74,9 @@ impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { p.expect(Kind::Colon)?; let element_type = p.parse_ts_type()?; - self.elements.push(TSTupleElement::TSType(TSType::TSNamedTupleMember(p.ast.alloc( + self.elements.push(TSTupleElement::TSNamedTupleMember(p.ast.alloc( TSNamedTupleMember { span: p.end_span(span), element_type, label, optional }, - )))); + ))); return Ok(()); } @@ -95,7 +95,7 @@ impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { p.ast.alloc(TSOptionalType { span: p.end_span(span), type_annotation }), )); } else { - self.elements.push(TSTupleElement::TSType(type_annotation)); + self.elements.push(TSTupleElement::from(type_annotation)); } Ok(()) diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index f100c4eedb237..a4c732118a38d 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -273,7 +273,7 @@ impl<'a> ParserImpl<'a> { self.ctx = self.ctx.union_ambient_if(flags.declare()).and_await(flags.r#async()); let result = self.parse_declaration(start_span, modifiers); self.ctx = reserved_ctx; - result.map(Statement::Declaration) + result.map(Statement::from) } pub(crate) fn parse_declaration( diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index 98d55a5319ce9..882ea77527e0b 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -44,21 +44,15 @@ impl<'a, 'b> Array<'a, 'b> { return false; } - return array.elements.iter().all(|element| { - let ArrayExpressionElement::Expression(expr) = element else { - return false; - }; - - match expr { - Expression::NumericLiteral(_) => true, - Expression::UnaryExpression(unary_expr) => { - matches!( - unary_expr.operator, - UnaryOperator::UnaryPlus | UnaryOperator::UnaryNegation - ) && matches!(unary_expr.argument, Expression::NumericLiteral(_)) - } - _ => false, + return array.elements.iter().all(|element| match element { + ArrayExpressionElement::NumericLiteral(_) => true, + ArrayExpressionElement::UnaryExpression(unary_expr) => { + matches!( + unary_expr.operator, + UnaryOperator::UnaryPlus | UnaryOperator::UnaryNegation + ) && matches!(unary_expr.argument, Expression::NumericLiteral(_)) } + _ => false, }); } Self::ArrayPattern(_) | Self::ArrayAssignmentTarget(_) | Self::TSTupleType(_) => false, @@ -260,16 +254,16 @@ fn should_break(array: &Array) -> bool { match array { Array::ArrayExpression(array) => { array.elements.iter().enumerate().all(|(index, element)| { - let ArrayExpressionElement::Expression(element) = element else { - return false; - }; - if let Some(ArrayExpressionElement::Expression(next_element)) = - array.elements.get(index + 1) - { + if let Some(next_element) = array.elements.get(index + 1) { let all_array_or_object = matches!( (element, next_element), - (Expression::ArrayExpression(_), Expression::ArrayExpression(_)) - | (Expression::ObjectExpression(_), Expression::ObjectExpression(_)) + ( + ArrayExpressionElement::ArrayExpression(_), + ArrayExpressionElement::ArrayExpression(_) + ) | ( + ArrayExpressionElement::ObjectExpression(_), + ArrayExpressionElement::ObjectExpression(_) + ) ); if !all_array_or_object { return false; @@ -277,31 +271,26 @@ fn should_break(array: &Array) -> bool { } match element { - Expression::ArrayExpression(array) => array.elements.len() > 1, - Expression::ObjectExpression(object) => object.properties.len() > 1, + ArrayExpressionElement::ArrayExpression(array) => array.elements.len() > 1, + ArrayExpressionElement::ObjectExpression(object) => object.properties.len() > 1, _ => false, } }) } Array::TSTupleType(tuple) => { tuple.element_types.iter().enumerate().all(|(index, element)| { - let TSTupleElement::TSType(element) = element else { return false }; + let TSTupleElement::TSTupleType(array) = element else { + return false; + }; - if let Some(TSTupleElement::TSType(next_element)) = + if let Some(next_element @ match_ts_type!(TSTupleElement)) = tuple.element_types.get(index + 1) { - if !matches!( - (element, next_element), - (TSType::TSTupleType(_), TSType::TSTupleType(_)) - ) { + if !matches!(next_element, TSTupleElement::TSTupleType(_)) { return false; } } - let TSType::TSTupleType(array) = element else { - return false; - }; - array.element_types.len() > 1 }) } diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index b6b91b3ee8dcf..0daa24b49af84 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -1,8 +1,8 @@ use oxc_ast::{ ast::{ - AccessorProperty, Argument, AssignmentExpression, AssignmentTarget, - AssignmentTargetPattern, AssignmentTargetProperty, BindingPatternKind, Expression, - ObjectProperty, PropertyDefinition, PropertyKind, Statement, TSTypeParameterInstantiation, + match_member_expression, AccessorProperty, Argument, AssignmentExpression, + AssignmentTarget, AssignmentTargetProperty, BindingPatternKind, Expression, ObjectProperty, + PropertyDefinition, PropertyKind, Statement, TSTypeParameterInstantiation, VariableDeclarator, }, AstKind, @@ -231,9 +231,8 @@ fn is_assignment(expr: &Expression) -> bool { fn is_complex_destructuring(expr: &AssignmentLikeNode) -> bool { match expr { AssignmentLikeNode::AssignmentExpression(assignment_expr) => { - if let AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ObjectAssignmentTarget(obj_assignment_target), - ) = &assignment_expr.left + if let AssignmentTarget::ObjectAssignmentTarget(obj_assignment_target) = + &assignment_expr.left { if obj_assignment_target.properties.len() > 2 && obj_assignment_target.properties.iter().any(|property| { @@ -373,9 +372,9 @@ fn is_poorly_breakable_member_or_call_chain<'a>(p: &Prettier<'a>, expr: &Express call_expressions.push(call_expr); Some(callee) } - Expression::MemberExpression(member_expr) => { + match_member_expression!(Expression) => { is_chain_expression = true; - Some(member_expr.object()) + Some(node.to_member_expression().object()) } Expression::Identifier(_) | Expression::ThisExpression(_) => { is_ident_or_this_expr = true; diff --git a/crates/oxc_prettier/src/format/call_arguments.rs b/crates/oxc_prettier/src/format/call_arguments.rs index 90e71362770be..73c9721200fc8 100644 --- a/crates/oxc_prettier/src/format/call_arguments.rs +++ b/crates/oxc_prettier/src/format/call_arguments.rs @@ -185,38 +185,34 @@ fn should_expand_first_arg<'a>(arguments: &Vec<'a, Argument<'a>>) -> bool { return false; } - let Argument::Expression(first_arg) = &arguments[0] else { return false }; - let Argument::Expression(second_arg) = &arguments[1] else { return false }; + match &arguments[0] { + Argument::FunctionExpression(_) => {} + Argument::ArrowFunctionExpression(arrow) if !arrow.expression => {} + _ => return false, + } - let first_check = match first_arg { - Expression::FunctionExpression(_) => true, - Expression::ArrowFunctionExpression(arrow) => !arrow.expression, + match &arguments[1] { + Argument::FunctionExpression(_) + | Argument::ArrowFunctionExpression(_) + | Argument::ConditionalExpression(_) => false, + second_arg if second_arg.is_expression() => { + let second_arg = second_arg.to_expression(); + is_hopefully_short_call_argument(second_arg) && !could_expand_arg(second_arg, false) + } _ => false, - }; - - first_check - && !matches!( - second_arg, - Expression::FunctionExpression(_) - | Expression::ArrowFunctionExpression(_) - | Expression::ConditionalExpression(_) - ) - && is_hopefully_short_call_argument(second_arg) - && !could_expand_arg(second_arg, false) + } } fn should_expand_last_arg(args: &Vec<'_, Argument<'_>>) -> bool { - let Some(Argument::Expression(last_arg)) = args.last() else { return false }; + let Some(last_arg) = args.last() else { return false }; + let Some(last_arg) = last_arg.as_expression() else { return false }; let penultimate_arg = if args.len() >= 2 { Some(&args[args.len() - 2]) } else { None }; could_expand_arg(last_arg, false) && (penultimate_arg.is_none() || matches!(last_arg, arg)) && (args.len() != 2 - || !matches!( - penultimate_arg, - Some(Argument::Expression(Expression::ArrowFunctionExpression(_))) - ) + || !matches!(penultimate_arg, Some(Argument::ArrowFunctionExpression(_))) || !matches!(last_arg, Expression::ArrayExpression(_))) } @@ -273,9 +269,7 @@ fn is_simple_call_argument(node: &Expression, depth: usize) -> bool { } if let Expression::ArrayExpression(expr) = node { - return expr.elements.iter().all( - |x| matches!(x, ArrayExpressionElement::Expression(expr) if is_child_simple(expr)), - ); + return expr.elements.iter().all(|elem| elem.as_expression().is_some_and(is_child_simple)); } if node.is_call_expression() { @@ -285,7 +279,7 @@ fn is_simple_call_argument(node: &Expression, depth: usize) -> bool { if is_simple_call_argument(&expr.callee, depth) { return expr.arguments.len() <= depth && expr.arguments.iter().all(|arg| { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { is_child_simple(expr) } else { false @@ -296,7 +290,7 @@ fn is_simple_call_argument(node: &Expression, depth: usize) -> bool { if is_simple_call_argument(&expr.callee, depth) { return expr.arguments.len() <= depth && expr.arguments.iter().all(|arg| { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { is_child_simple(expr) } else { false @@ -321,7 +315,7 @@ fn is_simple_call_argument(node: &Expression, depth: usize) -> bool { false }; - if let Expression::MemberExpression(expr) = node { + if let Some(expr) = node.as_member_expression() { return check_member_expression(expr); } @@ -338,8 +332,8 @@ fn is_simple_call_argument(node: &Expression, depth: usize) -> bool { if let Expression::UpdateExpression(expr) = node { return match &expr.argument { SimpleAssignmentTarget::AssignmentTargetIdentifier(target) => true, - SimpleAssignmentTarget::MemberAssignmentTarget(target) => { - check_member_expression(target) + target @ match_member_expression!(SimpleAssignmentTarget) => { + check_member_expression(target.to_member_expression()) } _ => return false, }; diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index e10f7d78525a4..5e3b3fedbe273 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -78,24 +78,16 @@ pub fn is_commons_js_or_amd_call<'a>( ) -> bool { if let Expression::Identifier(callee) = callee { if callee.name == "require" { - return arguments.len() == 1 - && matches!(arguments[0], Argument::Expression(Expression::StringLiteral(_))) + return arguments.len() == 1 && matches!(arguments[0], Argument::StringLiteral(_)) || arguments.len() > 1; } if callee.name == "define" { // TODO: the parent node is ExpressionStatement return arguments.len() == 1 - || (arguments.len() == 2 - && matches!( - arguments[1], - Argument::Expression(Expression::ArrayExpression(_)) - )) + || (arguments.len() == 2 && matches!(arguments[1], Argument::ArrayExpression(_))) || (arguments.len() == 3 - && matches!(arguments[0], Argument::Expression(Expression::StringLiteral(_))) - && matches!( - arguments[1], - Argument::Expression(Expression::ArrayExpression(_)) - )); + && matches!(arguments[0], Argument::StringLiteral(_)) + && matches!(arguments[1], Argument::ArrayExpression(_))); } } false diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index bd1147598bb89..46021249ac403 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -116,14 +116,14 @@ impl<'a> Format<'a> for Statement<'a> { Self::ForStatement(stmt) => stmt.format(p), Self::IfStatement(stmt) => stmt.format(p), Self::LabeledStatement(stmt) => stmt.format(p), - Self::ModuleDeclaration(decl) => decl.format(p), Self::ReturnStatement(stmt) => stmt.format(p), Self::SwitchStatement(stmt) => stmt.format(p), Self::ThrowStatement(stmt) => stmt.format(p), Self::TryStatement(stmt) => stmt.format(p), Self::WhileStatement(stmt) => stmt.format(p), Self::WithStatement(stmt) => stmt.format(p), - Self::Declaration(decl) => decl.format(p), + match_module_declaration!(Self) => self.to_module_declaration().format(p), + match_declaration!(Self) => self.to_declaration().format(p), } } } @@ -234,7 +234,7 @@ impl<'a> Format<'a> for ForStatementInit<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { ForStatementInit::VariableDeclaration(v) => v.format(p), - ForStatementInit::Expression(v) => v.format(p), + match_expression!(ForStatementInit) => self.to_expression().format(p), ForStatementInit::UsingDeclaration(v) => v.format(p), } } @@ -280,7 +280,7 @@ impl<'a> Format<'a> for ForStatementLeft<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { ForStatementLeft::VariableDeclaration(v) => v.format(p), - ForStatementLeft::AssignmentTarget(v) => v.format(p), + match_assignment_target!(ForStatementLeft) => self.to_assignment_target().format(p), ForStatementLeft::UsingDeclaration(v) => v.format(p), } } @@ -988,7 +988,8 @@ impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { impl<'a> Format<'a> for TSModuleReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - TSModuleReference::TypeName(v) => v.format(p), + TSModuleReference::IdentifierReference(it) => format!(p, it), + TSModuleReference::QualifiedName(it) => format!(p, it), TSModuleReference::ExternalModuleReference(v) => v.format(p), } } @@ -1228,7 +1229,7 @@ impl<'a> Format<'a> for ExportDefaultDeclaration<'a> { impl<'a> Format<'a> for ExportDefaultDeclarationKind<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - Self::Expression(expr) => expr.format(p), + match_expression!(Self) => self.to_expression().format(p), Self::FunctionDeclaration(decl) => decl.format(p), Self::ClassDeclaration(decl) => decl.format(p), Self::TSInterfaceDeclaration(decl) => decl.format(p), @@ -1248,7 +1249,7 @@ impl<'a> Format<'a> for Expression<'a> { Self::StringLiteral(lit) => lit.format(p), Self::Identifier(ident) => ident.format(p), Self::ThisExpression(expr) => expr.format(p), - Self::MemberExpression(expr) => expr.format(p), + match_member_expression!(Self) => self.to_member_expression().format(p), Self::CallExpression(expr) => expr.format(p), Self::ArrayExpression(expr) => expr.format(p), Self::ObjectExpression(expr) => expr.format(p), @@ -1495,7 +1496,7 @@ impl<'a> Format<'a> for CallExpression<'a> { impl<'a> Format<'a> for Argument<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - Self::Expression(expr) => expr.format(p), + match_expression!(Self) => self.to_expression().format(p), Self::SpreadElement(expr) => expr.format(p), } } @@ -1505,7 +1506,7 @@ impl<'a> Format<'a> for ArrayExpressionElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { Self::SpreadElement(expr) => expr.format(p), - Self::Expression(expr) => expr.format(p), + match_expression!(Self) => self.to_expression().format(p), Self::Elision(elision) => Doc::Str(""), } } @@ -1606,10 +1607,10 @@ impl<'a> Format<'a> for PropertyKey<'a> { if is_parent_computed { let mut parts = p.vec(); parts.push(ss!("[")); - let doc = match &self { - PropertyKey::Identifier(ident) => ident.format(p), + let doc = match self { + PropertyKey::StaticIdentifier(ident) => ident.format(p), PropertyKey::PrivateIdentifier(ident) => ident.format(p), - PropertyKey::Expression(expr) => expr.format(p), + match_expression!(PropertyKey) => self.to_expression().format(p), }; parts.push(doc); parts.push(ss!("]")); @@ -1636,7 +1637,7 @@ impl<'a> Format<'a> for PropertyKey<'a> { }; match self { - PropertyKey::Identifier(ident) => { + PropertyKey::StaticIdentifier(ident) => { if need_quote { Doc::Str(string::print_string(p, &ident.name, p.options.single_quote)) } else { @@ -1644,36 +1645,34 @@ impl<'a> Format<'a> for PropertyKey<'a> { } } PropertyKey::PrivateIdentifier(ident) => ident.format(p), - PropertyKey::Expression(expr) => match expr { - Expression::StringLiteral(literal) => { - // This does not pass quotes/objects.js - // because prettier uses the function `isEs5IdentifierName` based on unicode version 3, - // but `is_identifier_name` uses the latest unicode version. - if is_identifier_name(literal.value.as_str()) - && (p.options.quote_props.as_needed() - || (p.options.quote_props.consistent()/* && !needsQuoteProps.get(parent) */)) - { - string!(p, literal.value.as_str()) - } else { - Doc::Str(string::print_string( - p, - literal.value.as_str(), - p.options.single_quote, - )) - } - } - Expression::NumericLiteral(literal) => { - if need_quote { - Doc::Str(string::print_string(p, literal.raw, p.options.single_quote)) - } else { - literal.format(p) - } + PropertyKey::StringLiteral(literal) => { + // This does not pass quotes/objects.js + // because prettier uses the function `isEs5IdentifierName` based on unicode version 3, + // but `is_identifier_name` uses the latest unicode version. + if is_identifier_name(literal.value.as_str()) + && (p.options.quote_props.as_needed() + || (p.options.quote_props.consistent()/* && !needsQuoteProps.get(parent) */)) + { + string!(p, literal.value.as_str()) + } else { + Doc::Str(string::print_string( + p, + literal.value.as_str(), + p.options.single_quote, + )) } - Expression::Identifier(ident) => { - array!(p, ss!("["), ident.format(p), ss!("]")) + } + PropertyKey::NumericLiteral(literal) => { + if need_quote { + Doc::Str(string::print_string(p, literal.raw, p.options.single_quote)) + } else { + literal.format(p) } - _ => expr.format(p), - }, + } + PropertyKey::Identifier(ident) => { + array!(p, ss!("["), ident.format(p), ss!("]")) + } + match_expression!(PropertyKey) => self.to_expression().format(p), } }) } @@ -1795,8 +1794,8 @@ impl<'a> Format<'a> for AssignmentExpression<'a> { impl<'a> Format<'a> for AssignmentTarget<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - Self::SimpleAssignmentTarget(target) => target.format(p), - Self::AssignmentTargetPattern(pat) => pat.format(p), + match_simple_assignment_target!(Self) => self.to_simple_assignment_target().format(p), + match_assignment_target_pattern!(Self) => self.to_assignment_target_pattern().format(p), } } } @@ -1805,7 +1804,7 @@ impl<'a> Format<'a> for SimpleAssignmentTarget<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { Self::AssignmentTargetIdentifier(ident) => ident.format(p), - Self::MemberAssignmentTarget(member_expr) => member_expr.format(p), + match_member_expression!(Self) => self.to_member_expression().format(p), Self::TSAsExpression(expr) => expr.expression.format(p), Self::TSSatisfiesExpression(expr) => expr.expression.format(p), Self::TSNonNullExpression(expr) => expr.expression.format(p), @@ -1832,7 +1831,9 @@ impl<'a> Format<'a> for ArrayAssignmentTarget<'a> { impl<'a> Format<'a> for AssignmentTargetMaybeDefault<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - AssignmentTargetMaybeDefault::AssignmentTarget(v) => v.format(p), + match_assignment_target!(AssignmentTargetMaybeDefault) => { + self.to_assignment_target().format(p) + } AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(v) => v.format(p), } } @@ -1988,7 +1989,7 @@ impl<'a> Format<'a> for ChainElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { Self::CallExpression(expr) => expr.format(p), - Self::MemberExpression(expr) => expr.format(p), + match_member_expression!(Self) => self.to_member_expression().format(p), } } } diff --git a/crates/oxc_prettier/src/format/module.rs b/crates/oxc_prettier/src/format/module.rs index 9e34fabf2d389..428f6a9c02ff3 100644 --- a/crates/oxc_prettier/src/format/module.rs +++ b/crates/oxc_prettier/src/format/module.rs @@ -57,7 +57,7 @@ fn print_semicolon_after_export_declaration<'a>( match decl { ModuleDeclaration::ExportDefaultDeclaration(decl) => match decl.declaration { - ExportDefaultDeclarationKind::Expression(_) => Some(ss!(";")), + match_expression!(ExportDefaultDeclarationKind) => Some(ss!(";")), ExportDefaultDeclarationKind::FunctionDeclaration(_) | ExportDefaultDeclarationKind::ClassDeclaration(_) | ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) diff --git a/crates/oxc_prettier/src/format/property.rs b/crates/oxc_prettier/src/format/property.rs index 9fa6b8b859a6d..993cbdd861d89 100644 --- a/crates/oxc_prettier/src/format/property.rs +++ b/crates/oxc_prettier/src/format/property.rs @@ -1,8 +1,8 @@ -use oxc_ast::ast::{Expression, PropertyKey}; +use oxc_ast::ast::PropertyKey; use oxc_syntax::identifier::is_identifier_name; pub(super) fn is_property_key_has_quote(key: &PropertyKey<'_>) -> bool { - matches!(key, PropertyKey::Expression(Expression::StringLiteral(literal)) if is_string_prop_safe_to_unquote(literal.value.as_str())) + matches!(key, PropertyKey::StringLiteral(literal) if is_string_prop_safe_to_unquote(literal.value.as_str())) } pub(super) fn is_string_prop_safe_to_unquote(value: &str) -> bool { diff --git a/crates/oxc_prettier/src/needs_parens.rs b/crates/oxc_prettier/src/needs_parens.rs index 0e7e4618eabd4..1e5e002eabb6e 100644 --- a/crates/oxc_prettier/src/needs_parens.rs +++ b/crates/oxc_prettier/src/needs_parens.rs @@ -10,9 +10,9 @@ )] use oxc_ast::{ ast::{ - AssignmentTarget, AssignmentTargetPattern, ChainElement, ExportDefaultDeclarationKind, - Expression, ForStatementLeft, MemberExpression, ModuleDeclaration, ObjectExpression, - SimpleAssignmentTarget, + match_member_expression, AssignmentTarget, ChainElement, ExportDefaultDeclarationKind, + Expression, ForStatementInit, ForStatementLeft, MemberExpression, ModuleDeclaration, + ObjectExpression, SimpleAssignmentTarget, }, AstKind, }; @@ -94,12 +94,9 @@ impl<'a> Prettier<'a> { { false } - AstKind::ExpressionStatement(_) => matches!( - assign_expr.left, - AssignmentTarget::AssignmentTargetPattern( - AssignmentTargetPattern::ObjectAssignmentTarget(_) - ) - ), + AstKind::ExpressionStatement(_) => { + matches!(assign_expr.left, AssignmentTarget::ObjectAssignmentTarget(_)) + } _ => true, }, AstKind::UpdateExpression(update_expr) => match parent_kind { @@ -222,10 +219,11 @@ impl<'a> Prettier<'a> { } } AstKind::ModuleDeclaration(ModuleDeclaration::ExportDefaultDeclaration(decl)) => { - if let ExportDefaultDeclarationKind::Expression(e) = &decl.declaration { - return matches!(e, Expression::SequenceExpression(_)) - || self.should_wrap_function_for_export_default(); - } + return matches!( + decl.declaration, + ExportDefaultDeclarationKind::SequenceExpression(_) + ) || (decl.declaration.is_expression() + && self.should_wrap_function_for_export_default()); } AstKind::BinaryExpression(binary_expr) => { if binary_expr.operator.is_relational() { @@ -277,10 +275,7 @@ impl<'a> Prettier<'a> { fn check_for_of_stmt_head_starts_with_async_or_let(&self, kind: AstKind<'a>) -> bool { let AstKind::IdentifierReference(ident) = kind else { return false }; let AstKind::ForOfStatement(stmt) = self.parent_kind() else { return false }; - if let ForStatementLeft::AssignmentTarget(AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(i), - )) = &stmt.left - { + if let ForStatementLeft::AssignmentTargetIdentifier(i) = &stmt.left { if (i.span == ident.span) && (i.name == "let" || (i.name == "async" && !stmt.r#await)) { return true; } @@ -297,13 +292,10 @@ impl<'a> Prettier<'a> { } for kind in self.stack.iter().rev() { if let AstKind::ForOfStatement(stmt) = kind { - if let ForStatementLeft::AssignmentTarget( - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(e), - ), - ) = &stmt.left - { - return Self::starts_with_no_lookahead_token(e.object(), ident.span); + if let Some(target) = stmt.left.as_assignment_target() { + if let Some(e) = target.as_member_expression() { + return Self::starts_with_no_lookahead_token(e.object(), ident.span); + } } break; } @@ -342,16 +334,13 @@ impl<'a> Prettier<'a> { AstKind::ForStatement(stmt) => stmt .init .as_ref() - .and_then(|init| init.expression()) + .and_then(ForStatementInit::as_expression) .map_or(false, |e| Self::starts_with_no_lookahead_token(e, ident.span)), AstKind::ForInStatement(stmt) => { - if let ForStatementLeft::AssignmentTarget( - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(e), - ), - ) = &stmt.left - { - return Self::starts_with_no_lookahead_token(e.object(), ident.span); + if let Some(target) = stmt.left.as_assignment_target() { + if let Some(e) = target.as_member_expression() { + return Self::starts_with_no_lookahead_token(e.object(), ident.span); + } } false } @@ -488,17 +477,13 @@ impl<'a> Prettier<'a> { AstKind::NewExpression(new_expr) if new_expr.callee.span() == span => { let mut object = &new_expr.callee; loop { - match object { + object = match object { Expression::CallExpression(_) => return true, - Expression::MemberExpression(e) => { - object = e.object(); - } - Expression::TaggedTemplateExpression(e) => { - object = &e.tag; - } - Expression::TSNonNullExpression(e) => { - object = &e.expression; - } + Expression::ComputedMemberExpression(e) => &e.object, + Expression::StaticMemberExpression(e) => &e.object, + Expression::PrivateFieldExpression(e) => &e.object, + Expression::TaggedTemplateExpression(e) => &e.tag, + Expression::TSNonNullExpression(e) => &e.expression, _ => return false, } } @@ -583,28 +568,33 @@ impl<'a> Prettier<'a> { Expression::BinaryExpression(e) => Self::starts_with_no_lookahead_token(&e.left, span), Expression::LogicalExpression(e) => Self::starts_with_no_lookahead_token(&e.left, span), Expression::AssignmentExpression(e) => match &e.left { - AssignmentTarget::SimpleAssignmentTarget(t) => match t { - SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => false, - SimpleAssignmentTarget::MemberAssignmentTarget(e) => { - Self::starts_with_no_lookahead_token(e.object(), span) - } - SimpleAssignmentTarget::TSAsExpression(e) => { - Self::starts_with_no_lookahead_token(&e.expression, span) - } - SimpleAssignmentTarget::TSSatisfiesExpression(e) => { - Self::starts_with_no_lookahead_token(&e.expression, span) - } - SimpleAssignmentTarget::TSNonNullExpression(e) => { - Self::starts_with_no_lookahead_token(&e.expression, span) - } - SimpleAssignmentTarget::TSTypeAssertion(e) => { - Self::starts_with_no_lookahead_token(&e.expression, span) - } - }, - AssignmentTarget::AssignmentTargetPattern(_) => false, + AssignmentTarget::AssignmentTargetIdentifier(_) => false, + AssignmentTarget::ComputedMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + AssignmentTarget::StaticMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + AssignmentTarget::PrivateFieldExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + AssignmentTarget::TSAsExpression(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } + AssignmentTarget::TSSatisfiesExpression(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } + AssignmentTarget::TSNonNullExpression(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } + AssignmentTarget::TSTypeAssertion(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } + AssignmentTarget::ArrayAssignmentTarget(_) + | AssignmentTarget::ObjectAssignmentTarget(_) => false, }, - Expression::MemberExpression(e) => { - Self::starts_with_no_lookahead_token(e.object(), span) + match_member_expression!(Expression) => { + Self::starts_with_no_lookahead_token(e.to_member_expression().object(), span) } Expression::TaggedTemplateExpression(e) => { if matches!(e.tag, Expression::FunctionExpression(_)) { @@ -625,8 +615,14 @@ impl<'a> Prettier<'a> { !e.prefix && match &e.argument { SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => false, - SimpleAssignmentTarget::MemberAssignmentTarget(e) => { - Self::starts_with_no_lookahead_token(e.object(), span) + SimpleAssignmentTarget::ComputedMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + SimpleAssignmentTarget::StaticMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + SimpleAssignmentTarget::PrivateFieldExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) } SimpleAssignmentTarget::TSAsExpression(e) => { Self::starts_with_no_lookahead_token(&e.expression, span) @@ -650,8 +646,14 @@ impl<'a> Prettier<'a> { ChainElement::CallExpression(e) => { Self::starts_with_no_lookahead_token(&e.callee, span) } - ChainElement::MemberExpression(e) => { - Self::starts_with_no_lookahead_token(e.object(), span) + ChainElement::ComputedMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + ChainElement::StaticMemberExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) + } + ChainElement::PrivateFieldExpression(e) => { + Self::starts_with_no_lookahead_token(&e.object, span) } }, Expression::TSSatisfiesExpression(e) => { diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 463f00213d539..f6a0494fcece1 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -335,14 +335,14 @@ impl<'a> Binder for TSEnumDeclaration<'a> { impl<'a> Binder for TSEnumMember<'a> { fn bind(&self, builder: &mut SemanticBuilder) { // TODO: Perf - if matches!(&self.id, TSEnumMemberName::ComputedPropertyName(_)) { + if self.id.is_expression() { return; } let name = match &self.id { - TSEnumMemberName::Identifier(id) => Cow::Borrowed(id.name.as_str()), - TSEnumMemberName::StringLiteral(s) => Cow::Borrowed(s.value.as_str()), - TSEnumMemberName::NumericLiteral(n) => Cow::Owned(n.value.to_string()), - TSEnumMemberName::ComputedPropertyName(_) => panic!("TODO: implement"), + TSEnumMemberName::StaticIdentifier(id) => Cow::Borrowed(id.name.as_str()), + TSEnumMemberName::StaticStringLiteral(s) => Cow::Borrowed(s.value.as_str()), + TSEnumMemberName::StaticNumericLiteral(n) => Cow::Owned(n.value.to_string()), + match_expression!(TSEnumMemberName) => panic!("TODO: implement"), }; builder.declare_symbol( self.span, diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 50a2e1841b9fc..00bbe64fe99d8 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -836,7 +836,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { ForStatementInit::VariableDeclaration(decl) => { self.visit_variable_declaration(decl); } - ForStatementInit::Expression(expr) => self.visit_expression(expr), + match_expression!(ForStatementInit) => self.visit_expression(init.to_expression()), } self.leave_node(kind); } diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index b14b1a9c6545c..0ccb7554cb0c2 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -579,7 +579,7 @@ fn check_function_declaration<'a>( struct FunctionDeclarationNonStrict(#[label] Span); // Function declaration not allowed in statement position - if let Statement::Declaration(Declaration::FunctionDeclaration(decl)) = stmt { + if let Statement::FunctionDeclaration(decl) = stmt { if ctx.strict_mode() { ctx.error(FunctionDeclarationStrict(decl.span)); } else if !is_if_stmt_or_labeled_stmt { @@ -1033,7 +1033,9 @@ fn check_assignment_expression(assign_expr: &AssignmentExpression, ctx: &Semanti // LeftHandSideExpression ||= AssignmentExpression // LeftHandSideExpression ??= AssignmentExpression // It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is not SIMPLE. - if assign_expr.operator != AssignmentOperator::Assign && !assign_expr.left.is_simple() { + if assign_expr.operator != AssignmentOperator::Assign + && !assign_expr.left.is_simple_assignment_target() + { ctx.error(AssignmentIsNotSimple(assign_expr.left.span())); } } @@ -1137,10 +1139,8 @@ fn check_unary_expression<'a>( Expression::Identifier(ident) if ctx.strict_mode() => { ctx.error(DeleteOfUnqualified(ident.span)); } - Expression::MemberExpression(expr) => { - if let MemberExpression::PrivateFieldExpression(expr) = &**expr { - ctx.error(DeletePrivateField(expr.span)); - } + Expression::PrivateFieldExpression(expr) => { + ctx.error(DeletePrivateField(expr.span)); } _ => {} } diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index b0e6902315d83..d694c15b20472 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -105,8 +105,10 @@ fn check_simple_assignment_target<'a>( ctx: &SemanticBuilder<'a>, ) { if let Some(expression) = target.get_expression() { + #[allow(clippy::match_same_arms)] match expression.get_inner_expression() { - Expression::Identifier(_) | Expression::MemberExpression(_) => {} + Expression::Identifier(_) => {} + match_member_expression!(Expression) => {} _ => { #[derive(Debug, Error, Diagnostic)] #[error( @@ -169,6 +171,7 @@ fn check_ts_enum_declaration(decl: &TSEnumDeclaration<'_>, ctx: &SemanticBuilder let mut need_initializer = false; decl.members.iter().for_each(|member| { + #[allow(clippy::unnested_or_patterns)] if let Some(initializer) = &member.initializer { need_initializer = !matches!( initializer, @@ -177,7 +180,7 @@ fn check_ts_enum_declaration(decl: &TSEnumDeclaration<'_>, ctx: &SemanticBuilder // B = A | Expression::Identifier(_) // C = E.D - | Expression::MemberExpression(_) + | match_member_expression!(Expression) // D = 1 + 2 | Expression::BinaryExpression(_) // E = -1 diff --git a/crates/oxc_semantic/src/module_record/builder.rs b/crates/oxc_semantic/src/module_record/builder.rs index 6c6c93ea89457..039609cf9e1a8 100644 --- a/crates/oxc_semantic/src/module_record/builder.rs +++ b/crates/oxc_semantic/src/module_record/builder.rs @@ -22,7 +22,7 @@ impl ModuleRecordBuilder { // This avoids additional checks on TypeScript `TsModuleBlock` which // also has `ModuleDeclaration`s. for stmt in &program.body { - if let Statement::ModuleDeclaration(module_decl) = stmt { + if let Some(module_decl) = stmt.as_module_declaration() { self.module_record.not_esm = false; self.visit_module_declaration(module_decl); } @@ -246,7 +246,7 @@ impl ModuleRecordBuilder { self.add_default_export(exported_name.span()); let id = match &decl.declaration { - ExportDefaultDeclarationKind::Expression(_) => None, + match_expression!(ExportDefaultDeclarationKind) => None, ExportDefaultDeclarationKind::FunctionDeclaration(func) => func.id.as_ref(), ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref(), ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) diff --git a/crates/oxc_transformer/src/helpers/module_imports.rs b/crates/oxc_transformer/src/helpers/module_imports.rs index 199c66f1ace66..4938533b00656 100644 --- a/crates/oxc_transformer/src/helpers/module_imports.rs +++ b/crates/oxc_transformer/src/helpers/module_imports.rs @@ -116,7 +116,7 @@ impl<'a> ModuleImports<'a> { }; let args = { let string = self.ast.string_literal(SPAN, source.as_str()); - let arg = Argument::Expression(self.ast.literal_string_expression(string)); + let arg = Argument::from(self.ast.literal_string_expression(string)); self.ast.new_vec_single(arg) }; let name = names.into_iter().next().unwrap(); @@ -130,6 +130,6 @@ impl<'a> ModuleImports<'a> { self.ast.new_vec_single(decl) }; let var_decl = self.ast.variable_declaration(SPAN, var_kind, decl, Modifiers::empty()); - Statement::Declaration(Declaration::VariableDeclaration(var_decl)) + Statement::VariableDeclaration(var_decl) } } diff --git a/crates/oxc_transformer/src/react/display_name/mod.rs b/crates/oxc_transformer/src/react/display_name/mod.rs index 57b9ba0c63e39..73cc27d22d111 100644 --- a/crates/oxc_transformer/src/react/display_name/mod.rs +++ b/crates/oxc_transformer/src/react/display_name/mod.rs @@ -36,19 +36,18 @@ impl<'a> ReactDisplayName<'a> { return; }; let name = match &assign_expr.left { - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident), - ) => ident.name.clone(), - AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::MemberAssignmentTarget(target), - ) => { - if let Some(name) = target.static_property_name() { - self.ctx.ast.new_atom(name) + AssignmentTarget::AssignmentTargetIdentifier(ident) => ident.name.clone(), + target => { + if let Some(target) = target.as_member_expression() { + if let Some(name) = target.static_property_name() { + self.ctx.ast.new_atom(name) + } else { + return; + } } else { return; } } - _ => return, }; self.add_display_name(obj_expr, name); } @@ -77,7 +76,7 @@ impl<'a> ReactDisplayName<'a> { /// `export default React.createClass({})` /// Uses the current file name as the display name. pub fn transform_export_default_declaration(&self, decl: &mut ExportDefaultDeclaration<'a>) { - let ExportDefaultDeclarationKind::Expression(expr) = &mut decl.declaration else { return }; + let Some(expr) = decl.declaration.as_expression_mut() else { return }; let Some(obj_expr) = Self::get_object_from_create_class(expr) else { return }; let name = self.ctx.ast.new_atom(self.ctx.filename()); self.add_display_name(obj_expr, name); @@ -91,7 +90,9 @@ impl<'a> ReactDisplayName<'a> { ) -> Option<&'b mut Box<'a, ObjectExpression<'a>>> { let Expression::CallExpression(call_expr) = e else { return None }; if match &call_expr.callee { - Expression::MemberExpression(e) => !e.is_specific_member_access("React", "createClass"), + callee @ match_member_expression!(Expression) => { + !callee.to_member_expression().is_specific_member_access("React", "createClass") + } Expression::Identifier(ident) => ident.name != "createReactClass", _ => true, } { @@ -103,11 +104,8 @@ impl<'a> ReactDisplayName<'a> { } let arg = call_expr.arguments.get_mut(0)?; match arg { - Argument::SpreadElement(_) => None, - Argument::Expression(e) => match e { - Expression::ObjectExpression(obj_expr) => Some(obj_expr), - _ => None, - }, + Argument::ObjectExpression(obj_expr) => Some(obj_expr), + _ => None, } } diff --git a/crates/oxc_transformer/src/react/jsx/mod.rs b/crates/oxc_transformer/src/react/jsx/mod.rs index 6f3dfac9bb430..03b0eb3e570ad 100644 --- a/crates/oxc_transformer/src/react/jsx/mod.rs +++ b/crates/oxc_transformer/src/react/jsx/mod.rs @@ -120,7 +120,7 @@ impl<'a> ReactJsx<'a> { let index = program .body .iter() - .rposition(|stmt| matches!(stmt, Statement::ModuleDeclaration(m) if m.is_import())) + .rposition(|stmt| matches!(stmt, Statement::ImportDeclaration(_))) .map_or(0, |i| i + 1); program.body.splice(index..index, imports); } @@ -257,7 +257,7 @@ impl<'a> ReactJsx<'a> { let has_key_after_props_spread = e.has_key_after_props_spread(); let mut arguments = self.ast().new_vec(); - arguments.push(Argument::Expression(match e { + arguments.push(Argument::from(match e { JSXElementOrFragment::Element(e) => { self.transform_element_name(&e.opening_element.name) } @@ -273,7 +273,7 @@ impl<'a> ReactJsx<'a> { // Add `null` to second argument in classic mode if is_classic && attributes_len == 0 { let null_expr = self.ast().literal_null_expression(NullLiteral::new(SPAN)); - arguments.push(Argument::Expression(null_expr)); + arguments.push(Argument::from(null_expr)); } // The object properties for the second argument of `React.createElement` @@ -292,7 +292,7 @@ impl<'a> ReactJsx<'a> { // deopt if spreading an object with `__proto__` key if !matches!(&spread.argument, Expression::ObjectExpression(o) if o.has_proto()) { - arguments.push(Argument::Expression(self.ast().copy(&spread.argument))); + arguments.push(Argument::from(self.ast().copy(&spread.argument))); continue; } } @@ -340,7 +340,7 @@ impl<'a> ReactJsx<'a> { children.pop().unwrap() } else { let elements = Vec::from_iter_in( - children.into_iter().map(ArrayExpressionElement::Expression), + children.into_iter().map(ArrayExpressionElement::from), allocator, ); need_jsxs = true; @@ -375,11 +375,11 @@ impl<'a> ReactJsx<'a> { if !properties.is_empty() || is_automatic { let object_expression = self.ast().object_expression(SPAN, properties, None); - arguments.push(Argument::Expression(object_expression)); + arguments.push(Argument::from(object_expression)); } if is_automatic && key_prop.is_some() { - arguments.push(Argument::Expression(self.transform_jsx_attribute_value(key_prop))); + arguments.push(Argument::from(self.transform_jsx_attribute_value(key_prop))); } if is_classic && !children.is_empty() { @@ -387,7 +387,7 @@ impl<'a> ReactJsx<'a> { children .iter() .filter_map(|child| self.transform_jsx_child(child)) - .map(Argument::Expression), + .map(Argument::from), ); } @@ -573,7 +573,7 @@ impl<'a> ReactJsx<'a> { self.transform_jsx(&JSXElementOrFragment::Fragment(e)) } Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression { - JSXExpression::Expression(e) => self.ast().copy(e), + e @ match_expression!(JSXExpression) => self.ast().copy(e.to_expression()), JSXExpression::EmptyExpression(_e) => { self.ast().literal_boolean_expression(BooleanLiteral::new(SPAN, true)) } @@ -586,7 +586,7 @@ impl<'a> ReactJsx<'a> { match child { JSXChild::Text(text) => self.transform_jsx_text(text.value.as_str()), JSXChild::ExpressionContainer(e) => match &e.expression { - JSXExpression::Expression(e) => Some(self.ast().copy(e)), + e @ match_expression!(JSXExpression) => Some(self.ast().copy(e.to_expression())), JSXExpression::EmptyExpression(_) => None, }, JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))), diff --git a/crates/oxc_transformer/src/react/jsx_self/mod.rs b/crates/oxc_transformer/src/react/jsx_self/mod.rs index 925e226db623f..367d9a4be72f5 100644 --- a/crates/oxc_transformer/src/react/jsx_self/mod.rs +++ b/crates/oxc_transformer/src/react/jsx_self/mod.rs @@ -69,7 +69,7 @@ impl<'a> ReactJsxSelf<'a> { let name = JSXAttributeName::Identifier(self.ctx.ast.alloc(JSXIdentifier::new(SPAN, SELF.into()))); let value = { - let jsx_expr = JSXExpression::Expression(self.ctx.ast.this_expression(SPAN)); + let jsx_expr = JSXExpression::from(self.ctx.ast.this_expression(SPAN)); let container = self.ctx.ast.jsx_expression_container(SPAN, jsx_expr); JSXAttributeValue::ExpressionContainer(container) }; diff --git a/crates/oxc_transformer/src/react/jsx_source/mod.rs b/crates/oxc_transformer/src/react/jsx_source/mod.rs index 3b9c014076e9e..656d39d662b6b 100644 --- a/crates/oxc_transformer/src/react/jsx_source/mod.rs +++ b/crates/oxc_transformer/src/react/jsx_source/mod.rs @@ -84,7 +84,7 @@ impl<'a> ReactJsxSource<'a> { self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())), ); let object = self.get_source_object(); - let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::Expression(object)); + let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::from(object)); let value = JSXAttributeValue::ExpressionContainer(expr); let attribute_item = self.ctx.ast.jsx_attribute(SPAN, key, Some(value)); elem.attributes.push(JSXAttributeItem::Attribute(attribute_item)); @@ -139,6 +139,6 @@ impl<'a> ReactJsxSource<'a> { self.ctx.ast.new_vec_single(decl) }; let var_decl = self.ctx.ast.variable_declaration(SPAN, var_kind, decl, Modifiers::empty()); - Statement::Declaration(Declaration::VariableDeclaration(var_decl)) + Statement::VariableDeclaration(var_decl) } } diff --git a/crates/oxc_transformer/src/typescript/annotations.rs b/crates/oxc_transformer/src/typescript/annotations.rs index 595486796e800..8b9742fb1577e 100644 --- a/crates/oxc_transformer/src/typescript/annotations.rs +++ b/crates/oxc_transformer/src/typescript/annotations.rs @@ -65,16 +65,16 @@ impl<'a> TypeScriptAnnotations<'a> { program.body.retain_mut(|stmt| { // fix namespace/export-type-only/input.ts // The namespace is type only. So if its name appear in the ExportNamedDeclaration, we should remove it. - if let Statement::Declaration(Declaration::TSModuleDeclaration(decl)) = stmt { + if let Statement::TSModuleDeclaration(decl) = stmt { type_names.insert(decl.id.name().clone()); return false; } - let Statement::ModuleDeclaration(module_decl) = stmt else { + let Some(module_decl) = stmt.as_module_declaration_mut() else { return true; }; - let need_delete = match &mut **module_decl { + let need_delete = match module_decl { ModuleDeclaration::ExportNamedDeclaration(decl) => { decl.specifiers.retain(|specifier| { !(specifier.export_kind.is_type() @@ -302,14 +302,8 @@ impl<'a> TypeScriptAnnotations<'a> { // Remove TS specific statements stmts.retain(|stmt| match stmt { Statement::ExpressionStatement(s) => !s.expression.is_typescript_syntax(), - Statement::Declaration(s) => { - // Removed in transform_program_on_exit - if matches!(s, Declaration::TSModuleDeclaration(_)) { - true - } else { - !s.is_typescript_syntax() - } - } + Statement::TSModuleDeclaration(_) => true, + match_declaration!(Statement) => !stmt.to_declaration().is_typescript_syntax(), // Ignore ModuleDeclaration as it's handled in the program _ => true, }); diff --git a/crates/oxc_transformer/src/typescript/enum.rs b/crates/oxc_transformer/src/typescript/enum.rs index fda6b11fd711e..b444745108bd1 100644 --- a/crates/oxc_transformer/src/typescript/enum.rs +++ b/crates/oxc_transformer/src/typescript/enum.rs @@ -73,7 +73,7 @@ impl<'a> TypeScript<'a> { .identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone())); let right = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None); let expression = self.ctx.ast.logical_expression(SPAN, left, op, right); - arguments.push(Argument::Expression(expression)); + arguments.push(Argument::from(expression)); let call_expression = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); @@ -111,10 +111,12 @@ impl<'a> TypeScript<'a> { for member in members.iter_mut() { let (member_name, member_span) = match &member.id { - TSEnumMemberName::Identifier(id) => (&id.name, id.span), - TSEnumMemberName::StringLiteral(str) => (&str.value, str.span), - TSEnumMemberName::ComputedPropertyName(..) - | TSEnumMemberName::NumericLiteral(..) => unreachable!(), + TSEnumMemberName::StaticIdentifier(id) => (&id.name, id.span), + TSEnumMemberName::StaticStringLiteral(str) => (&str.value, str.span), + #[allow(clippy::unnested_or_patterns)] // Clippy is wrong + TSEnumMemberName::StaticNumericLiteral(_) | match_expression!(TSEnumMemberName) => { + unreachable!() + } }; let mut init = self @@ -158,8 +160,7 @@ impl<'a> TypeScript<'a> { decls }; let decl = self.ctx.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty()); - let stmt: Statement<'_> = - Statement::Declaration(Declaration::VariableDeclaration(decl)); + let stmt: Statement<'_> = Statement::VariableDeclaration(decl); statements.push(stmt); } diff --git a/crates/oxc_transformer/src/typescript/module.rs b/crates/oxc_transformer/src/typescript/module.rs index 2b5d0bb917612..e562def48270e 100644 --- a/crates/oxc_transformer/src/typescript/module.rs +++ b/crates/oxc_transformer/src/typescript/module.rs @@ -45,8 +45,8 @@ impl<'a> TypeScript<'a> { let decl_span = decl.span; let init = match &mut decl.module_reference { - TSModuleReference::TypeName(type_name) => { - self.transform_ts_type_name(&mut *type_name) + type_name @ match_ts_type_name!(TSModuleReference) => { + self.transform_ts_type_name(&mut *type_name.to_ts_type_name_mut()) } TSModuleReference::ExternalModuleReference(reference) => { if self.ctx.source_type().is_module() { @@ -56,7 +56,7 @@ impl<'a> TypeScript<'a> { let callee = self.ctx.ast.identifier_reference_expression( IdentifierReference::new(SPAN, "require".into()), ); - let arguments = self.ctx.ast.new_vec_single(Argument::Expression( + let arguments = self.ctx.ast.new_vec_single(Argument::from( self.ctx.ast.literal_string_expression(reference.expression.clone()), )); self.ctx.ast.call_expression(SPAN, callee, arguments, false, None) diff --git a/crates/oxc_transformer/src/typescript/namespace.rs b/crates/oxc_transformer/src/typescript/namespace.rs index 4833e2a1829b0..7cddfae5e858d 100644 --- a/crates/oxc_transformer/src/typescript/namespace.rs +++ b/crates/oxc_transformer/src/typescript/namespace.rs @@ -28,27 +28,24 @@ impl<'a> TypeScript<'a> { for stmt in self.ctx.ast.move_statement_vec(&mut program.body) { match stmt { - Statement::Declaration(Declaration::TSModuleDeclaration(decl)) => { + Statement::TSModuleDeclaration(decl) => { if !decl.modifiers.is_contains_declare() { if let Some(transformed_stmt) = self.handle_nested(self.ctx.ast.copy(&decl).unbox(), None) { let name = decl.id.name(); if names.insert(name.clone()) { - new_stmts.push(Statement::Declaration( - self.create_variable_declaration(name), - )); + new_stmts + .push(Statement::from(self.create_variable_declaration(name))); } new_stmts.push(transformed_stmt); continue; } } - new_stmts.push(Statement::Declaration(Declaration::TSModuleDeclaration(decl))); + new_stmts.push(Statement::TSModuleDeclaration(decl)); } - Statement::ModuleDeclaration(module_decl) => { - if let ModuleDeclaration::ExportNamedDeclaration(export_decl) = - self.ctx.ast.copy(&module_decl).unbox() - { + match_module_declaration!(Statement) => { + if let Statement::ExportNamedDeclaration(export_decl) = &stmt { if let Some(Declaration::TSModuleDeclaration(decl)) = &export_decl.declaration { @@ -81,29 +78,29 @@ impl<'a> TypeScript<'a> { } } - module_decl.bound_names(&mut |id| { + stmt.to_module_declaration().bound_names(&mut |id| { names.insert(id.name.clone()); }); - new_stmts.push(Statement::ModuleDeclaration(module_decl)); + new_stmts.push(stmt); } // Collect bindings from class, function, variable and enum declarations - Statement::Declaration(Declaration::FunctionDeclaration(ref decl)) => { + Statement::FunctionDeclaration(ref decl) => { if let Some(ident) = &decl.id { names.insert(ident.name.clone()); } new_stmts.push(stmt); } - Statement::Declaration(Declaration::ClassDeclaration(ref decl)) => { + Statement::ClassDeclaration(ref decl) => { if let Some(ident) = &decl.id { names.insert(ident.name.clone()); } new_stmts.push(stmt); } - Statement::Declaration(Declaration::TSEnumDeclaration(ref decl)) => { + Statement::TSEnumDeclaration(ref decl) => { names.insert(decl.id.name.clone()); new_stmts.push(stmt); } - Statement::Declaration(Declaration::VariableDeclaration(ref decl)) => { + Statement::VariableDeclaration(ref decl) => { decl.bound_names(&mut |id| { names.insert(id.name.clone()); }); @@ -157,102 +154,103 @@ impl<'a> TypeScript<'a> { for stmt in namespace_top_level { match stmt { - Statement::Declaration(Declaration::TSModuleDeclaration(decl)) => { + Statement::TSModuleDeclaration(decl) => { let module_name = decl.id.name().clone(); if let Some(transformed) = self.handle_nested(decl.unbox(), None) { is_empty = false; if names.insert(module_name.clone()) { - new_stmts.push(Statement::Declaration( + new_stmts.push(Statement::from( self.create_variable_declaration(&module_name), )); } new_stmts.push(transformed); } } - Statement::Declaration(Declaration::ClassDeclaration(decl)) => { + Statement::ClassDeclaration(decl) => { is_empty = false; decl.bound_names(&mut |id| { names.insert(id.name.clone()); }); - new_stmts.push(Statement::Declaration(Declaration::ClassDeclaration(decl))); + new_stmts.push(Statement::ClassDeclaration(decl)); } - Statement::Declaration(Declaration::TSEnumDeclaration(enum_decl)) => { + Statement::TSEnumDeclaration(enum_decl) => { is_empty = false; names.insert(enum_decl.id.name.clone()); - new_stmts - .push(Statement::Declaration(Declaration::TSEnumDeclaration(enum_decl))); + new_stmts.push(Statement::TSEnumDeclaration(enum_decl)); } - Statement::ModuleDeclaration(decl) => { - if let ModuleDeclaration::ExportNamedDeclaration(export_decl) = decl.unbox() { - let export_decl = export_decl.unbox(); - if let Some(decl) = export_decl.declaration { - if decl.modifiers().is_some_and(Modifiers::is_contains_declare) { - continue; + Statement::ExportNamedDeclaration(export_decl) => { + let export_decl = export_decl.unbox(); + if let Some(decl) = export_decl.declaration { + if decl.modifiers().is_some_and(Modifiers::is_contains_declare) { + continue; + } + match decl { + Declaration::TSEnumDeclaration(enum_decl) => { + is_empty = false; + self.add_declaration( + Declaration::TSEnumDeclaration(enum_decl), + &name, + &mut names, + &mut new_stmts, + ); } - match decl { - Declaration::TSEnumDeclaration(enum_decl) => { - is_empty = false; - self.add_declaration( - Declaration::TSEnumDeclaration(enum_decl), - &name, - &mut names, - &mut new_stmts, - ); - } - Declaration::FunctionDeclaration(func_decl) => { - is_empty = false; - self.add_declaration( - Declaration::FunctionDeclaration(func_decl), - &name, - &mut names, - &mut new_stmts, - ); - } - Declaration::ClassDeclaration(class_decl) => { - is_empty = false; - self.add_declaration( - Declaration::ClassDeclaration(class_decl), - &name, - &mut names, - &mut new_stmts, - ); - } - Declaration::VariableDeclaration(var_decl) => { + Declaration::FunctionDeclaration(func_decl) => { + is_empty = false; + self.add_declaration( + Declaration::FunctionDeclaration(func_decl), + &name, + &mut names, + &mut new_stmts, + ); + } + Declaration::ClassDeclaration(class_decl) => { + is_empty = false; + self.add_declaration( + Declaration::ClassDeclaration(class_decl), + &name, + &mut names, + &mut new_stmts, + ); + } + Declaration::VariableDeclaration(var_decl) => { + is_empty = false; + let stmts = self.handle_variable_declaration(var_decl, &name); + new_stmts.extend(stmts); + } + Declaration::TSModuleDeclaration(module_decl) => { + let module_name = module_decl.id.name().clone(); + if let Some(transformed) = self.handle_nested( + module_decl.unbox(), + Some(self.ctx.ast.identifier_reference_expression( + IdentifierReference::new(SPAN, name.clone()), + )), + ) { is_empty = false; - let stmts = self.handle_variable_declaration(var_decl, &name); - new_stmts.extend(stmts); - } - Declaration::TSModuleDeclaration(module_decl) => { - let module_name = module_decl.id.name().clone(); - if let Some(transformed) = self.handle_nested( - module_decl.unbox(), - Some(self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, name.clone()), - )), - ) { - is_empty = false; - if names.insert(module_name.clone()) { - new_stmts.push(Statement::Declaration( - self.create_variable_declaration(&module_name), - )); - } - new_stmts.push(transformed); + if names.insert(module_name.clone()) { + new_stmts.push(Statement::from( + self.create_variable_declaration(&module_name), + )); } + new_stmts.push(transformed); } - _ => {} } - } else { - let stmt = self.ctx.ast.module_declaration( - ModuleDeclaration::ExportNamedDeclaration( - self.ctx.ast.alloc(export_decl), - ), - ); - new_stmts.push(stmt); + _ => {} } + } else { + let stmt = self.ctx.ast.module_declaration( + ModuleDeclaration::ExportNamedDeclaration( + self.ctx.ast.alloc(export_decl), + ), + ); + new_stmts.push(stmt); } } - Statement::Declaration(decl) if decl.is_typescript_syntax() => continue, stmt => { + if let Some(decl) = stmt.as_declaration() { + if decl.is_typescript_syntax() { + continue; + } + } is_empty = false; new_stmts.push(stmt); } @@ -382,7 +380,7 @@ impl<'a> TypeScript<'a> { let op = LogicalOperator::Or; let expr = self.ctx.ast.logical_expression(SPAN, logical_left, op, logical_right); - self.ctx.ast.new_vec_single(Argument::Expression(expr)) + self.ctx.ast.new_vec_single(Argument::from(expr)) }; let expr = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); @@ -401,7 +399,7 @@ impl<'a> TypeScript<'a> { if let Some(ident) = decl.id() { let item_name = ident.name.clone(); let assignment_statement = self.create_assignment_statement(name, &item_name); - new_stmts.push(Statement::Declaration(decl)); + new_stmts.push(Statement::from(decl)); let assignment_statement = self.ctx.ast.expression_statement(SPAN, assignment_statement); new_stmts.push(assignment_statement); @@ -415,8 +413,7 @@ impl<'a> TypeScript<'a> { let object = self.ctx.ast.identifier_reference_expression(ident); let property = IdentifierName::new(SPAN, item_name.clone()); let left = self.ctx.ast.static_member(SPAN, object, property, false); - let left = SimpleAssignmentTarget::MemberAssignmentTarget(self.ctx.ast.alloc(left)); - let left = AssignmentTarget::SimpleAssignmentTarget(left); + let left = AssignmentTarget::from(left); let ident = self.ctx.ast.identifier_reference(SPAN, item_name.as_str()); let right = self.ctx.ast.identifier_reference_expression(ident); let op = AssignmentOperator::Assign; @@ -459,9 +456,7 @@ impl<'a> TypeScript<'a> { )); } }); - return self.ctx.ast.new_vec_single(Statement::Declaration( - Declaration::VariableDeclaration(var_decl), - )); + return self.ctx.ast.new_vec_single(Statement::VariableDeclaration(var_decl)); } // Now we have pattern in declarators @@ -472,7 +467,7 @@ impl<'a> TypeScript<'a> { }); let mut stmts = self.ctx.ast.new_vec_with_capacity(2); - stmts.push(Statement::Declaration(Declaration::VariableDeclaration(var_decl))); + stmts.push(Statement::VariableDeclaration(var_decl)); stmts.push( self.ctx .ast @@ -485,13 +480,9 @@ impl<'a> TypeScript<'a> { /// Check if the statements contain a namespace declaration fn has_namespace(stmts: &[Statement]) -> bool { stmts.iter().any(|stmt| match stmt { - Statement::Declaration(Declaration::TSModuleDeclaration(_)) => true, - Statement::ModuleDeclaration(module_decl) => { - if let ModuleDeclaration::ExportNamedDeclaration(decl) = &**module_decl { - matches!(decl.declaration, Some(Declaration::TSModuleDeclaration(_))) - } else { - false - } + Statement::TSModuleDeclaration(_) => true, + Statement::ExportNamedDeclaration(decl) => { + matches!(decl.declaration, Some(Declaration::TSModuleDeclaration(_))) } _ => false, }) diff --git a/tasks/prettier_conformance/src/spec.rs b/tasks/prettier_conformance/src/spec.rs index 327b56319257d..57de9b215b6ff 100644 --- a/tasks/prettier_conformance/src/spec.rs +++ b/tasks/prettier_conformance/src/spec.rs @@ -43,7 +43,7 @@ impl VisitMut<'_> for SpecParser { let mut options = PrettierOptions::default(); if let Some(argument) = expr.arguments.get(1) { - let Argument::Expression(argument_expr) = argument else { + let Some(argument_expr) = argument.as_expression() else { return; }; @@ -52,10 +52,7 @@ impl VisitMut<'_> for SpecParser { .elements .iter() .filter_map(|el| { - if let ArrayExpressionElement::Expression(Expression::StringLiteral( - literal, - )) = el - { + if let ArrayExpressionElement::StringLiteral(literal) = el { return Some(literal.value.to_string()); } None @@ -66,9 +63,7 @@ impl VisitMut<'_> for SpecParser { return; } - if let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) = - expr.arguments.get(2) - { + if let Some(Argument::ObjectExpression(obj_expr)) = expr.arguments.get(2) { obj_expr.properties.iter().for_each(|item| { if let ObjectPropertyKind::ObjectProperty(obj_prop) = item { if let Some(name) = obj_prop.key.static_name() { diff --git a/tasks/rulegen/src/main.rs b/tasks/rulegen/src/main.rs index b50237d2405ad..4c841d9c213a5 100644 --- a/tasks/rulegen/src/main.rs +++ b/tasks/rulegen/src/main.rs @@ -9,9 +9,9 @@ use oxc_allocator::Allocator; use oxc_ast::{ ast::{ Argument, ArrayExpressionElement, CallExpression, ExportDefaultDeclarationKind, Expression, - ExpressionStatement, MemberExpression, ModuleDeclaration, ObjectExpression, ObjectProperty, - ObjectPropertyKind, Program, PropertyKey, Statement, StaticMemberExpression, StringLiteral, - TaggedTemplateExpression, TemplateLiteral, + ExpressionStatement, ObjectExpression, ObjectProperty, ObjectPropertyKind, Program, + PropertyKey, Statement, StaticMemberExpression, StringLiteral, TaggedTemplateExpression, + TemplateLiteral, }, Visit, }; @@ -141,13 +141,12 @@ impl<'a> Visit<'a> for TestCase<'a> { } fn visit_call_expression(&mut self, expr: &CallExpression<'a>) { - if let Expression::MemberExpression(member_expr) = &expr.callee { + if let Some(member_expr) = expr.callee.as_member_expression() { if let Expression::ArrayExpression(array_expr) = member_expr.object() { // ['class A {', '}'].join('\n') let mut code = String::new(); for arg in &array_expr.elements { - let ArrayExpressionElement::Expression(Expression::StringLiteral(lit)) = arg - else { + let ArrayExpressionElement::StringLiteral(lit) = arg else { continue; }; code.push_str(lit.value.as_str()); @@ -163,7 +162,7 @@ impl<'a> Visit<'a> for TestCase<'a> { for obj_prop in &expr.properties { match obj_prop { ObjectPropertyKind::ObjectProperty(prop) => match &prop.key { - PropertyKey::Identifier(ident) if ident.name == "code" => { + PropertyKey::StaticIdentifier(ident) if ident.name == "code" => { self.code = match &prop.value { Expression::StringLiteral(s) => Some(s.value.to_string()), Expression::TaggedTemplateExpression(tag_expr) => { @@ -177,15 +176,10 @@ impl<'a> Visit<'a> for TestCase<'a> { } // handle code like ["{", "a: 1", "}"].join("\n") Expression::CallExpression(call_expr) => { - if !call_expr.arguments.first().is_some_and(|arg| matches!(arg, Argument::Expression(Expression::StringLiteral(string)) if string.value == "\n")) { + if !call_expr.arguments.first().is_some_and(|arg| matches!(arg, Argument::StringLiteral(string) if string.value == "\n")) { continue; } - let Expression::MemberExpression(member_expr) = &call_expr.callee - else { - continue; - }; - let MemberExpression::StaticMemberExpression(member) = - &**member_expr + let Expression::StaticMemberExpression(member) = &call_expr.callee else { continue; }; @@ -200,9 +194,9 @@ impl<'a> Visit<'a> for TestCase<'a> { .elements .iter() .map(|arg| match arg { - ArrayExpressionElement::Expression( - Expression::StringLiteral(string), - ) => string.value.as_str(), + ArrayExpressionElement::StringLiteral(string) => { + string.value.as_str() + } _ => "", }) .collect::>() @@ -212,13 +206,13 @@ impl<'a> Visit<'a> for TestCase<'a> { _ => continue, } } - PropertyKey::Identifier(ident) if ident.name == "options" => { + PropertyKey::StaticIdentifier(ident) if ident.name == "options" => { let span = prop.value.span(); let option_text = &self.source_text[span.start as usize..span.end as usize]; self.config = Some(Cow::Owned(json::convert_config_to_json_literal(option_text))); } - PropertyKey::Identifier(ident) if ident.name == "settings" => { + PropertyKey::StaticIdentifier(ident) if ident.name == "settings" => { let span = prop.value.span(); let setting_text = &self.source_text[span.start as usize..span.end as usize]; @@ -347,14 +341,11 @@ impl<'a> Visit<'a> for State<'a> { match stmt { Statement::ExpressionStatement(expr_stmt) => self.visit_expression_statement(expr_stmt), // for eslint-plugin-jsdoc - Statement::ModuleDeclaration(mod_decl) => { - if let ModuleDeclaration::ExportDefaultDeclaration(export_decl) = &**mod_decl { - if let ExportDefaultDeclarationKind::Expression(Expression::ObjectExpression( - obj_expr, - )) = &export_decl.declaration - { - self.visit_object_expression(obj_expr); - } + Statement::ExportDefaultDeclaration(export_decl) => { + if let ExportDefaultDeclarationKind::ObjectExpression(obj_expr) = + &export_decl.declaration + { + self.visit_object_expression(obj_expr); } } _ => {} @@ -371,9 +362,7 @@ impl<'a> Visit<'a> for State<'a> { // Add describe's first parameter as part group comment // e.g. for `describe('valid', () => { ... })`, the group comment will be "valid" if ident.name == "describe" { - if let Some(Argument::Expression(Expression::StringLiteral(lit))) = - expr.arguments.first() - { + if let Some(Argument::StringLiteral(lit)) = expr.arguments.first() { pushed = true; self.group_comment_stack.push(lit.value.to_string()); } @@ -391,13 +380,13 @@ impl<'a> Visit<'a> for State<'a> { } fn visit_object_property(&mut self, prop: &ObjectProperty<'a>) { - let PropertyKey::Identifier(ident) = &prop.key else { return }; + let PropertyKey::StaticIdentifier(ident) = &prop.key else { return }; match ident.name.as_str() { "valid" => { if let Expression::ArrayExpression(array_expr) = &prop.value { let array_expr = self.alloc(array_expr); for arg in &array_expr.elements { - if let ArrayExpressionElement::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_valid_test(expr); } } @@ -407,21 +396,21 @@ impl<'a> Visit<'a> for State<'a> { if let Some(args) = find_parser_arguments(&prop.value).map(|args| self.alloc(args)) { for arg in args { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_valid_test(expr); } } } if let Expression::CallExpression(call_expr) = &prop.value { - if let Expression::MemberExpression(_) = &call_expr.callee { + if call_expr.callee.is_member_expression() { // for eslint-plugin-react - if let Some(Argument::Expression(Expression::ArrayExpression(array_expr))) = + if let Some(Argument::ArrayExpression(array_expr)) = call_expr.arguments.first() { let array_expr = self.alloc(array_expr); for arg in &array_expr.elements { - if let ArrayExpressionElement::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_valid_test(expr); } } @@ -433,7 +422,7 @@ impl<'a> Visit<'a> for State<'a> { if let Expression::ArrayExpression(array_expr) = &prop.value { let array_expr = self.alloc(array_expr); for arg in &array_expr.elements { - if let ArrayExpressionElement::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_invalid_test(expr); } } @@ -443,7 +432,7 @@ impl<'a> Visit<'a> for State<'a> { if let Some(args) = find_parser_arguments(&prop.value).map(|args| self.alloc(args)) { for arg in args { - if let Argument::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_invalid_test(expr); } } @@ -451,13 +440,13 @@ impl<'a> Visit<'a> for State<'a> { // for eslint-plugin-react if let Expression::CallExpression(call_expr) = &prop.value { - if let Expression::MemberExpression(_) = &call_expr.callee { - if let Some(Argument::Expression(Expression::ArrayExpression(array_expr))) = + if call_expr.callee.is_member_expression() { + if let Some(Argument::ArrayExpression(array_expr)) = call_expr.arguments.first() { let array_expr = self.alloc(array_expr); for arg in &array_expr.elements { - if let ArrayExpressionElement::Expression(expr) = arg { + if let Some(expr) = arg.as_expression() { self.add_invalid_test(expr); } } @@ -471,30 +460,29 @@ impl<'a> Visit<'a> for State<'a> { } fn find_parser_arguments<'a, 'b>( - expr: &'b Expression<'a>, + mut expr: &'b Expression<'a>, ) -> Option<&'b oxc_allocator::Vec<'a, Argument<'a>>> { - let Expression::CallExpression(call_expr) = expr else { return None }; - let Expression::MemberExpression(member_expr) = &call_expr.callee else { - return None; - }; - let MemberExpression::StaticMemberExpression(StaticMemberExpression { - object, property, .. - }) = &**member_expr - else { - return None; - }; - match (object, call_expr.arguments.first()) { - (Expression::Identifier(iden), Some(Argument::Expression(arg))) - if iden.name == "parsers" && property.name == "all" => - { - if let Expression::CallExpression(call_expr) = arg { - if let Expression::MemberExpression(_) = &call_expr.callee { - return Some(&call_expr.arguments); + loop { + let Expression::CallExpression(call_expr) = expr else { return None }; + let Expression::StaticMemberExpression(static_member_expr) = &call_expr.callee else { + return None; + }; + let StaticMemberExpression { object, property, .. } = &**static_member_expr; + if let Expression::Identifier(iden) = object { + if iden.name == "parsers" && property.name == "all" { + if let Some(arg) = call_expr.arguments.first() { + if let Argument::CallExpression(call_expr) = arg { + if call_expr.callee.is_member_expression() { + return Some(&call_expr.arguments); + } + return None; + } else if arg.is_expression() { + return None; + } } } - None } - _ => find_parser_arguments(object), + expr = object; } } From c7a9672c2f47b712b6b103cb31ec09ee3baf40e8 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Sun, 28 Apr 2024 11:38:43 +0100 Subject: [PATCH 2/2] Improve documentation of inheritance --- crates/oxc_ast/src/ast/js.rs | 69 ++++++++++++----- crates/oxc_ast/src/ast/jsx.rs | 5 +- crates/oxc_ast/src/ast/macros.rs | 125 ++++++++++++++++++++++++++++--- crates/oxc_ast/src/ast/ts.rs | 20 ++++- 4 files changed, 184 insertions(+), 35 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index aad59dc490da8..2a6c0167a0739 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -63,6 +63,8 @@ impl<'a> Program<'a> { inherit_variants! { /// Expression +/// +/// Inherits variants from [`MemberExpression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -490,17 +492,19 @@ pub struct ArrayExpression<'a> { inherit_variants! { /// Array Expression Element +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ArrayExpressionElement<'a> { SpreadElement(Box<'a, SpreadElement<'a>>) = 64, - // `Expression` variants added here by `inherit_variants!` macro - @inherit Expression /// Array hole for sparse arrays /// Elision(Elision) = 65, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression } } @@ -560,6 +564,9 @@ pub struct ObjectProperty<'a> { } inherit_variants! { +/// Property Key +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -936,6 +943,8 @@ pub struct SpreadElement<'a> { inherit_variants! { /// Argument +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1038,6 +1047,8 @@ pub struct AssignmentExpression<'a> { inherit_variants! { /// Destructuring Assignment +/// +/// Inherits variants from [`SimpleAssignmentTarget`] and [`AssignmentTargetPattern`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1070,18 +1081,21 @@ macro_rules! match_assignment_target { pub use match_assignment_target; inherit_variants! { +/// Simple Assignment Target +/// +/// Inherits variants from [`MemberExpression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum SimpleAssignmentTarget<'a> { AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>) = 0, - // `MemberExpression` variants added here by `inherit_variants!` macro - @inherit MemberExpression TSAsExpression(Box<'a, TSAsExpression<'a>>) = 1, TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 2, TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, + // `MemberExpression` variants added here by `inherit_variants!` macro + @inherit MemberExpression } } @@ -1203,14 +1217,17 @@ pub struct AssignmentTargetRest<'a> { } inherit_variants! { +/// Assignment Target Maybe Default +/// +/// Inherits variants from [`AssignmentTarget`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum AssignmentTargetMaybeDefault<'a> { + AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>) = 16, // `AssignmentTarget` variants added here by `inherit_variants!` macro @inherit AssignmentTarget - AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>) = 16, } } @@ -1308,6 +1325,9 @@ pub struct ChainExpression<'a> { } inherit_variants! { +/// Chain Element +/// +/// Inherits variants from [`MemberExpression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1330,7 +1350,9 @@ pub struct ParenthesizedExpression<'a> { } inherit_variants! { -/// Statements +/// Statement +/// +/// Inherits variants from [`Declaration`] and [`ModuleDeclaration`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1630,15 +1652,18 @@ pub struct ForStatement<'a> { } inherit_variants! { +/// For Statement Init +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ForStatementInit<'a> { VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 64, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 65, // `Expression` variants added here by `inherit_variants!` macro @inherit Expression - UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 65, } } @@ -1676,15 +1701,18 @@ pub struct ForOfStatement<'a> { } inherit_variants! { +/// For Statement Left +/// +/// Inherits variants from [`AssignmentTarget`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ForStatementLeft<'a> { VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 16, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 17, // `AssignmentTarget` variants added here by `inherit_variants!` macro @inherit AssignmentTarget - UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 17, } } @@ -2432,20 +2460,20 @@ pub struct StaticBlock<'a> { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ModuleDeclaration<'a> { - /// import hello from './world.js'; - /// import * as t from './world.js'; + /// `import hello from './world.js';` + /// `import * as t from './world.js';` ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, - /// export * as numbers from '../numbers.js' + /// `export * as numbers from '../numbers.js'` ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, - /// export default 5; + /// `export default 5;` ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>) = 66, - /// export {five} from './numbers.js'; - /// export {six, seven}; + /// `export {five} from './numbers.js';` + /// `export {six, seven};` ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>) = 67, - /// export = 5; + /// `export = 5;` TSExportAssignment(Box<'a, TSExportAssignment<'a>>) = 68, - /// export as namespace React; + /// `export as namespace React;` TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>) = 69, } @@ -2705,19 +2733,22 @@ impl<'a> ExportSpecifier<'a> { } inherit_variants! { +/// Export Default Declaration Kind +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ExportDefaultDeclarationKind<'a> { - // `Expression` variants added here by `inherit_variants!` macro - @inherit Expression - FunctionDeclaration(Box<'a, Function<'a>>) = 64, ClassDeclaration(Box<'a, Class<'a>>) = 65, TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 66, TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 67, + + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression } } diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index 423525bf6f1ba..5938d6a6f9ab6 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -147,14 +147,17 @@ pub struct JSXExpressionContainer<'a> { } inherit_variants! { +/// JSX Expression +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum JSXExpression<'a> { + EmptyExpression(JSXEmptyExpression) = 64, // `Expression` variants added here by `inherit_variants!` macro @inherit Expression - EmptyExpression(JSXEmptyExpression) = 64, } } diff --git a/crates/oxc_ast/src/ast/macros.rs b/crates/oxc_ast/src/ast/macros.rs index c356bd950d6a4..49c7422c82fac 100644 --- a/crates/oxc_ast/src/ast/macros.rs +++ b/crates/oxc_ast/src/ast/macros.rs @@ -79,49 +79,88 @@ macro_rules! inherit_variants { // `Expression`'s own variants + /// Inherited from [`Expression`] BooleanLiteral(Box<'a, BooleanLiteral>) = 0, + /// Inherited from [`Expression`] NullLiteral(Box<'a, NullLiteral>) = 1, + /// Inherited from [`Expression`] NumericLiteral(Box<'a, NumericLiteral<'a>>) = 2, + /// Inherited from [`Expression`] BigintLiteral(Box<'a, BigIntLiteral<'a>>) = 3, + /// Inherited from [`Expression`] RegExpLiteral(Box<'a, RegExpLiteral<'a>>) = 4, + /// Inherited from [`Expression`] StringLiteral(Box<'a, StringLiteral<'a>>) = 5, + /// Inherited from [`Expression`] TemplateLiteral(Box<'a, TemplateLiteral<'a>>) = 6, + /// Inherited from [`Expression`] Identifier(Box<'a, IdentifierReference<'a>>) = 7, + /// Inherited from [`Expression`] MetaProperty(Box<'a, MetaProperty<'a>>) = 8, + /// Inherited from [`Expression`] Super(Box<'a, Super>) = 9, + /// Inherited from [`Expression`] ArrayExpression(Box<'a, ArrayExpression<'a>>) = 10, + /// Inherited from [`Expression`] ArrowFunctionExpression(Box<'a, ArrowFunctionExpression<'a>>) = 11, + /// Inherited from [`Expression`] AssignmentExpression(Box<'a, AssignmentExpression<'a>>) = 12, + /// Inherited from [`Expression`] AwaitExpression(Box<'a, AwaitExpression<'a>>) = 13, + /// Inherited from [`Expression`] BinaryExpression(Box<'a, BinaryExpression<'a>>) = 14, + /// Inherited from [`Expression`] CallExpression(Box<'a, CallExpression<'a>>) = 15, + /// Inherited from [`Expression`] ChainExpression(Box<'a, ChainExpression<'a>>) = 16, + /// Inherited from [`Expression`] ClassExpression(Box<'a, Class<'a>>) = 17, + /// Inherited from [`Expression`] ConditionalExpression(Box<'a, ConditionalExpression<'a>>) = 18, + /// Inherited from [`Expression`] FunctionExpression(Box<'a, Function<'a>>) = 19, + /// Inherited from [`Expression`] ImportExpression(Box<'a, ImportExpression<'a>>) = 20, + /// Inherited from [`Expression`] LogicalExpression(Box<'a, LogicalExpression<'a>>) = 21, + /// Inherited from [`Expression`] NewExpression(Box<'a, NewExpression<'a>>) = 22, + /// Inherited from [`Expression`] ObjectExpression(Box<'a, ObjectExpression<'a>>) = 23, + /// Inherited from [`Expression`] ParenthesizedExpression(Box<'a, ParenthesizedExpression<'a>>) = 24, + /// Inherited from [`Expression`] SequenceExpression(Box<'a, SequenceExpression<'a>>) = 25, + /// Inherited from [`Expression`] TaggedTemplateExpression(Box<'a, TaggedTemplateExpression<'a>>) = 26, + /// Inherited from [`Expression`] ThisExpression(Box<'a, ThisExpression>) = 27, + /// Inherited from [`Expression`] UnaryExpression(Box<'a, UnaryExpression<'a>>) = 28, + /// Inherited from [`Expression`] UpdateExpression(Box<'a, UpdateExpression<'a>>) = 29, + /// Inherited from [`Expression`] YieldExpression(Box<'a, YieldExpression<'a>>) = 30, + /// Inherited from [`Expression`] PrivateInExpression(Box<'a, PrivateInExpression<'a>>) = 31, + /// Inherited from [`Expression`] JSXElement(Box<'a, JSXElement<'a>>) = 32, + /// Inherited from [`Expression`] JSXFragment(Box<'a, JSXFragment<'a>>) = 33, + /// Inherited from [`Expression`] TSAsExpression(Box<'a, TSAsExpression<'a>>) = 34, + /// Inherited from [`Expression`] TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 35, + /// Inherited from [`Expression`] TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 36, + /// Inherited from [`Expression`] TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 37, + /// Inherited from [`Expression`] TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>) = 38, // Inherited from `MemberExpression` @@ -200,10 +239,13 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + /// Inherited from [`MemberExpression`]. /// `MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]` ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, + /// Inherited from [`MemberExpression`]. /// `MemberExpression[?Yield, ?Await] . IdentifierName` StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, + /// Inherited from [`MemberExpression`]. /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, @@ -281,16 +323,21 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + /// Inherited from [`SimpleAssignmentTarget`] AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>) = 0, - // Inherited from `MemberExpression` - @inherit MemberExpression - + /// Inherited from [`SimpleAssignmentTarget`] TSAsExpression(Box<'a, TSAsExpression<'a>>) = 1, + /// Inherited from [`SimpleAssignmentTarget`] TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 2, + /// Inherited from [`SimpleAssignmentTarget`] TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, + /// Inherited from [`SimpleAssignmentTarget`] TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, + // Inherited from `MemberExpression` + @inherit MemberExpression + $($rest)* } } @@ -330,7 +377,9 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + /// Inherited from [`AssignmentTargetPattern`] ArrayAssignmentTarget(Box<'a, ArrayAssignmentTarget<'a>>) = 8, + /// Inherited from [`AssignmentTargetPattern`] ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>) = 9, $($rest)* @@ -363,15 +412,24 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + /// Inherited from [`Declaration`] VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, + /// Inherited from [`Declaration`] FunctionDeclaration(Box<'a, Function<'a>>) = 33, + /// Inherited from [`Declaration`] ClassDeclaration(Box<'a, Class<'a>>) = 34, + /// Inherited from [`Declaration`] UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 35, + /// Inherited from [`Declaration`] TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 36, + /// Inherited from [`Declaration`] TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 37, + /// Inherited from [`Declaration`] TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 38, + /// Inherited from [`Declaration`] TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 39, + /// Inherited from [`Declaration`] TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40, $($rest)* @@ -414,20 +472,26 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* - /// import hello from './world.js'; - /// import * as t from './world.js'; + /// Inherited from [`ModuleDeclaration`]. + /// `import hello from './world.js';` + /// `import * as t from './world.js';` ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, - /// export * as numbers from '../numbers.js' + /// Inherited from [`ModuleDeclaration`]. + /// `export * as numbers from '../numbers.js'` ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, - /// export default 5; + /// Inherited from [`ModuleDeclaration`]. + /// `export default 5;` ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>) = 66, - /// export {five} from './numbers.js'; - /// export {six, seven}; + /// Inherited from [`ModuleDeclaration`]. + /// `export {five} from './numbers.js';` + /// `export {six, seven};` ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>) = 67, - /// export = 5; + /// Inherited from [`ModuleDeclaration`]. + /// `export = 5;` TSExportAssignment(Box<'a, TSExportAssignment<'a>>) = 68, - /// export as namespace React; + /// Inherited from [`ModuleDeclaration`]. + /// `export as namespace React;` TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>) = 69, $($rest)* @@ -468,42 +532,79 @@ macro_rules! inherit_variants { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* // Keyword + /// Inherited from [`TSType`] TSAnyKeyword(Box<'a, TSAnyKeyword>) = 0, + /// Inherited from [`TSType`] TSBigIntKeyword(Box<'a, TSBigIntKeyword>) = 1, + /// Inherited from [`TSType`] TSBooleanKeyword(Box<'a, TSBooleanKeyword>) = 2, + /// Inherited from [`TSType`] TSNeverKeyword(Box<'a, TSNeverKeyword>) = 3, + /// Inherited from [`TSType`] TSNullKeyword(Box<'a, TSNullKeyword>) = 4, + /// Inherited from [`TSType`] TSNumberKeyword(Box<'a, TSNumberKeyword>) = 5, + /// Inherited from [`TSType`] TSObjectKeyword(Box<'a, TSObjectKeyword>) = 6, + /// Inherited from [`TSType`] TSStringKeyword(Box<'a, TSStringKeyword>) = 7, + /// Inherited from [`TSType`] TSSymbolKeyword(Box<'a, TSSymbolKeyword>) = 8, + /// Inherited from [`TSType`] TSThisType(Box<'a, TSThisType>) = 9, + /// Inherited from [`TSType`] TSUndefinedKeyword(Box<'a, TSUndefinedKeyword>) = 10, + /// Inherited from [`TSType`] TSUnknownKeyword(Box<'a, TSUnknownKeyword>) = 11, + /// Inherited from [`TSType`] TSVoidKeyword(Box<'a, TSVoidKeyword>) = 12, + // Compound + /// Inherited from [`TSType`] TSArrayType(Box<'a, TSArrayType<'a>>) = 13, + /// Inherited from [`TSType`] TSConditionalType(Box<'a, TSConditionalType<'a>>) = 14, + /// Inherited from [`TSType`] TSConstructorType(Box<'a, TSConstructorType<'a>>) = 15, + /// Inherited from [`TSType`] TSFunctionType(Box<'a, TSFunctionType<'a>>) = 16, + /// Inherited from [`TSType`] TSImportType(Box<'a, TSImportType<'a>>) = 17, + /// Inherited from [`TSType`] TSIndexedAccessType(Box<'a, TSIndexedAccessType<'a>>) = 18, + /// Inherited from [`TSType`] TSInferType(Box<'a, TSInferType<'a>>) = 19, + /// Inherited from [`TSType`] TSIntersectionType(Box<'a, TSIntersectionType<'a>>) = 20, + /// Inherited from [`TSType`] TSLiteralType(Box<'a, TSLiteralType<'a>>) = 21, + /// Inherited from [`TSType`] TSMappedType(Box<'a, TSMappedType<'a>>) = 22, + /// Inherited from [`TSType`] TSNamedTupleMember(Box<'a, TSNamedTupleMember<'a>>) = 23, + /// Inherited from [`TSType`] TSQualifiedName(Box<'a, TSQualifiedName<'a>>) = 24, + /// Inherited from [`TSType`] TSTemplateLiteralType(Box<'a, TSTemplateLiteralType<'a>>) = 25, + /// Inherited from [`TSType`] TSTupleType(Box<'a, TSTupleType<'a>>) = 26, + /// Inherited from [`TSType`] TSTypeLiteral(Box<'a, TSTypeLiteral<'a>>) = 27, + /// Inherited from [`TSType`] TSTypeOperatorType(Box<'a, TSTypeOperator<'a>>) = 28, + /// Inherited from [`TSType`] TSTypePredicate(Box<'a, TSTypePredicate<'a>>) = 29, + /// Inherited from [`TSType`] TSTypeQuery(Box<'a, TSTypeQuery<'a>>) = 30, + /// Inherited from [`TSType`] TSTypeReference(Box<'a, TSTypeReference<'a>>) = 31, + /// Inherited from [`TSType`] TSUnionType(Box<'a, TSUnionType<'a>>) = 32, + // JSDoc + /// Inherited from [`TSType`] JSDocNullableType(Box<'a, JSDocNullableType<'a>>) = 33, + /// Inherited from [`TSType`] JSDocUnknownType(Box<'a, JSDocUnknownType>) = 34, $($rest)* @@ -572,7 +673,9 @@ macro_rules! inherit_variants { pub enum $ty<'a> { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* + /// Inherited from [`TSTypeName`] IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0, + /// Inherited from [`TSTypeName`] QualifiedName(Box<'a, TSQualifiedName<'a>>) = 1, $($rest)* diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index a42e38a0b865a..ea0b8ccff4c71 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -61,6 +61,9 @@ pub struct TSEnumMember<'a> { } inherit_variants! { +/// TS Enum Member Name +/// +/// Inherits variants from [`Expression`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -341,17 +344,20 @@ pub struct TSRestType<'a> { } inherit_variants! { +/// TS Tuple Element +/// +/// Inherits variants from [`TSType`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))] pub enum TSTupleElement<'a> { - // `TSType` variants added here by `inherit_variants!` macro - @inherit TSType // Discriminants start at 64, so that `TSTupleElement::is_ts_type` is a single // bitwise AND operation on the discriminant (`discriminant & 63 != 0`). TSOptionalType(Box<'a, TSOptionalType<'a>>) = 64, TSRestType(Box<'a, TSRestType<'a>>) = 65, + // `TSType` variants added here by `inherit_variants!` macro + @inherit TSType } } @@ -847,14 +853,17 @@ pub struct TSTypeQuery<'a> { } inherit_variants! { +/// TS Type Query Expr Name +/// +/// Inherits variants from [`TSTypeName`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum TSTypeQueryExprName<'a> { + TSImportType(Box<'a, TSImportType<'a>>) = 2, // `TSTypeName` variants added here by `inherit_variants!` macro @inherit TSTypeName - TSImportType(Box<'a, TSImportType<'a>>) = 2, } } @@ -998,14 +1007,17 @@ pub struct TSImportEqualsDeclaration<'a> { } inherit_variants! { +/// TS Module Reference +/// +/// Inherits variants from [`TSTypeName`]. #[repr(C, u8)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))] pub enum TSModuleReference<'a> { + ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 2, // `TSTypeName` variants added here by `inherit_variants!` macro @inherit TSTypeName - ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 2, } }