From baf44c89e4ac390c6230f2e72be42b6750cb950f Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Sun, 28 Apr 2024 13:40:37 +0100 Subject: [PATCH] refactor(ast): squash nested enums (#3115) OK, this is a big one... I have done this as part of work on Traversable AST, but I believe it has wider benefits, so thought better to spin it off into its own PR. ## What this PR does This PR squashes all nested AST enum types (#2685). e.g.: Previously: ```rs pub enum Statement<'a> { BlockStatement(Box<'a, BlockStatement<'a>>), /* ...other Statement variants... */ Declaration(Declaration<'a>), } pub enum Declaration<'a> { VariableDeclaration(Box<'a, VariableDeclaration<'a>>), /* ...other Declaration variants... */ } ``` After this PR: ```rs #[repr(C, u8)] pub enum Statement<'a> { BlockStatement(Box<'a, BlockStatement<'a>>) = 0, /* ...other Statement variants... */ VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, /* ...other Declaration variants... */ } #[repr(C, u8)] pub enum Declaration<'a> { VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32, /* ...other Declaration variants... */ } ``` All `Declaration`'s variants are combined into `Statement`, but `Declaration` type still exists. As both types are `#[repr(C, u8)]`, and the discriminants are aligned, a `Declaration` can be transmuted to a `Statement` at zero cost. This is the same thing as #2847, but here applied to *all* nested enums in the AST, and with improved helper methods. No enums increase in size, and a few get smaller. Indirection is reduced for some types (this removes multiple levels of boxing). ## Why? 1. It is a prerequisite for Traversable AST (#2987). 2. It would help a lot with AST Transfer (#2409) - it solves the only remaining blocker for this. 3. It is a step closer to making the whole AST `#[repr(C)]`. ## Why is it a good thing for the AST to be `#[repr(C)]`? Oxc's direction appears to be increasingly to build up control over the fundamental primitives we use, in order to unlock performance and features. We have our own allocator, our own custom implementations for `Box` and `Vec`, our own `IndexVec` (TBC). The AST is the central building block of Oxc, and taking control of its memory layout feels like a step in this same direction. Oxc has a major advantage over other similar libraries in that it keeps all the AST data in an arena. This opens the door to treating the AST either as Rust types or as *pure data* (just bytes). That data can be moved around and manipulated beyond what Rust natively allows. However, to enable that, the types need to be well-specified, with completely stable layouts. `#[repr(C)]` is the only tool Rust provides to do this. Once the types are `#[repr(C)]`, various features become possible: 1. Cheap transfer of the AST across boundaries without ser/deser - the property used by AST Transfer. 2. Having multiple versions of the AST (standard, read-only, traversable), and these AST representations can be converted to one other at zero cost via transmute - the property used by Traversable AST scheme. 3. Caching AST data on disk (#3079) or transferring across network. 4. Stuff we haven't thought of yet! Allowing the AST to be treated as pure data will likely unlock other "next level" features further down the track (caching for "edge bundling" comes to mind). ## The problem with `#[repr(C)]` It's not *required* to squash nested enums to make the AST `#[repr(C)]`. But the problem with `#[repr(C)]` is that it disables some compiler optimizations. Without `#[repr(C)]`, the compiler squashes enums itself in some cases (which is how `Statement` is currently 16 bytes). But making the types `#[repr(C)]` as they are currently disables this optimization. So this PR essentially makes explicit what the compiler is already doing - and in fact goes a bit further with the optimization than the compiler is able to, in squashing 3 or 4 layers of nested enums (the compiler only does up to 2 layers). ## Implementation One enum "inheriting" variants from another is implemented with `inherit_variants!` macro. ```rs inherit_variants! { #[repr(C, u8)] pub enum Statement<'a> { BlockStatement(Box<'a, BlockStatement<'a>>), /* ...other Statement variants... */ // `Declaration` variants added here by `inherit_variants!` macro @inherit Declaration // `ModuleDeclaration` variants added here by `inherit_variants!` macro @inherit ModuleDeclaration } } ``` The macro is *fairly* lightweight, and I think the above is quite easy to understand. No proc macros. The macro also implements utility methods for converting between enums e.g. `Statement::as_declaration`. These methods are all zero-cost (essentially transmutes). New patterns for dealing with nested enums are introduced: Creation: ```rs // Old let stmt = Statement::Declaration(Declaration::VariableDeclaration(var_decl)); // New let stmt = Statement::VariableDeclaration(var_decl); ``` Conversion: ```rs // Old let stmt = Statement::Declaration(decl); // New let stmt = Statement::from(decl); ``` Testing: ```rs // Old if matches!(stmt, Statement::Declaration(_)) { } if matches!(stmt, Statement::ModuleDeclaration(m) if m.is_import()) { } // New if stmt.is_declaration() { } if matches!(stmt, Statement::ImportDeclaration(_)) { } ``` Branching: ```rs // Old if let Statement::Declaration(decl) = &stmt { decl.do_stuff() }; // New if let Some(decl) = stmt.as_declaration() { decl.do_stuff() }; ``` Matching: ```rs // Old match stmt { Statement::Declaration(decl) => visitor.visit(decl), } // New (exhaustive match) match stmt { match_declaration!(Statement) => visitor.visit(stmt.to_declaration()), } // New (alternative) match stmt { _ if stmt.is_declaration() => visitor.visit(stmt.to_declaration()), } ``` New syntax has pluses and minuses vs the old. `match` syntax is worse, but when working with a deeply nested enum, the code is much nicer - it's shorter and easier to read. This PR removes 200 lines from the linter with changes like this: https://github.com/oxc-project/oxc/pull/3115/files#diff-dc417ff57352da6727a760ec6dee22de6816f8231fb69dbef1bf05d478699103L92-R95 ```diff - 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; }; ``` --- crates/oxc_allocator/src/arena.rs | 9 + crates/oxc_ast/src/ast/js.rs | 623 +++++++----- crates/oxc_ast/src/ast/jsx.rs | 20 +- crates/oxc_ast/src/ast/macros.rs | 926 ++++++++++++++++++ crates/oxc_ast/src/ast/mod.rs | 2 + crates/oxc_ast/src/ast/ts.rs | 190 +++- 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, 3246 insertions(+), 1960 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..2a6c0167a0739 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,115 @@ impl<'a> Program<'a> { } } +inherit_variants! { /// Expression +/// +/// 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 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 +292,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 +365,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 +490,22 @@ pub struct ArrayExpression<'a> { pub trailing_comma: Option, } +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>>), - Expression(Expression<'a>), + SpreadElement(Box<'a, SpreadElement<'a>>) = 64, /// Array hole for sparse arrays /// - Elision(Elision), + Elision(Elision) = 65, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } impl<'a> ArrayExpressionElement<'a> { @@ -500,34 +563,35 @@ pub struct ObjectProperty<'a> { pub computed: bool, } +inherit_variants! { +/// Property Key +/// +/// 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 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 +600,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 +624,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 +704,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 +791,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 +864,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 +876,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 +887,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 +902,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 +941,19 @@ pub struct SpreadElement<'a> { pub argument: Expression<'a>, } +inherit_variants! { /// Argument +/// +/// 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 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 +1045,76 @@ pub struct AssignmentExpression<'a> { pub right: Expression<'a>, } +inherit_variants! { /// Destructuring Assignment +/// +/// Inherits variants from [`SimpleAssignmentTarget`] and [`AssignmentTargetPattern`]. +#[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>), -} - -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(_)) - ) - } -} - + // `SimpleAssignmentTarget` variants added here by `inherit_variants!` macro + @inherit SimpleAssignmentTarget + // `AssignmentTargetPattern` variants added here by `inherit_variants!` macro + @inherit AssignmentTargetPattern +} +} + +/// 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! { +/// 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>>), - 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, + 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 +} +} + +/// 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 +1128,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 +1216,33 @@ pub struct AssignmentTargetRest<'a> { pub target: AssignmentTarget<'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> { - AssignmentTarget(AssignmentTarget<'a>), - AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>), + AssignmentTargetWithDefault(Box<'a, AssignmentTargetWithDefault<'a>>) = 16, + // `AssignmentTarget` variants added here by `inherit_variants!` macro + @inherit AssignmentTarget +} } 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 +1324,19 @@ pub struct ChainExpression<'a> { pub expression: ChainElement<'a>, } +inherit_variants! { +/// Chain Element +/// +/// 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 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 +1349,39 @@ pub struct ParenthesizedExpression<'a> { pub expression: Expression<'a>, } -/// Statements +inherit_variants! { +/// Statement +/// +/// Inherits variants from [`Declaration`] and [`ModuleDeclaration`]. +#[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 +1432,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 +1651,20 @@ pub struct ForStatement<'a> { pub body: Statement<'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>>), - Expression(Expression<'a>), - UsingDeclaration(Box<'a, UsingDeclaration<'a>>), + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 64, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 65, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } impl<'a> ForStatementInit<'a> { @@ -1508,13 +1673,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 +1700,20 @@ pub struct ForOfStatement<'a> { pub body: Statement<'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>>), - AssignmentTarget(AssignmentTarget<'a>), - UsingDeclaration(Box<'a, UsingDeclaration<'a>>), + VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 16, + UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 17, + // `AssignmentTarget` variants added here by `inherit_variants!` macro + @inherit AssignmentTarget +} } impl<'a> ForStatementLeft<'a> { @@ -2290,26 +2455,41 @@ 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>>), - /// export * as numbers from '../numbers.js' - ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>), - /// export default 5; - ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>), - /// export {five} from './numbers.js'; - /// export {six, seven}; - ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>), - - /// export = 5; - TSExportAssignment(Box<'a, TSExportAssignment<'a>>), - /// export as namespace React; - TSNamespaceExportDeclaration(Box<'a, TSNamespaceExportDeclaration<'a>>), -} + /// `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, +} + +/// 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 { @@ -2552,27 +2732,34 @@ 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(Expression<'a>), - FunctionDeclaration(Box<'a, Function<'a>>), - ClassDeclaration(Box<'a, Class<'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, - TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>), - TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>), + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} } 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..5938d6a6f9ab6 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,26 @@ pub struct JSXExpressionContainer<'a> { pub expression: JSXExpression<'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> { - Expression(Expression<'a>), - EmptyExpression(JSXEmptyExpression), + EmptyExpression(JSXEmptyExpression) = 64, + // `Expression` variants added here by `inherit_variants!` macro + @inherit Expression +} +} + +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..49c7422c82fac --- /dev/null +++ b/crates/oxc_ast/src/ast/macros.rs @@ -0,0 +1,926 @@ +/// 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 + + /// 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` + @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,)* + + /// 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, + + $($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,)* + + /// Inherited from [`SimpleAssignmentTarget`] + AssignmentTargetIdentifier(Box<'a, IdentifierReference<'a>>) = 0, + + /// 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)* + } + } + + $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,)* + + /// Inherited from [`AssignmentTargetPattern`] + ArrayAssignmentTarget(Box<'a, ArrayAssignmentTarget<'a>>) = 8, + /// Inherited from [`AssignmentTargetPattern`] + 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,)* + + /// 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)* + } + } + + $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,)* + + /// Inherited from [`ModuleDeclaration`]. + /// `import hello from './world.js';` + /// `import * as t from './world.js';` + ImportDeclaration(Box<'a, ImportDeclaration<'a>>) = 64, + /// Inherited from [`ModuleDeclaration`]. + /// `export * as numbers from '../numbers.js'` + ExportAllDeclaration(Box<'a, ExportAllDeclaration<'a>>) = 65, + /// Inherited from [`ModuleDeclaration`]. + /// `export default 5;` + ExportDefaultDeclaration(Box<'a, ExportDefaultDeclaration<'a>>) = 66, + /// Inherited from [`ModuleDeclaration`]. + /// `export {five} from './numbers.js';` + /// `export {six, seven};` + ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>) = 67, + + /// Inherited from [`ModuleDeclaration`]. + /// `export = 5;` + TSExportAssignment(Box<'a, TSExportAssignment<'a>>) = 68, + /// Inherited from [`ModuleDeclaration`]. + /// `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 + /// 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)* + } + } + + $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,)* + + /// Inherited from [`TSTypeName`] + IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0, + /// Inherited from [`TSTypeName`] + 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..ea0b8ccff4c71 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,23 @@ pub struct TSEnumMember<'a> { pub initializer: Option>, } +inherit_variants! { +/// TS Enum Member Name +/// +/// 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 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 +111,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 +343,22 @@ pub struct TSRestType<'a> { pub type_annotation: TSType<'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(TSType<'a>), - TSOptionalType(Box<'a, TSOptionalType<'a>>), - TSRestType(Box<'a, TSRestType<'a>>), + // 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 +} } #[derive(Debug, Hash)] @@ -421,13 +481,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 +852,19 @@ pub struct TSTypeQuery<'a> { pub type_parameters: Option>>, } +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> { - TSTypeName(TSTypeName<'a>), - TSImportType(Box<'a, TSImportType<'a>>), + TSImportType(Box<'a, TSImportType<'a>>) = 2, + // `TSTypeName` variants added here by `inherit_variants!` macro + @inherit TSTypeName +} } #[derive(Debug, Hash)] @@ -929,12 +1006,19 @@ pub struct TSImportEqualsDeclaration<'a> { pub import_kind: ImportOrExportKind, } +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> { - TypeName(TSTypeName<'a>), - ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>), + ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 2, + // `TSTypeName` variants added here by `inherit_variants!` macro + @inherit TSTypeName +} } #[derive(Debug, Hash)] @@ -976,7 +1060,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; } }