From 5af55c3477242821dce6fa2917cc0b290f344f00 Mon Sep 17 00:00:00 2001 From: Boshen Date: Sat, 11 May 2024 17:00:36 +0800 Subject: [PATCH] refactor(linter,diagnostic): one diagnostic struct to eliminate monomorphization of generic types part of #3213 --- crates/oxc_diagnostics/src/lib.rs | 20 +- crates/oxc_linter/src/ast_util.rs | 4 +- crates/oxc_linter/src/context.rs | 10 +- crates/oxc_linter/src/disable_directives.rs | 94 ++-- crates/oxc_linter/src/lib.rs | 6 +- crates/oxc_linter/src/partial_loader/astro.rs | 8 +- crates/oxc_linter/src/partial_loader/vue.rs | 8 +- .../deepscan/bad_array_method_on_arguments.rs | 49 +- .../rules/deepscan/bad_bitwise_operator.rs | 46 +- .../rules/deepscan/bad_char_at_comparison.rs | 29 +- .../rules/deepscan/bad_comparison_sequence.rs | 24 +- .../src/rules/deepscan/bad_min_max_func.rs | 24 +- .../deepscan/bad_object_literal_comparison.rs | 33 +- .../src/rules/deepscan/bad_replace_all_arg.rs | 28 +- .../src/rules/deepscan/missing_throw.rs | 28 +- .../rules/deepscan/number_arg_out_of_range.rs | 40 +- .../deepscan/uninvoked_array_callback.rs | 39 +- .../rules/eslint/array_callback_return/mod.rs | 44 +- .../src/rules/eslint/constructor_super.rs | 4 - .../src/rules/eslint/default_case_last.rs | 25 +- .../src/rules/eslint/default_param_last.rs | 17 +- crates/oxc_linter/src/rules/eslint/eqeqeq.rs | 25 +- .../src/rules/eslint/for_direction.rs | 30 +- .../src/rules/eslint/getter_return.rs | 167 ++---- .../src/rules/eslint/guard_for_in.rs | 17 +- .../oxc_linter/src/rules/eslint/max_lines.rs | 81 ++- .../oxc_linter/src/rules/eslint/max_params.rs | 40 +- .../src/rules/eslint/no_array_constructor.rs | 17 +- .../rules/eslint/no_async_promise_executor.rs | 22 +- .../src/rules/eslint/no_await_in_loop.rs | 16 +- .../oxc_linter/src/rules/eslint/no_bitwise.rs | 24 +- .../oxc_linter/src/rules/eslint/no_caller.rs | 22 +- .../src/rules/eslint/no_case_declarations.rs | 22 +- .../src/rules/eslint/no_class_assign.rs | 29 +- .../src/rules/eslint/no_compare_neg_zero.rs | 22 +- .../src/rules/eslint/no_cond_assign.rs | 19 +- .../oxc_linter/src/rules/eslint/no_console.rs | 16 +- .../src/rules/eslint/no_const_assign.rs | 29 +- .../eslint/no_constant_binary_expression.rs | 63 ++- .../src/rules/eslint/no_constant_condition.rs | 19 +- .../src/rules/eslint/no_continue.rs | 23 +- .../src/rules/eslint/no_control_regex.rs | 23 +- .../src/rules/eslint/no_debugger.rs | 16 +- .../src/rules/eslint/no_delete_var.rs | 20 +- .../src/rules/eslint/no_dupe_class_members.rs | 34 +- .../src/rules/eslint/no_dupe_else_if.rs | 34 +- .../src/rules/eslint/no_dupe_keys.rs | 29 +- .../src/rules/eslint/no_duplicate_case.rs | 127 ++--- .../oxc_linter/src/rules/eslint/no_empty.rs | 23 +- .../rules/eslint/no_empty_character_class.rs | 17 +- .../src/rules/eslint/no_empty_function.rs | 17 +- .../src/rules/eslint/no_empty_pattern.rs | 20 +- .../src/rules/eslint/no_empty_static_block.rs | 17 +- .../oxc_linter/src/rules/eslint/no_eq_null.rs | 20 +- crates/oxc_linter/src/rules/eslint/no_eval.rs | 17 +- .../src/rules/eslint/no_ex_assign.rs | 15 +- .../src/rules/eslint/no_extra_boolean_cast.rs | 38 +- .../src/rules/eslint/no_fallthrough.rs | 34 +- .../src/rules/eslint/no_func_assign.rs | 20 +- .../src/rules/eslint/no_global_assign.rs | 24 +- .../src/rules/eslint/no_import_assign.rs | 28 +- .../src/rules/eslint/no_inner_declarations.rs | 19 +- .../rules/eslint/no_irregular_whitespace.rs | 17 +- .../src/rules/eslint/no_iterator.rs | 21 +- .../src/rules/eslint/no_loss_of_precision.rs | 26 +- .../eslint/no_new_native_nonconstructor.rs | 30 +- .../src/rules/eslint/no_new_wrappers.rs | 32 +- .../eslint/no_nonoctal_decimal_escape.rs | 58 +- .../src/rules/eslint/no_obj_calls.rs | 23 +- .../oxc_linter/src/rules/eslint/no_proto.rs | 21 +- .../src/rules/eslint/no_prototype_builtins.rs | 37 +- .../src/rules/eslint/no_redeclare.rs | 47 +- .../src/rules/eslint/no_regex_spaces.rs | 21 +- .../src/rules/eslint/no_script_url.rs | 17 +- .../src/rules/eslint/no_self_assign.rs | 24 +- .../src/rules/eslint/no_self_compare.rs | 26 +- .../src/rules/eslint/no_setter_return.rs | 94 +--- .../eslint/no_shadow_restricted_names.rs | 58 +- .../src/rules/eslint/no_sparse_arrays.rs | 31 +- .../eslint/no_template_curly_in_string.rs | 22 +- .../oxc_linter/src/rules/eslint/no_ternary.rs | 17 +- .../src/rules/eslint/no_this_before_super.rs | 75 +-- .../oxc_linter/src/rules/eslint/no_undef.rs | 21 +- .../src/rules/eslint/no_unsafe_finally.rs | 125 +---- .../src/rules/eslint/no_unsafe_negation.rs | 22 +- .../eslint/no_unsafe_optional_chaining.rs | 43 +- .../src/rules/eslint/no_unused_labels.rs | 24 +- .../eslint/no_unused_private_class_members.rs | 22 +- .../src/rules/eslint/no_useless_catch.rs | 48 +- .../src/rules/eslint/no_useless_escape.rs | 17 +- .../src/rules/eslint/no_useless_rename.rs | 35 +- crates/oxc_linter/src/rules/eslint/no_var.rs | 17 +- crates/oxc_linter/src/rules/eslint/no_void.rs | 16 +- crates/oxc_linter/src/rules/eslint/no_with.rs | 17 +- crates/oxc_linter/src/rules/eslint/radix.rs | 41 +- .../src/rules/eslint/require_yield.rs | 16 +- .../oxc_linter/src/rules/eslint/use_isnan.rs | 164 ++---- .../src/rules/eslint/valid_typeof.rs | 41 +- crates/oxc_linter/src/rules/import/default.rs | 19 +- crates/oxc_linter/src/rules/import/export.rs | 48 +- crates/oxc_linter/src/rules/import/named.rs | 27 +- .../oxc_linter/src/rules/import/namespace.rs | 91 ++-- crates/oxc_linter/src/rules/import/no_amd.rs | 24 +- .../oxc_linter/src/rules/import/no_cycle.rs | 16 +- .../src/rules/import/no_default_export.rs | 16 +- .../src/rules/import/no_deprecated.rs | 5 +- .../src/rules/import/no_duplicates.rs | 8 +- .../src/rules/import/no_named_as_default.rs | 21 +- .../import/no_named_as_default_member.rs | 40 +- .../src/rules/import/no_self_import.rs | 18 +- .../src/rules/import/no_unused_modules.rs | 14 +- .../src/rules/jest/expect_expect.rs | 82 +-- .../oxc_linter/src/rules/jest/max_expects.rs | 22 +- .../src/rules/jest/no_alias_methods.rs | 17 +- .../src/rules/jest/no_commented_out_tests.rs | 19 +- .../src/rules/jest/no_conditional_expect.rs | 19 +- .../rules/jest/no_confusing_set_timeout.rs | 53 +- .../src/rules/jest/no_deprecated_functions.rs | 19 +- .../src/rules/jest/no_disabled_tests.rs | 43 +- .../src/rules/jest/no_done_callback.rs | 37 +- crates/oxc_linter/src/rules/jest/no_export.rs | 19 +- .../src/rules/jest/no_focused_tests.rs | 23 +- crates/oxc_linter/src/rules/jest/no_hooks.rs | 16 +- .../src/rules/jest/no_identical_title.rs | 24 +- .../jest/no_interpolation_in_snapshots.rs | 21 +- .../src/rules/jest/no_jasmine_globals.rs | 40 +- .../src/rules/jest/no_mocks_import.rs | 22 +- .../rules/jest/no_restricted_jest_methods.rs | 43 +- .../src/rules/jest/no_restricted_matchers.rs | 37 +- .../src/rules/jest/no_standalone_expect.rs | 52 +- .../src/rules/jest/no_test_prefixes.rs | 35 +- .../rules/jest/no_test_return_statement.rs | 17 +- .../src/rules/jest/no_untyped_mock_factory.rs | 27 +- .../src/rules/jest/prefer_called_with.rs | 31 +- .../rules/jest/prefer_comparison_matcher.rs | 19 +- .../src/rules/jest/prefer_equality_matcher.rs | 18 +- .../src/rules/jest/prefer_expect_resolves.rs | 17 +- .../src/rules/jest/prefer_lowercase_title.rs | 23 +- .../jest/prefer_mock_promise_shorthand.rs | 22 +- .../src/rules/jest/prefer_spy_on.rs | 58 +- .../src/rules/jest/prefer_strict_equal.rs | 18 +- .../oxc_linter/src/rules/jest/prefer_to_be.rs | 58 +- .../src/rules/jest/prefer_to_contain.rs | 21 +- .../src/rules/jest/prefer_to_have_length.rs | 18 +- .../oxc_linter/src/rules/jest/prefer_todo.rs | 28 +- .../oxc_linter/src/rules/jest/require_hook.rs | 21 +- .../rules/jest/require_to_throw_message.rs | 19 +- .../src/rules/jest/valid_describe_callback.rs | 21 +- .../oxc_linter/src/rules/jest/valid_expect.rs | 176 +++--- .../oxc_linter/src/rules/jest/valid_title.rs | 39 +- .../src/rules/jsdoc/check_access.rs | 36 +- .../src/rules/jsdoc/check_property_names.rs | 49 +- .../src/rules/jsdoc/check_tag_names.rs | 335 +++++++----- .../oxc_linter/src/rules/jsdoc/empty_tags.rs | 48 +- .../src/rules/jsdoc/implements_on_classes.rs | 19 +- .../oxc_linter/src/rules/jsdoc/no_defaults.rs | 43 +- .../src/rules/jsdoc/require_property.rs | 23 +- .../jsdoc/require_property_description.rs | 19 +- .../src/rules/jsdoc/require_property_name.rs | 19 +- .../src/rules/jsdoc/require_property_type.rs | 19 +- .../src/rules/jsdoc/require_yields.rs | 45 +- .../oxc_linter/src/rules/jsx_a11y/alt_text.rs | 126 +++-- .../src/rules/jsx_a11y/anchor_has_content.rs | 33 +- .../src/rules/jsx_a11y/anchor_is_valid.rs | 58 +- .../aria_activedescendant_has_tabindex.rs | 20 +- .../src/rules/jsx_a11y/aria_props.rs | 17 +- .../src/rules/jsx_a11y/aria_role.rs | 30 +- .../jsx_a11y/aria_unsupported_elements.rs | 17 +- .../src/rules/jsx_a11y/autocomplete_valid.rs | 33 +- .../jsx_a11y/click_events_have_key_events.rs | 17 +- .../src/rules/jsx_a11y/heading_has_content.rs | 20 +- .../src/rules/jsx_a11y/html_has_lang.rs | 28 +- .../src/rules/jsx_a11y/iframe_has_title.rs | 25 +- .../src/rules/jsx_a11y/img_redundant_alt.rs | 23 +- crates/oxc_linter/src/rules/jsx_a11y/lang.rs | 19 +- .../src/rules/jsx_a11y/media_has_caption.rs | 26 +- .../jsx_a11y/mouse_events_have_key_events.rs | 42 +- .../src/rules/jsx_a11y/no_access_key.rs | 23 +- .../jsx_a11y/no_aria_hidden_on_focusable.rs | 21 +- .../src/rules/jsx_a11y/no_autofocus.rs | 19 +- .../rules/jsx_a11y/no_distracting_elements.rs | 26 +- .../src/rules/jsx_a11y/no_redundant_roles.rs | 33 +- .../rules/jsx_a11y/prefer_tag_over_role.rs | 30 +- .../jsx_a11y/role_has_required_aria_props.rs | 43 +- .../jsx_a11y/role_supports_aria_props.rs | 36 +- crates/oxc_linter/src/rules/jsx_a11y/scope.rs | 19 +- .../rules/jsx_a11y/tabindex_no_positive.rs | 25 +- .../src/rules/nextjs/google_font_display.rs | 72 ++- .../rules/nextjs/google_font_preconnect.rs | 28 +- .../src/rules/nextjs/inline_script_id.rs | 25 +- .../src/rules/nextjs/next_script_for_ga.rs | 27 +- .../rules/nextjs/no_assign_module_variable.rs | 26 +- .../rules/nextjs/no_async_client_component.rs | 28 +- ...ore_interactive_script_outside_document.rs | 24 +- .../src/rules/nextjs/no_css_tags.rs | 37 +- .../nextjs/no_document_import_in_page.rs | 22 +- .../src/rules/nextjs/no_duplicate_head.rs | 13 +- .../src/rules/nextjs/no_head_element.rs | 25 +- .../nextjs/no_head_import_in_document.rs | 22 +- .../src/rules/nextjs/no_img_element.rs | 29 +- .../src/rules/nextjs/no_page_custom_font.rs | 41 +- .../nextjs/no_script_component_in_head.rs | 32 +- .../rules/nextjs/no_styled_jsx_in_document.rs | 25 +- .../src/rules/nextjs/no_sync_scripts.rs | 54 +- .../rules/nextjs/no_title_in_document_head.rs | 32 +- .../oxc_linter/src/rules/nextjs/no_typos.rs | 41 +- .../rules/nextjs/no_unwanted_polyfillio.rs | 22 +- .../src/rules/oxc/approx_constant.rs | 17 +- .../src/rules/oxc/const_comparisons.rs | 87 ++- .../src/rules/oxc/double_comparisons.rs | 22 +- crates/oxc_linter/src/rules/oxc/erasing_op.rs | 23 +- .../src/rules/oxc/misrefactored_assign_op.rs | 23 +- .../src/rules/oxc/no_accumulating_spread.rs | 60 ++- .../src/rules/oxc/no_barrel_file.rs | 23 +- .../src/rules/oxc/only_used_in_recursion.rs | 38 +- .../src/rules/react/button_has_type.rs | 40 +- .../checked_requires_onchange_or_readonly.rs | 58 +- crates/oxc_linter/src/rules/react/jsx_key.rs | 59 +- .../rules/react/jsx_no_comment_textnodes.rs | 19 +- .../src/rules/react/jsx_no_duplicate_props.rs | 38 +- .../src/rules/react/jsx_no_target_blank.rs | 41 +- .../src/rules/react/jsx_no_undef.rs | 21 +- .../rules/react/jsx_no_useless_fragment.rs | 43 +- .../src/rules/react/no_children_prop.rs | 28 +- .../oxc_linter/src/rules/react/no_danger.rs | 27 +- .../rules/react/no_direct_mutation_state.rs | 24 +- .../src/rules/react/no_find_dom_node.rs | 31 +- .../src/rules/react/no_is_mounted.rs | 17 +- .../src/rules/react/no_render_return_value.rs | 31 +- .../src/rules/react/no_string_refs.rs | 28 +- .../src/rules/react/no_unescaped_entities.rs | 20 +- .../src/rules/react/no_unknown_property.rs | 61 +-- .../src/rules/react/react_in_jsx_scope.rs | 17 +- .../src/rules/react/require_render_return.rs | 21 +- .../react/void_dom_elements_no_children.rs | 23 +- .../rules/react_perf/jsx_no_jsx_as_prop.rs | 19 +- .../react_perf/jsx_no_new_array_as_prop.rs | 17 +- .../react_perf/jsx_no_new_function_as_prop.rs | 17 +- .../react_perf/jsx_no_new_object_as_prop.rs | 17 +- .../listener_map.rs | 73 +-- .../no_side_effects_in_initialization/mod.rs | 110 ++-- .../adjacent_overload_signatures.rs | 35 +- .../src/rules/typescript/array_type.rs | 96 ++-- .../src/rules/typescript/ban_ts_comment.rs | 508 +++++++++++------- .../rules/typescript/ban_tslint_comment.rs | 18 +- .../src/rules/typescript/ban_types.rs | 56 +- .../typescript/consistent_type_definitions.rs | 28 +- .../typescript/no_duplicate_enum_values.rs | 32 +- .../rules/typescript/no_empty_interface.rs | 29 +- .../src/rules/typescript/no_explicit_any.rs | 27 +- .../typescript/no_extra_non_null_assertion.rs | 18 +- .../src/rules/typescript/no_misused_new.rs | 42 +- .../src/rules/typescript/no_namespace.rs | 34 +- .../no_non_null_asserted_optional_chain.rs | 24 +- .../src/rules/typescript/no_this_alias.rs | 46 +- .../no_unnecessary_type_constraint.rs | 33 +- .../no_unsafe_declaration_merging.rs | 17 +- .../src/rules/typescript/no_var_requires.rs | 23 +- .../src/rules/typescript/prefer_as_const.rs | 18 +- .../typescript/prefer_enum_initializers.rs | 25 +- .../src/rules/typescript/prefer_for_of.rs | 45 +- .../rules/typescript/prefer_function_type.rs | 99 ++-- .../typescript/prefer_literal_enum_member.rs | 25 +- .../typescript/prefer_ts_expect_error.rs | 24 +- .../typescript/triple_slash_reference.rs | 31 +- .../src/rules/unicorn/catch_error_name.rs | 51 +- .../src/rules/unicorn/empty_brace_spaces.rs | 24 +- .../src/rules/unicorn/error_message.rs | 48 +- .../src/rules/unicorn/escape_case.rs | 21 +- .../rules/unicorn/explicit_length_check.rs | 87 ++- .../src/rules/unicorn/filename_case.rs | 22 +- .../src/rules/unicorn/new_for_builtins.rs | 45 +- .../unicorn/no_abusive_eslint_disable.rs | 19 +- .../src/rules/unicorn/no_array_for_each.rs | 17 +- .../src/rules/unicorn/no_array_reduce.rs | 19 +- .../unicorn/no_await_expression_member.rs | 21 +- .../unicorn/no_await_in_promise_methods.rs | 19 +- .../src/rules/unicorn/no_console_spaces.rs | 23 +- .../src/rules/unicorn/no_document_cookie.rs | 23 +- .../src/rules/unicorn/no_empty_file.rs | 21 +- .../src/rules/unicorn/no_hex_escape.rs | 24 +- .../src/rules/unicorn/no_instanceof_array.rs | 21 +- .../no_invalid_remove_event_listener.rs | 34 +- .../src/rules/unicorn/no_lonely_if.rs | 21 +- .../src/rules/unicorn/no_negated_condition.rs | 26 +- .../src/rules/unicorn/no_nested_ternary.rs | 51 +- .../src/rules/unicorn/no_new_array.rs | 23 +- .../src/rules/unicorn/no_new_buffer.rs | 24 +- .../oxc_linter/src/rules/unicorn/no_null.rs | 36 +- .../unicorn/no_object_as_default_parameter.rs | 34 +- .../src/rules/unicorn/no_process_exit.rs | 24 +- .../no_single_promise_in_promise_methods.rs | 20 +- .../src/rules/unicorn/no_static_only_class.rs | 26 +- .../src/rules/unicorn/no_thenable.rs | 56 +- .../src/rules/unicorn/no_this_assignment.rs | 30 +- .../src/rules/unicorn/no_typeof_undefined.rs | 27 +- .../src/rules/unicorn/no_unnecessary_await.rs | 24 +- .../no_unreadable_array_destructuring.rs | 22 +- .../src/rules/unicorn/no_unreadable_iife.rs | 24 +- .../unicorn/no_useless_fallback_in_spread.rs | 33 +- .../rules/unicorn/no_useless_length_check.rs | 40 +- .../no_useless_promise_resolve_reject.rs | 53 +- .../src/rules/unicorn/no_useless_spread.rs | 93 ++-- .../rules/unicorn/no_useless_switch_case.rs | 22 +- .../src/rules/unicorn/no_zero_fractions.rs | 35 +- .../src/rules/unicorn/number_literal_case.rs | 78 ++- .../rules/unicorn/numeric_separators_style.rs | 34 +- .../unicorn/prefer_add_event_listener.rs | 23 +- .../src/rules/unicorn/prefer_array_flat.rs | 25 +- .../rules/unicorn/prefer_array_flat_map.rs | 25 +- .../src/rules/unicorn/prefer_array_some.rs | 35 +- .../unicorn/prefer_blob_reading_methods.rs | 23 +- .../src/rules/unicorn/prefer_code_point.rs | 27 +- .../src/rules/unicorn/prefer_date_now.rs | 73 ++- .../rules/unicorn/prefer_dom_node_append.rs | 21 +- .../rules/unicorn/prefer_dom_node_dataset.rs | 72 ++- .../rules/unicorn/prefer_dom_node_remove.rs | 26 +- .../unicorn/prefer_dom_node_text_content.rs | 23 +- .../src/rules/unicorn/prefer_event_target.rs | 21 +- .../src/rules/unicorn/prefer_includes.rs | 17 +- .../prefer_logical_operator_over_ternary.rs | 25 +- .../src/rules/unicorn/prefer_math_trunc.rs | 18 +- .../rules/unicorn/prefer_modern_dom_apis.rs | 26 +- .../rules/unicorn/prefer_modern_math_apis.rs | 57 +- .../prefer_native_coercion_functions.rs | 70 +-- .../src/rules/unicorn/prefer_node_protocol.rs | 17 +- .../rules/unicorn/prefer_number_properties.rs | 37 +- .../unicorn/prefer_optional_catch_binding.rs | 19 +- .../rules/unicorn/prefer_prototype_methods.rs | 50 +- .../rules/unicorn/prefer_query_selector.rs | 34 +- .../src/rules/unicorn/prefer_reflect_apply.rs | 23 +- .../src/rules/unicorn/prefer_regexp_test.rs | 40 +- .../src/rules/unicorn/prefer_set_size.rs | 15 +- .../src/rules/unicorn/prefer_spread.rs | 47 +- .../unicorn/prefer_string_replace_all.rs | 41 +- .../src/rules/unicorn/prefer_string_slice.rs | 24 +- .../unicorn/prefer_string_starts_ends_with.rs | 33 +- .../unicorn/prefer_string_trim_start_end.rs | 27 +- .../src/rules/unicorn/prefer_type_error.rs | 37 +- .../unicorn/require_array_join_separator.rs | 19 +- ...require_number_to_fixed_digits_argument.rs | 17 +- .../src/rules/unicorn/switch_case_braces.rs | 78 +-- .../unicorn/text_encoding_identifier_case.rs | 32 +- .../src/rules/unicorn/throw_new_error.rs | 24 +- crates/oxc_linter/src/service.rs | 4 +- .../src/snapshots/array_callback_return.snap | 80 +-- .../oxc_linter/src/snapshots/max_lines.snap | 46 +- .../oxc_linter/src/snapshots/namespace.snap | 3 +- .../src/snapshots/no_setter_return.snap | 84 +-- .../no_useless_promise_resolve_reject.snap | 2 +- crates/oxc_linter/src/tester.rs | 3 +- .../src/utils/jest/parse_jest_fn.rs | 4 +- crates/oxc_linter/src/utils/nextjs.rs | 4 +- crates/oxc_linter/src/utils/react.rs | 16 +- .../oxc_linter/src/utils/unicorn/boolean.rs | 8 +- 355 files changed, 5930 insertions(+), 6437 deletions(-) diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index aa86aeaaa8b1..b5fc721e6c59 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -27,8 +27,8 @@ pub type Report = miette::Report; pub type Result = std::result::Result; -pub use miette::LabeledSpan; use miette::{Diagnostic, SourceCode}; +pub use miette::{LabeledSpan, NamedSource}; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] @@ -60,6 +60,7 @@ pub struct OxcDiagnosticInner { pub message: String, pub labels: Option>, pub help: Option, + pub severity: Severity, } impl fmt::Display for OxcDiagnostic { @@ -75,6 +76,10 @@ impl Diagnostic for OxcDiagnostic { self.help.as_ref().map(Box::new).map(|c| c as Box) } + fn severity(&self) -> Option { + Some(self.severity) + } + fn labels(&self) -> Option + '_>> { self.labels .as_ref() @@ -92,6 +97,19 @@ impl OxcDiagnostic { message: message.into(), labels: None, help: None, + severity: Severity::Error, + }), + } + } + + #[must_use] + pub fn warning>(message: T) -> Self { + Self { + inner: Box::new(OxcDiagnosticInner { + message: message.into(), + labels: None, + help: None, + severity: Severity::Warning, }), } } diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index e3cffb73ebe7..f237bd5dd8df 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -318,7 +318,9 @@ pub fn is_method_call<'a>( } if let Some(methods) = methods { - let Some(static_property_name) = member_expr.static_property_name() else { return false }; + let Some(static_property_name) = member_expr.static_property_name() else { + return false; + }; if !methods.contains(&static_property_name) { return false; } diff --git a/crates/oxc_linter/src/context.rs b/crates/oxc_linter/src/context.rs index 9caded75006c..747982263fbe 100644 --- a/crates/oxc_linter/src/context.rs +++ b/crates/oxc_linter/src/context.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc}; use oxc_codegen::{Codegen, CodegenOptions}; -use oxc_diagnostics::Error; +use oxc_diagnostics::OxcDiagnostic; use oxc_semantic::{AstNodes, JSDocFinder, ScopeTree, Semantic, SymbolTable}; use oxc_span::SourceType; @@ -116,15 +116,11 @@ impl<'a> LintContext<'a> { } } - pub fn diagnostic>(&self, diagnostic: T) { + pub fn diagnostic(&self, diagnostic: OxcDiagnostic) { self.add_diagnostic(Message::new(diagnostic.into(), None)); } - pub fn diagnostic_with_fix(&self, diagnostic: T, fix: F) - where - T: Into, - F: FnOnce() -> Fix<'a>, - { + pub fn diagnostic_with_fix Fix<'a>>(&self, diagnostic: OxcDiagnostic, fix: F) { if self.fix { self.add_diagnostic(Message::new(diagnostic.into(), Some(fix()))); } else { diff --git a/crates/oxc_linter/src/disable_directives.rs b/crates/oxc_linter/src/disable_directives.rs index d461e7c4c9a1..5b87462f22a3 100644 --- a/crates/oxc_linter/src/disable_directives.rs +++ b/crates/oxc_linter/src/disable_directives.rs @@ -214,36 +214,45 @@ fn test() { // [Disabling Rules](https://eslint.org/docs/latest/use/configure/rules#disabling-rules) // Using configuration comments let pass = vec![ - // To disable rule warnings in a part of a file, use block comments in the following format: - format!(" + // To disable rule warnings in a part of a file, use block comments in the following format: + format!( + " /* {prefix}-disable */ debugger; /* {prefix}-enable */ - "), - // You can also disable or enable warnings for specific rules: - format!(" + " + ), + // You can also disable or enable warnings for specific rules: + format!( + " /* {prefix}-disable no-debugger, no-console */ debugger; /* {prefix}-enable no-debugger, no-console */ - "), - // To disable rule warnings in an entire file, put a /* eslint-disable */ block comment at the top of the file: - format!(" + " + ), + // To disable rule warnings in an entire file, put a /* eslint-disable */ block comment at the top of the file: + format!( + " /* {prefix}-disable */ debugger; - "), - // You can also disable or enable specific rules for an entire file: - format!(" + " + ), + // You can also disable or enable specific rules for an entire file: + format!( + " /* {prefix}-disable no-debugger */ debugger; - "), - // To ensure that a rule is never applied (regardless of any future enable/disable lines): - // This is not supported. - // " - // /* eslint no-debugger: \"off\" */ - // debugger; - // "), - // To disable all rules on a specific line, use a line or block comment in one of the following formats: - format!("debugger; // {prefix}-disable-line + " + ), + // To ensure that a rule is never applied (regardless of any future enable/disable lines): + // This is not supported. + // " + // /* eslint no-debugger: \"off\" */ + // debugger; + // "), + // To disable all rules on a specific line, use a line or block comment in one of the following formats: + format!( + "debugger; // {prefix}-disable-line debugger; // {prefix}-disable-line // {prefix}-disable-next-line @@ -253,9 +262,11 @@ fn test() { debugger; debugger; /* {prefix}-disable-line */ - "), - // To disable a specific rule on a specific line: - format!(" + " + ), + // To disable a specific rule on a specific line: + format!( + " debugger; // {prefix}-disable-line no-debugger // {prefix}-disable-next-line no-debugger @@ -265,9 +276,11 @@ fn test() { /* {prefix}-disable-next-line no-debugger */ debugger; - "), - // To disable multiple rules on a specific line: - format!(" + " + ), + // To disable multiple rules on a specific line: + format!( + " debugger; // {prefix}-disable-line no-debugger, quotes, semi // {prefix}-disable-next-line no-debugger, quotes, semi @@ -284,23 +297,29 @@ fn test() { semi */ debugger; - "), - // To disable all rules twice: - format!(" + " + ), + // To disable all rules twice: + format!( + " /* {prefix}-disable */ debugger; /* {prefix}-disable */ debugger; - "), - // To disable a rule twice: - format!(" + " + ), + // To disable a rule twice: + format!( + " /* {prefix}-disable no-debugger */ debugger; /* {prefix}-disable no-debugger */ debugger; - "), - // Comment descriptions - format!(" + " + ), + // Comment descriptions + format!( + " // {prefix}-disable-next-line no-debugger -- Here's a description about why this configuration is necessary. debugger; @@ -309,8 +328,9 @@ fn test() { * along with some additional information **/ debugger; - ") - ]; + " + ), + ]; let fail = vec![ "debugger".to_string(), diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index f22fa1e52b73..0e2a5be02d3f 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -163,11 +163,7 @@ impl Linter { } else { ("", 7) }; - writeln!( - writer, - "| {rule_name: AstroPartialLoader<'a> { let start = offsets.first()?; let end = offsets.last()?; - let Ok(start) = u32::try_from(*start) else { return None }; - let Ok(end) = u32::try_from(*end) else { return None }; + let Ok(start) = u32::try_from(*start) else { + return None; + }; + let Ok(end) = u32::try_from(*end) else { + return None; + }; let js_code = Span::new(start + ASTRO_SPLIT.len() as u32, end).source_text(self.source_text); diff --git a/crates/oxc_linter/src/partial_loader/vue.rs b/crates/oxc_linter/src/partial_loader/vue.rs index 4132f0b8a241..30492de81637 100644 --- a/crates/oxc_linter/src/partial_loader/vue.rs +++ b/crates/oxc_linter/src/partial_loader/vue.rs @@ -23,8 +23,12 @@ impl<'a> VuePartialLoader<'a> { /// fn parse_scripts(&self) -> Vec> { let mut pointer = 0; - let Some(result1) = self.parse_script(&mut pointer) else { return vec![] }; - let Some(result2) = self.parse_script(&mut pointer) else { return vec![result1] }; + let Some(result1) = self.parse_script(&mut pointer) else { + return vec![]; + }; + let Some(result2) = self.parse_script(&mut pointer) else { + return vec![result1]; + }; vec![result1, result2] } diff --git a/crates/oxc_linter/src/rules/deepscan/bad_array_method_on_arguments.rs b/crates/oxc_linter/src/rules/deepscan/bad_array_method_on_arguments.rs index 65650f419dfe..66144fefca6f 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_array_method_on_arguments.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_array_method_on_arguments.rs @@ -2,24 +2,20 @@ use oxc_ast::{ ast::{Expression, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-array-method-on-arguments): Bad array method on arguments")] -#[diagnostic( - severity(warning), - help( - "The 'arguments' object does not have '{0}()' method. If an array method was intended, consider converting the 'arguments' object to an array or using ES6 rest parameter instead." - ) -)] -struct BadArrayMethodOnArgumentsDiagnostic(CompactStr, #[label] pub Span); +fn bad_array_method_on_arguments_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-array-method-on-arguments): Bad array method on arguments") + .with_help(format!( + "The 'arguments' object does not have '{x0}()' method. If an array method was intended, consider converting the 'arguments' object to an array or using ES6 rest parameter instead." + )) + .with_labels([span1.into()]) +} /// `https://deepscan.io/docs/rules/bad-array-method-on-arguments` #[derive(Debug, Default, Clone)] @@ -51,17 +47,23 @@ impl Rule for BadArrayMethodOnArguments { if !node.kind().is_specific_id_reference("arguments") { return; } - let Some(parent_node_id) = ctx.nodes().parent_id(node.id()) else { return }; + let Some(parent_node_id) = ctx.nodes().parent_id(node.id()) else { + return; + }; let AstKind::MemberExpression(member_expr) = ctx.nodes().kind(parent_node_id) else { return; }; - let Some(parent_node_id) = ctx.nodes().parent_id(parent_node_id) else { return }; - let AstKind::CallExpression(_) = ctx.nodes().kind(parent_node_id) else { return }; + let Some(parent_node_id) = ctx.nodes().parent_id(parent_node_id) else { + return; + }; + let AstKind::CallExpression(_) = ctx.nodes().kind(parent_node_id) else { + return; + }; match member_expr { MemberExpression::StaticMemberExpression(expr) => { if ARRAY_METHODS.binary_search(&expr.property.name.as_str()).is_ok() { - ctx.diagnostic(BadArrayMethodOnArgumentsDiagnostic( - expr.property.name.to_compact_str(), + ctx.diagnostic(bad_array_method_on_arguments_diagnostic( + expr.property.name.as_str(), expr.span, )); } @@ -70,8 +72,8 @@ impl Rule for BadArrayMethodOnArguments { match &expr.expression { Expression::StringLiteral(name) => { if ARRAY_METHODS.binary_search(&name.value.as_str()).is_ok() { - ctx.diagnostic(BadArrayMethodOnArgumentsDiagnostic( - name.value.to_compact_str(), + ctx.diagnostic(bad_array_method_on_arguments_diagnostic( + name.value.as_str(), expr.span, )); } @@ -85,9 +87,8 @@ impl Rule for BadArrayMethodOnArguments { }) { if ARRAY_METHODS.binary_search(&name).is_ok() { - ctx.diagnostic(BadArrayMethodOnArgumentsDiagnostic( - CompactStr::from(name), - expr.span, + ctx.diagnostic(bad_array_method_on_arguments_diagnostic( + name, expr.span, )); } } 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 1d622a1c838c..ab68f155e98c 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_bitwise_operator.rs @@ -2,33 +2,27 @@ use oxc_ast::{ ast::{BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, UnaryOperator}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-bitwise-operator): Bad bitwise operator")] -#[diagnostic( - severity(warning), - help("Bitwise operator '{0}' seems unintended. Did you mean logical operator '{1}'?") -)] -struct BadBitwiseOperatorDiagnostic(&'static str, &'static str, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Bad bitwise operator")] -#[diagnostic( - severity(warning), - help( - "Bitwise operator '|=' seems unintended. Consider using non-compound assignment and logical operator '||' instead." - ) -)] -struct BadBitwiseOrOperatorDiagnostic(#[label] pub Span); +fn bad_bitwise_operator_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-bitwise-operator): Bad bitwise operator") + .with_help(format!( + "Bitwise operator '{x0}' seems unintended. Did you mean logical operator '{x1}'?" + )) + .with_labels([span2.into()]) +} + +fn bad_bitwise_or_operator_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("Bad bitwise operator") + .with_help("Bitwise operator '|=' seems unintended. Consider using non-compound assignment and logical operator '||' instead.") + .with_labels([span0.into()]) +} /// `https://deepscan.io/docs/rules/bad-bitwise-operator` #[derive(Debug, Default, Clone)] @@ -67,16 +61,16 @@ impl Rule for BadBitwiseOperator { match node.kind() { AstKind::BinaryExpression(bin_expr) => { if is_mistype_short_circuit(node) { - ctx.diagnostic(BadBitwiseOperatorDiagnostic("&", "&&", bin_expr.span)); + ctx.diagnostic(bad_bitwise_operator_diagnostic("&", "&&", bin_expr.span)); } else if is_mistype_option_fallback(node) { - ctx.diagnostic(BadBitwiseOperatorDiagnostic("|", "||", bin_expr.span)); + ctx.diagnostic(bad_bitwise_operator_diagnostic("|", "||", bin_expr.span)); } } AstKind::AssignmentExpression(assign_expr) => { if assign_expr.operator == AssignmentOperator::BitwiseOR && !is_numeric_expr(&assign_expr.right, true) { - ctx.diagnostic(BadBitwiseOrOperatorDiagnostic(assign_expr.span)); + ctx.diagnostic(bad_bitwise_or_operator_diagnostic(assign_expr.span)); } } _ => {} @@ -91,7 +85,9 @@ fn is_mistype_short_circuit(node: &AstNode) -> bool { return false; } - let Expression::Identifier(left_ident) = &bin_expr.left else { return false }; + let Expression::Identifier(left_ident) = &bin_expr.left else { + return false; + }; if let Some(member_expr) = bin_expr.right.as_member_expression() { if let Expression::Identifier(ident) = member_expr.object() { diff --git a/crates/oxc_linter/src/rules/deepscan/bad_char_at_comparison.rs b/crates/oxc_linter/src/rules/deepscan/bad_char_at_comparison.rs index 6eb1366339f4..15dc332ebb65 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_char_at_comparison.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_char_at_comparison.rs @@ -1,22 +1,17 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::BinaryOperator; use crate::{ast_util::is_method_call, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-char-at-comparison): Invalid comparison with `charAt` method")] -#[diagnostic(severity(warning), help("`String.prototype.charAt` returns a string of length 1. If the return value is compared with a string of length greater than 1, the comparison will always be false."))] -struct BadCharAtComparisonDiagnostic( - #[label("`charAt` called here")] pub Span, - #[label("And compared with a string of length {2} here")] pub Span, - usize, -); +fn bad_char_at_comparison_diagnostic(span0: Span, span1: Span, x2: usize) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-char-at-comparison): Invalid comparison with `charAt` method") + .with_help("`String.prototype.charAt` returns a string of length 1. If the return value is compared with a string of length greater than 1, the comparison will always be false.") + .with_labels([LabeledSpan::new_with_span(Some("`charAt` called here".into()), span0), LabeledSpan::new_with_span(Some(format!("And compared with a string of length {x2} here")), span1)]) +} #[derive(Debug, Default, Clone)] pub struct BadCharAtComparison; @@ -46,7 +41,9 @@ declare_oxc_lint!( impl Rule for BadCharAtComparison { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; if !is_method_call(call_expr, None, Some(&["charAt"]), Some(1), Some(1)) { return; @@ -56,7 +53,9 @@ impl Rule for BadCharAtComparison { return; }; - let AstKind::BinaryExpression(binary_expr) = parent.kind() else { return }; + let AstKind::BinaryExpression(binary_expr) = parent.kind() else { + return; + }; if !matches!( binary_expr.operator, BinaryOperator::Equality @@ -75,7 +74,7 @@ impl Rule for BadCharAtComparison { if let Expression::StringLiteral(string_lit) = comparison_with { if !is_string_valid(string_lit.value.as_str()) { - ctx.diagnostic(BadCharAtComparisonDiagnostic( + ctx.diagnostic(bad_char_at_comparison_diagnostic( call_expr.span, string_lit.span, string_lit.value.len(), diff --git a/crates/oxc_linter/src/rules/deepscan/bad_comparison_sequence.rs b/crates/oxc_linter/src/rules/deepscan/bad_comparison_sequence.rs index 3620944fab4b..51d3a515a669 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_comparison_sequence.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_comparison_sequence.rs @@ -2,24 +2,16 @@ use oxc_ast::{ ast::{BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-comparison-sequence): Bad comparison sequence")] -#[diagnostic( - severity(warning), - help( - "Comparison result should not be used directly as an operand of another comparison. If you need to compare three or more operands, you should connect each comparison operation with logical AND operator (`&&`)" - ) -)] -struct BadComparisonSequenceDiagnostic(#[label] pub Span); +fn bad_comparison_sequence_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-comparison-sequence): Bad comparison sequence").with_help("Comparison result should not be used directly as an operand of another comparison. If you need to compare three or more operands, you should connect each comparison operation with logical AND operator (`&&`)").with_labels([span0.into()]) +} /// `https://deepscan.io/docs/rules/bad-comparison-sequence` #[derive(Debug, Default, Clone)] @@ -45,9 +37,11 @@ declare_oxc_lint!( impl Rule for BadComparisonSequence { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::BinaryExpression(expr) = node.kind() else { return }; + let AstKind::BinaryExpression(expr) = node.kind() else { + return; + }; if is_bad_comparison(expr) && has_no_bad_comparison_in_parents(node, ctx) { - ctx.diagnostic(BadComparisonSequenceDiagnostic(expr.span)); + ctx.diagnostic(bad_comparison_sequence_diagnostic(expr.span)); } } } 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 f9c2f022b86d..fe8d1ac7d940 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 @@ -2,22 +2,22 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-min-max-func): Math.min and Math.max combination leads to constant result")] -#[diagnostic( - severity(warning), - help("This evaluates to {0:?} because of the incorrect `Math.min`/`Math.max` combination") -)] -struct BadMinMaxFuncDiagnostic(f64, #[label] pub Span); +fn bad_min_max_func_diagnostic(x0: f64, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "deepscan(bad-min-max-func): Math.min and Math.max combination leads to constant result", + ) + .with_help(format!( + "This evaluates to {x0:?} because of the incorrect `Math.min`/`Math.max` combination" + )) + .with_labels([span1.into()]) +} /// `https://deepscan.io/docs/rules/bad-min-max-func` #[derive(Debug, Default, Clone)] @@ -66,7 +66,7 @@ impl Rule for BadMinMaxFunc { }; if let Some(constant) = constant_result { - ctx.diagnostic(BadMinMaxFuncDiagnostic(*constant, call_expr.span)); + ctx.diagnostic(bad_min_max_func_diagnostic(*constant, call_expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/deepscan/bad_object_literal_comparison.rs b/crates/oxc_linter/src/rules/deepscan/bad_object_literal_comparison.rs index a8a87ce1f34c..1e0aee81c1d4 100644 --- a/crates/oxc_linter/src/rules/deepscan/bad_object_literal_comparison.rs +++ b/crates/oxc_linter/src/rules/deepscan/bad_object_literal_comparison.rs @@ -1,22 +1,23 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::BinaryOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum BadObjectLiteralComparisonDiagnostic { - #[error("deepscan(bad-object-literal-comparison): Unexpected object literal comparison.")] - #[diagnostic(severity(warning), help("This comparison will always return {1:?} as object literals are never equal to each other. Consider using `Object.entries()` of `Object.keys()` and comparing their lengths."))] - ObjectComparison(#[label] Span, bool), - #[error("deepscan(bad-object-literal-comparison): Unexpected array literal comparison.")] - #[diagnostic(severity(warning), help("This comparison will always return {1:?} as array literals are never equal to each other. Consider using `Array.length` if empty checking was intended."))] - ArrayComparison(#[label] Span, bool), +fn object_comparison(span0: Span, x1: bool) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-object-literal-comparison): Unexpected object literal comparison.") + .with_help(format!( + "This comparison will always return {x1:?} as object literals are never equal to each other. Consider using `Object.entries()` of `Object.keys()` and comparing their lengths." + )) + .with_labels([span0.into()]) +} + +fn array_comparison(span0: Span, x1: bool) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-object-literal-comparison): Unexpected array literal comparison.") + .with_help(format!("This comparison will always return {x1:?} as array literals are never equal to each other. Consider using `Array.length` if empty checking was intended.")) + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -50,7 +51,9 @@ declare_oxc_lint!( impl Rule for BadObjectLiteralComparison { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::BinaryExpression(binary_expression) = node.kind() else { return }; + let AstKind::BinaryExpression(binary_expression) = node.kind() else { + return; + }; if !matches!( binary_expression.operator, @@ -65,7 +68,7 @@ impl Rule for BadObjectLiteralComparison { if is_empty_object_expression(&binary_expression.left) || is_empty_object_expression(&binary_expression.right) { - ctx.diagnostic(BadObjectLiteralComparisonDiagnostic::ObjectComparison( + ctx.diagnostic(object_comparison( binary_expression.span, matches!( binary_expression.operator, @@ -77,7 +80,7 @@ impl Rule for BadObjectLiteralComparison { if is_empty_array_expression(&binary_expression.left) || is_empty_array_expression(&binary_expression.right) { - ctx.diagnostic(BadObjectLiteralComparisonDiagnostic::ArrayComparison( + ctx.diagnostic(array_comparison( binary_expression.span, matches!( binary_expression.operator, 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 f9160faf7e80..8a5fd85910e6 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 @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Expression, RegExpFlags}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,13 +14,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(bad-replace-all-arg): Global flag (g) is missing in the regular expression supplied to the `replaceAll` method.")] -#[diagnostic(severity(warning), help("To replace all occurrences of a string, use the `replaceAll` method with the global flag (g) in the regular expression."))] -struct BadReplaceAllArgDiagnostic( - #[label("`replaceAll` called here")] pub Span, - #[label("RegExp supplied here")] pub Span, -); +fn bad_replace_all_arg_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(bad-replace-all-arg): Global flag (g) is missing in the regular expression supplied to the `replaceAll` method.") + .with_help("To replace all occurrences of a string, use the `replaceAll` method with the global flag (g) in the regular expression.") + .with_labels([LabeledSpan::new_with_span(Some("`replaceAll` called here".into()), span0), LabeledSpan::new_with_span(Some("RegExp supplied here".into()), span1)]) +} #[derive(Debug, Default, Clone)] pub struct BadReplaceAllArg; @@ -50,7 +46,9 @@ declare_oxc_lint!( impl Rule for BadReplaceAllArg { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; if !is_method_call(call_expr, None, Some(&["replaceAll"]), Some(1), None) { return; @@ -65,12 +63,14 @@ impl Rule for BadReplaceAllArg { }; if !flags.contains(RegExpFlags::G) { - let Some(call_expr_callee) = call_expr.callee.as_member_expression() 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; }; - ctx.diagnostic(BadReplaceAllArgDiagnostic(replace_all_span, regex_span)); + ctx.diagnostic(bad_replace_all_arg_diagnostic(replace_all_span, regex_span)); } } } diff --git a/crates/oxc_linter/src/rules/deepscan/missing_throw.rs b/crates/oxc_linter/src/rules/deepscan/missing_throw.rs index 84c038e425a7..64a6f3464a45 100644 --- a/crates/oxc_linter/src/rules/deepscan/missing_throw.rs +++ b/crates/oxc_linter/src/rules/deepscan/missing_throw.rs @@ -1,20 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(missing-throw): Missing throw")] -#[diagnostic( - severity(warning), - help("The `throw` keyword seems to be missing in front of this 'new' expression") -)] -struct MissingThrowDiagnostic(#[label] pub Span); +fn missing_throw_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(missing-throw): Missing throw") + .with_help("The `throw` keyword seems to be missing in front of this 'new' expression") + .with_labels([span0.into()]) +} /// `https://deepscan.io/docs/rules/missing-throw` #[derive(Debug, Default, Clone)] @@ -36,9 +32,11 @@ declare_oxc_lint!( impl Rule for MissingThrow { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::NewExpression(new_expr) = node.kind() else { return }; + let AstKind::NewExpression(new_expr) = node.kind() else { + return; + }; if new_expr.callee.is_specific_id("Error") && Self::has_missing_throw(node, ctx) { - ctx.diagnostic(MissingThrowDiagnostic(new_expr.span)); + ctx.diagnostic(missing_throw_diagnostic(new_expr.span)); } } } @@ -47,7 +45,9 @@ impl MissingThrow { fn has_missing_throw<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { let mut node_ancestors = ctx.nodes().ancestors(node.id()).skip(1); - let Some(node_id) = node_ancestors.next() else { return false }; + let Some(node_id) = node_ancestors.next() else { + return false; + }; if matches!(ctx.nodes().kind(node_id), AstKind::ExpressionStatement(_)) { for node_id in node_ancestors { 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 e24ea9f44549..1813bfab375c 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,20 +1,21 @@ use oxc_ast::{ast::Argument, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(number-arg-out-of-range): Radix or precision arguments of number-related functions should not exceed the limit")] -#[diagnostic( - severity(warning), - help("The first argument of 'Number.prototype.{0}' should be a number between {1} and {2}") -)] -struct NumberArgOutOfRangeDiagnostic(CompactStr, usize, usize, #[label] pub Span); +fn number_arg_out_of_range_diagnostic( + x0: &str, + x1: usize, + x2: usize, + span3: Span, +) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(number-arg-out-of-range): Radix or precision arguments of number-related functions should not exceed the limit") + .with_help(format!("The first argument of 'Number.prototype.{x0}' should be a number between {x1} and {x2}")) + .with_labels([span3.into()]) +} /// `https://deepscan.io/docs/rules/number-arg-out-of-range` #[derive(Debug, Default, Clone)] @@ -47,20 +48,23 @@ impl Rule for NumberArgOutOfRange { match member.static_property_name() { Some(name @ "toString") => { if !(2.0_f64..=36.0_f64).contains(&value) { - let name = CompactStr::from(name); - ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 2, 36, expr.span)); + ctx.diagnostic(number_arg_out_of_range_diagnostic( + name, 2, 36, expr.span, + )); } } Some(name @ ("toFixed" | "toExponential")) => { if !(0.0_f64..=20.0_f64).contains(&value) { - let name = CompactStr::from(name); - ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 0, 20, expr.span)); + ctx.diagnostic(number_arg_out_of_range_diagnostic( + name, 0, 20, expr.span, + )); } } Some(name @ "toPrecision") => { if !(1.0_f64..=21.0_f64).contains(&value) { - let name = CompactStr::from(name); - ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 1, 21, expr.span)); + ctx.diagnostic(number_arg_out_of_range_diagnostic( + name, 1, 21, expr.span, + )); } } _ => {} 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 e70508d4799d..1e0c0adae426 100644 --- a/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs +++ b/crates/oxc_linter/src/rules/deepscan/uninvoked_array_callback.rs @@ -2,25 +2,26 @@ use oxc_ast::{ ast::{Argument, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("deepscan(uninvoked-array-callback): Uninvoked array callback")] -#[diagnostic( - severity(warning), - help("consider filling the array with `undefined` values using `Array.prototype.fill()`") -)] -struct UninvokedArrayCallbackDiagnostic( - #[label("this callback will not be invoked")] Span, - #[label("because this is an array with only empty slots")] Span, -); +fn uninvoked_array_callback_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("deepscan(uninvoked-array-callback): Uninvoked array callback") + .with_help( + "consider filling the array with `undefined` values using `Array.prototype.fill()`", + ) + .with_labels([ + LabeledSpan::new_with_span(Some("this callback will not be invoked".into()), span0), + LabeledSpan::new_with_span( + Some("because this is an array with only empty slots".into()), + span1, + ), + ]) +} /// `https://deepscan.io/docs/rules/uninvoked-array-callback` #[derive(Debug, Default, Clone)] @@ -44,7 +45,9 @@ declare_oxc_lint!( impl Rule for UninvokedArrayCallback { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::NewExpression(new_expr) = node.kind() else { return }; + let AstKind::NewExpression(new_expr) = node.kind() else { + return; + }; if !new_expr.callee.is_specific_id("Array") { return; } @@ -55,7 +58,9 @@ impl Rule for UninvokedArrayCallback { return; } - let Some(member_expr_node) = ctx.nodes().parent_node(node.id()) else { return }; + let Some(member_expr_node) = ctx.nodes().parent_node(node.id()) else { + return; + }; let AstKind::MemberExpression(member_expr) = member_expr_node.kind() else { return; @@ -78,7 +83,7 @@ impl Rule for UninvokedArrayCallback { MemberExpression::StaticMemberExpression(expr) => expr.property.span, MemberExpression::PrivateFieldExpression(expr) => expr.field.span, }; - ctx.diagnostic(UninvokedArrayCallbackDiagnostic(property_span, new_expr.span)); + ctx.diagnostic(uninvoked_array_callback_diagnostic(property_span, new_expr.span)); } } 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 eafc21d02c4f..095ad73c1ab8 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,10 +1,7 @@ pub mod return_checker; use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, GetSpan, Span}; use phf::phf_set; @@ -18,21 +15,20 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum ArrayCallbackReturnDiagnostic { - #[error("eslint(array-callback-return): Missing return on some path for array method {0:?}")] - #[diagnostic( - severity(warning), - help("Array method {0:?} needs to have valid return on all code paths") - )] - ExpectReturn(CompactStr, #[label] Span), +fn expect_return(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(array-callback-return): Missing return on some path for array method {x0:?}" + )) + .with_help(format!("Array method {x0:?} needs to have valid return on all code paths")) + .with_labels([span1.into()]) +} - #[error("eslint(array-callback-return): Unexpected return for array method {0}")] - #[diagnostic( - severity(warning), - help("Array method {0} expects no useless return from the function") - )] - ExpectNoReturn(CompactStr, #[label] Span), +fn expect_no_return(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(array-callback-return): Unexpected return for array method {x0:?}" + )) + .with_help(format!("Array method {x0:?} expects no useless return from the function")) + .with_labels([span1.into()]) } #[derive(Debug, Default, Clone)] @@ -107,24 +103,24 @@ impl Rule for ArrayCallbackReturn { ("forEach", false, _) => (), ("forEach", true, _) => { if return_status.may_return_explicit() { - ctx.diagnostic(ArrayCallbackReturnDiagnostic::ExpectNoReturn( - full_array_method_name(array_method), + ctx.diagnostic(expect_no_return( + &full_array_method_name(array_method), function_body.span, )); } } (_, _, true) => { if !return_status.must_return() { - ctx.diagnostic(ArrayCallbackReturnDiagnostic::ExpectReturn( - full_array_method_name(array_method), + ctx.diagnostic(expect_return( + &full_array_method_name(array_method), function_body.span, )); } } (_, _, false) => { if !return_status.must_return() || return_status.may_return_implicit() { - ctx.diagnostic(ArrayCallbackReturnDiagnostic::ExpectReturn( - full_array_method_name(array_method), + ctx.diagnostic(expect_return( + &full_array_method_name(array_method), function_body.span, )); } diff --git a/crates/oxc_linter/src/rules/eslint/constructor_super.rs b/crates/oxc_linter/src/rules/eslint/constructor_super.rs index 9d398b0a5f57..4164c0752b92 100644 --- a/crates/oxc_linter/src/rules/eslint/constructor_super.rs +++ b/crates/oxc_linter/src/rules/eslint/constructor_super.rs @@ -1,7 +1,3 @@ -// use oxc_diagnostics::{ -// miette::{self, Diagnostic}, -// thiserror::Error, -// }; use oxc_macros::declare_oxc_lint; // use oxc_span::Span; diff --git a/crates/oxc_linter/src/rules/eslint/default_case_last.rs b/crates/oxc_linter/src/rules/eslint/default_case_last.rs index a6fce1d0edb9..187c607e6ad5 100644 --- a/crates/oxc_linter/src/rules/eslint/default_case_last.rs +++ b/crates/oxc_linter/src/rules/eslint/default_case_last.rs @@ -1,17 +1,20 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(default-case-last): Enforce default clauses in switch statements to be last")] -#[diagnostic(severity(warning))] -struct DefaultCaseLastDiagnostic(#[label("Default clause should be the last clause.")] pub Span); +fn default_case_last_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(default-case-last): Enforce default clauses in switch statements to be last", + ) + .with_labels([LabeledSpan::new_with_span( + Some("Default clause should be the last clause.".into()), + span0, + )]) +} #[derive(Debug, Default, Clone)] pub struct DefaultCaseLast; @@ -55,14 +58,16 @@ declare_oxc_lint!( impl Rule for DefaultCaseLast { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::SwitchStatement(switch) = node.kind() else { return }; + let AstKind::SwitchStatement(switch) = node.kind() else { + return; + }; let cases = &switch.cases; let index_of_default = cases.iter().position(|c| c.test.is_none()); if let Some(index) = index_of_default { if index != cases.len() - 1 { let default_clause = &cases[index]; - ctx.diagnostic(DefaultCaseLastDiagnostic(Span::new( + ctx.diagnostic(default_case_last_diagnostic(Span::new( default_clause.span.start, default_clause.span.start + 7, ))); diff --git a/crates/oxc_linter/src/rules/eslint/default_param_last.rs b/crates/oxc_linter/src/rules/eslint/default_param_last.rs index 529e7d2f67b1..8567ad62a7cc 100644 --- a/crates/oxc_linter/src/rules/eslint/default_param_last.rs +++ b/crates/oxc_linter/src/rules/eslint/default_param_last.rs @@ -1,18 +1,17 @@ use oxc_ast::ast::FormalParameter; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(default-param-last): Default parameters should be last")] -#[diagnostic(severity(warning), help("Enforce default parameters to be last."))] -struct DefaultParamLastDiagnostic(#[label] pub Span); +fn default_param_last_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(default-param-last): Default parameters should be last") + .with_help("Enforce default parameters to be last.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct DefaultParamLast; @@ -61,7 +60,7 @@ fn check_params<'a>(items: &'a [FormalParameter<'a>], ctx: &LintContext<'a>) { continue; } if has_seen_plain_param && param.pattern.kind.is_assignment_pattern() { - ctx.diagnostic(DefaultParamLastDiagnostic(param.span)); + ctx.diagnostic(default_param_last_diagnostic(param.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/eqeqeq.rs b/crates/oxc_linter/src/rules/eslint/eqeqeq.rs index 7c576a9a5ae5..524f6491a519 100644 --- a/crates/oxc_linter/src/rules/eslint/eqeqeq.rs +++ b/crates/oxc_linter/src/rules/eslint/eqeqeq.rs @@ -2,20 +2,19 @@ use oxc_ast::{ ast::{BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(eqeqeq): Expected {1} and instead saw {0}")] -#[diagnostic(severity(warning), help("Prefer {1} operator"))] -struct EqeqeqDiagnostic(&'static str, &'static str, #[label] pub Span); +fn eqeqeq_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(eqeqeq): Expected {x1} and instead saw {x0}")) + .with_help(format!("Prefer {x1} operator")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct Eqeqeq { @@ -59,7 +58,9 @@ impl Rule for Eqeqeq { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::BinaryExpression(binary_expr) = node.kind() else { return }; + let AstKind::BinaryExpression(binary_expr) = node.kind() else { + return; + }; let is_null = is_null_check(binary_expr); let enforce_rule_for_null = matches!(self.null_type, NullType::Always); let enforce_inverse_rule_for_null = matches!(self.null_type, NullType::Never); @@ -70,7 +71,7 @@ impl Rule for Eqeqeq { // There are some uncontrolled cases to auto fix. // In ESlint, `null >= null` will be auto fixed to `null > null` which is also wrong. // So I just report it. - ctx.diagnostic(EqeqeqDiagnostic( + ctx.diagnostic(eqeqeq_diagnostic( operator, &operator[0..operator.len() - 1], binary_expr.span, @@ -118,7 +119,7 @@ impl Rule for Eqeqeq { // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix. if is_type_of_binary_bool || are_literals_and_same_type_bool { ctx.diagnostic_with_fix( - EqeqeqDiagnostic(operator, preferred_operator, operator_span), + eqeqeq_diagnostic(operator, preferred_operator, operator_span), || { let start = binary_expr.left.span().end; let end = binary_expr.right.span().start; @@ -126,7 +127,7 @@ impl Rule for Eqeqeq { }, ); } else { - ctx.diagnostic(EqeqeqDiagnostic(operator, preferred_operator, operator_span)); + ctx.diagnostic(eqeqeq_diagnostic(operator, preferred_operator, operator_span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/for_direction.rs b/crates/oxc_linter/src/rules/eslint/for_direction.rs index cf5591f3a2b9..cd16d51390eb 100644 --- a/crates/oxc_linter/src/rules/eslint/for_direction.rs +++ b/crates/oxc_linter/src/rules/eslint/for_direction.rs @@ -5,25 +5,19 @@ use oxc_ast::{ }, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, UnaryOperator, UpdateOperator}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(for-direction): The update clause in this loop moves the variable in the wrong direction" -)] -#[diagnostic(severity(warning), help("Use while loop for intended infinite loop"))] -struct ForDirectionDiagnostic( - #[label("This test moves in the wrong direction")] pub Span, /*test clause */ - #[label("with this update")] pub Span, /*update clause */ -); +fn for_direction_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(for-direction): The update clause in this loop moves the variable in the wrong direction") + .with_help("Use while loop for intended infinite loop") + .with_labels([LabeledSpan::new_with_span(Some("This test moves in the wrong direction".into()), span0), LabeledSpan::new_with_span(Some("with this update".into()), span1)]) +} #[derive(Debug, Default, Clone)] pub struct ForDirection; @@ -56,12 +50,12 @@ impl Rule for ForDirection { }; let test_operator = &test.operator; let wrong_direction = match (test_operator, counter_position) { - (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, LEFT) => BACKWARD, - (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, RIGHT) => FORWARD, - (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, LEFT) => { + (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, RIGHT) + | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, LEFT) => { FORWARD } - (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, RIGHT) => { + (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, LEFT) + | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, RIGHT) => { BACKWARD } _ => return, @@ -70,7 +64,7 @@ impl Rule for ForDirection { let update_direction = get_update_direction(update, counter); if update_direction == wrong_direction { let update_span = get_update_span(update); - ctx.diagnostic(ForDirectionDiagnostic(test.span, update_span)); + ctx.diagnostic(for_direction_diagnostic(test.span, update_span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/getter_return.rs b/crates/oxc_linter/src/rules/eslint/getter_return.rs index cc09e70b84e9..3d8f7b955f0b 100644 --- a/crates/oxc_linter/src/rules/eslint/getter_return.rs +++ b/crates/oxc_linter/src/rules/eslint/getter_return.rs @@ -5,10 +5,8 @@ use oxc_ast::{ }, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::{ pg::neighbors_filtered_by_edge_weight, AssignmentValue, BasicBlockElement, EdgeType, Register, @@ -17,10 +15,11 @@ use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(getter-return): Expected to always return a value in getter.")] -#[diagnostic(severity(warning), help("Return a value from all code paths in getter."))] -struct GetterReturnDiagnostic(#[label] pub Span); +fn getter_return_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(getter-return): Expected to always return a value in getter.") + .with_help("Return a value from all code paths in getter.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct GetterReturn { @@ -309,7 +308,7 @@ impl GetterReturn { // If not, flag it as a diagnostic. if !definitely_returns_in_all_codepaths { - ctx.diagnostic(GetterReturnDiagnostic(span)); + ctx.diagnostic(getter_return_diagnostic(span)); } } } @@ -326,64 +325,28 @@ fn test() { use crate::tester::Tester; let pass = vec![ ("var foo = { get bar(){return true;} };", None), - ( - "var foo = { get bar() {return;} };", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "var foo = { get bar(){return true;} };", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "var foo = { get bar(){if(bar) {return;} return true;} };", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("var foo = { get bar() {return;} };", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("var foo = { get bar(){return true;} };", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("var foo = { get bar(){if(bar) {return;} return true;} };", Some(serde_json::json!([{ "allowImplicit": true }]))), ("class foo { get bar(){return true;} }", None), ("class foo { get bar(){if(baz){return true;} else {return false;} } }", None), ("class foo { get(){return true;} }", None), - ( - "class foo { get bar(){return true;} }", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("class foo { get bar(){return true;} }", Some(serde_json::json!([{ "allowImplicit": true }]))), ("class foo { get bar(){return;} }", Some(serde_json::json!([{ "allowImplicit": true }]))), ("Object.defineProperty(foo, \"bar\", { get: function () {return true;}});", None), - ( - "Object.defineProperty(foo, \"bar\", { get: function () { ~function (){ return true; }();return true;}});", - None, - ), + ("Object.defineProperty(foo, \"bar\", { get: function () { ~function (){ return true; }();return true;}});", None), ("Object.defineProperties(foo, { bar: { get: function () {return true;}} });", None), - ( - "Object.defineProperties(foo, { bar: { get: function () { ~function (){ return true; }(); return true;}} });", - None, - ), + ("Object.defineProperties(foo, { bar: { get: function () { ~function (){ return true; }(); return true;}} });", None), ("Reflect.defineProperty(foo, \"bar\", { get: function () {return true;}});", None), - ( - "Reflect.defineProperty(foo, \"bar\", { get: function () { ~function (){ return true; }();return true;}});", - None, - ), + ("Reflect.defineProperty(foo, \"bar\", { get: function () { ~function (){ return true; }();return true;}});", None), ("Object.create(foo, { bar: { get() {return true;} } });", None), ("Object.create(foo, { bar: { get: function () {return true;} } });", None), ("Object.create(foo, { bar: { get: () => {return true;} } });", None), - ( - "Object.defineProperty(foo, \"bar\", { get: function () {return true;}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperty(foo, \"bar\", { get: function (){return;}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperties(foo, { bar: { get: function () {return true;}} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperties(foo, { bar: { get: function () {return;}} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Reflect.defineProperty(foo, \"bar\", { get: function () {return true;}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("Object.defineProperty(foo, \"bar\", { get: function () {return true;}});", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperty(foo, \"bar\", { get: function (){return;}});", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperties(foo, { bar: { get: function () {return true;}} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperties(foo, { bar: { get: function () {return;}} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Reflect.defineProperty(foo, \"bar\", { get: function () {return true;}});", Some(serde_json::json!([{ "allowImplicit": true }]))), ("var get = function(){};", None), ("var get = function(){ return true; };", None), ("var foo = { bar(){} };", None), @@ -399,7 +362,8 @@ fn test() { ("foo.create(null, { bar: { get() {} } });", None), ("var foo = { get willThrowSoValid() { throw MyException() } };", None), ("export abstract class Foo { protected abstract get foobar(): number; }", None), - ("class T { + ( + "class T { theme: number; get type(): number { switch (theme) { @@ -409,8 +373,11 @@ fn test() { } throw new Error('test') } - }", None), - ("class T { + }", + None, + ), + ( + "class T { theme: number; get type(): number { switch (theme) { @@ -419,11 +386,17 @@ fn test() { default: return 3; } } - }", None), - ("const originalClearTimeout = targetWindow.clearTimeout; + }", + None, + ), + ( + "const originalClearTimeout = targetWindow.clearTimeout; Object.defineProperty(targetWindow, 'vscodeOriginalClearTimeout', { get: () => originalClearTimeout }); - ", None), - ("class T { + ", + None, + ), + ( + "class T { get width(): number | undefined { const val = undefined if (!val) { @@ -432,8 +405,10 @@ fn test() { return val * val; } - }", None), - ("function fn(): void { console.log('test') }", None) + }", + None, + ), + ("function fn(): void { console.log('test') }", None), ]; let fail = vec![ @@ -443,70 +418,34 @@ fn test() { ("var foo = { get bar() { ~function () {return true;}} };", None), ("var foo = { get bar() { return; } };", None), ("var foo = { get bar() {} };", Some(serde_json::json!([{ "allowImplicit": true }]))), - ( - "var foo = { get bar() {if (baz) {return;}} };", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("var foo = { get bar() {if (baz) {return;}} };", Some(serde_json::json!([{ "allowImplicit": true }]))), ("class foo { get bar(){} }", None), ("var foo = class {\n static get\nbar(){} }", None), ("class foo { get bar(){ if (baz) { return true; }}}", None), ("class foo { get bar(){ ~function () { return true; }()}}", None), ("class foo { get bar(){} }", Some(serde_json::json!([{ "allowImplicit": true }]))), - ( - "class foo { get bar(){if (baz) {return true;} } }", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("class foo { get bar(){if (baz) {return true;} } }", Some(serde_json::json!([{ "allowImplicit": true }]))), ("Object.defineProperty(foo, 'bar', { get: function (){}});", None), ("Object.defineProperty(foo, 'bar', { get: function getfoo (){}});", None), ("Object.defineProperty(foo, 'bar', { get(){} });", None), ("Object.defineProperty(foo, 'bar', { get: () => {}});", None), ("Object.defineProperty(foo, \"bar\", { get: function (){if(bar) {return true;}}});", None), - ( - "Object.defineProperty(foo, \"bar\", { get: function (){ ~function () { return true; }()}});", - None, - ), + ("Object.defineProperty(foo, \"bar\", { get: function (){ ~function () { return true; }()}});", None), ("Reflect.defineProperty(foo, 'bar', { get: function (){}});", None), ("Object.create(foo, { bar: { get: function() {} } })", None), ("Object.create(foo, { bar: { get() {} } })", None), ("Object.create(foo, { bar: { get: () => {} } })", None), - ( - "Object.defineProperties(foo, { bar: { get: function () {}} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperties(foo, { bar: { get: function (){if(bar) {return true;}}}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperties(foo, { bar: { get: function () {~function () { return true; }()}} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.defineProperty(foo, \"bar\", { get: function (){}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Object.create(foo, { bar: { get: function (){} } });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "Reflect.defineProperty(foo, \"bar\", { get: function (){}});", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("Object.defineProperties(foo, { bar: { get: function () {}} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperties(foo, { bar: { get: function (){if(bar) {return true;}}}});", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperties(foo, { bar: { get: function () {~function () { return true; }()}} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.defineProperty(foo, \"bar\", { get: function (){}});", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Object.create(foo, { bar: { get: function (){} } });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("Reflect.defineProperty(foo, \"bar\", { get: function (){}});", Some(serde_json::json!([{ "allowImplicit": true }]))), ("Object?.defineProperty(foo, 'bar', { get: function (){} });", None), ("(Object?.defineProperty)(foo, 'bar', { get: function (){} });", None), - ( - "Object?.defineProperty(foo, 'bar', { get: function (){} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "(Object?.defineProperty)(foo, 'bar', { get: function (){} });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), - ( - "(Object?.create)(foo, { bar: { get: function (){} } });", - Some(serde_json::json!([{ "allowImplicit": true }])), - ), + ("Object?.defineProperty(foo, 'bar', { get: function (){} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("(Object?.defineProperty)(foo, 'bar', { get: function (){} });", Some(serde_json::json!([{ "allowImplicit": true }]))), + ("(Object?.create)(foo, { bar: { get: function (){} } });", Some(serde_json::json!([{ "allowImplicit": true }]))), ]; Tester::new(GetterReturn::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/eslint/guard_for_in.rs b/crates/oxc_linter/src/rules/eslint/guard_for_in.rs index 34ddcf2fceb9..82fb435a3cf5 100644 --- a/crates/oxc_linter/src/rules/eslint/guard_for_in.rs +++ b/crates/oxc_linter/src/rules/eslint/guard_for_in.rs @@ -1,18 +1,17 @@ use oxc_ast::ast::Statement; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(guard-for-in): Require `for-in` loops to include an `if` statement")] -#[diagnostic(severity(warning), help("The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype."))] -struct GuardForInDiagnostic(#[label] pub Span); +fn guard_for_in_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(guard-for-in): Require `for-in` loops to include an `if` statement") + .with_help("The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct GuardForIn; @@ -66,7 +65,7 @@ impl Rule for GuardForIn { } _ => {} } - ctx.diagnostic(GuardForInDiagnostic(Span::new( + ctx.diagnostic(guard_for_in_diagnostic(Span::new( for_in_statement.span.start, for_in_statement.right.span().end + 1, ))); diff --git a/crates/oxc_linter/src/rules/eslint/max_lines.rs b/crates/oxc_linter/src/rules/eslint/max_lines.rs index 37fed1696b34..6aec92d03cbf 100644 --- a/crates/oxc_linter/src/rules/eslint/max_lines.rs +++ b/crates/oxc_linter/src/rules/eslint/max_lines.rs @@ -1,18 +1,17 @@ +use oxc_diagnostics::OxcDiagnostic; + use serde_json::Value; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(max-lines): {0:?}")] -#[diagnostic(severity(warning), help("Reduce the number of lines in this file"))] -struct MaxLinesDiagnostic(CompactStr, #[label] Span); +fn max_lines_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(max-lines): {x0:?}")) + .with_help("Reduce the number of lines in this file") + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct MaxLines(Box); @@ -122,10 +121,10 @@ impl Rule for MaxLines { }; if lines_in_file.saturating_sub(blank_lines).saturating_sub(comment_lines) > self.max { - let error = CompactStr::from(format!( + let error = format!( "File has too many lines ({}). Maximum allowed is {}.", lines_in_file, self.max, - )); + ); let start = ctx .source_text() @@ -133,8 +132,8 @@ impl Rule for MaxLines { .take(self.max) .map(|line| line.chars().count() + 1) // padding 1 each line for '\n' .sum::(); - ctx.diagnostic(MaxLinesDiagnostic( - error, + ctx.diagnostic(max_lines_diagnostic( + &error, Span::new( u32::try_from(start).unwrap_or(u32::MIN), u32::try_from(ctx.source_text().len()).unwrap_or(u32::MAX), @@ -189,18 +188,18 @@ fn test() { ), ( "var x; - - - + + + var y;", Some(serde_json::json!([{ "max": 2, "skipBlankLines": true }])), ), ( "//a single line comment var xy; - + var xy; - + /* a multiline really really long comment*/", @@ -217,18 +216,18 @@ fn test() { ("//a single line comment\nvar xy;\nvar xy;", Some(serde_json::json!([2]))), ( "var x; - - - + + + var y;", Some(serde_json::json!([{ "max": 2 }])), ), ( "//a single line comment var xy; - + var xy; - + /* a multiline really really long comment*/", @@ -250,9 +249,9 @@ fn test() { ( "//a single line comment var xy; - + var xy; - + /* a multiline really really long comment*/", @@ -283,7 +282,7 @@ fn test() { ), ( "A - + ", Some(serde_json::json!([{ "max": 1 }])), ), @@ -310,7 +309,7 @@ fn test() { ), ( " - + var a = 'a', c, x; @@ -322,7 +321,7 @@ fn test() { var x var c; console.log - // some block + // some block // comments", Some(serde_json::json!([{ "max": 2, "skipComments": true }])), ), @@ -348,37 +347,37 @@ fn test() { var x var c; console.log - /** block - + /** block + comments */", Some(serde_json::json!([{ "max": 2, "skipComments": true }])), ), ( "var a = 'a'; - - + + // comment", Some(serde_json::json!([{ "max": 2, "skipComments": true }])), ), ( "var a = 'a'; var x - - + + var c; console.log - + ", Some(serde_json::json!([{ "max": 2, "skipBlankLines": true }])), ), ( "var a = 'a'; - - + + var x var c; console.log - + ", Some(serde_json::json!([{ "max": 2, "skipBlankLines": true }])), ), @@ -406,14 +405,14 @@ fn test() { ( " var x = ''; - + // comment - + var b = '', c, d, e - + // comment", Some(serde_json::json!([{ "max": 2, "skipComments": true, "skipBlankLines": true }])), ), diff --git a/crates/oxc_linter/src/rules/eslint/max_params.rs b/crates/oxc_linter/src/rules/eslint/max_params.rs index 0993a4898a4f..4bda36d5ac06 100644 --- a/crates/oxc_linter/src/rules/eslint/max_params.rs +++ b/crates/oxc_linter/src/rules/eslint/max_params.rs @@ -1,21 +1,19 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use serde_json::Value; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(max-params): {0:?}")] -#[diagnostic( - severity(warning), - help("This rule enforces a maximum number of parameters allowed in function definitions.") -)] -struct MaxParamsDiagnostic(CompactStr, #[label] pub Span); +fn max_params_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(max-params): {x0:?}")) + .with_help( + "This rule enforces a maximum number of parameters allowed in function definitions.", + ) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct MaxParams(Box); @@ -91,18 +89,22 @@ impl Rule for MaxParams { function.params.items.len(), self.max ); - let error = CompactStr::from(error_msg); let span = function.params.span; - ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); + ctx.diagnostic(max_params_diagnostic( + &error_msg, + Span::new(span.start, span.end), + )); } else { let error_msg = format!( "Function has too many parameters ({}). Maximum allowed is {}.", function.params.items.len(), self.max ); - let error = CompactStr::from(error_msg); let span = function.params.span; - ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); + ctx.diagnostic(max_params_diagnostic( + &error_msg, + Span::new(span.start, span.end), + )); } } } @@ -113,9 +115,11 @@ impl Rule for MaxParams { function.params.items.len(), self.max ); - let error = CompactStr::from(error_msg); let span = function.params.span; - ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); + ctx.diagnostic(max_params_diagnostic( + &error_msg, + Span::new(span.start, span.end), + )); } } _ => {} diff --git a/crates/oxc_linter/src/rules/eslint/no_array_constructor.rs b/crates/oxc_linter/src/rules/eslint/no_array_constructor.rs index f9e331aa6000..62f87fa3d90e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_array_constructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_array_constructor.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-array-constructor): Disallow `Array` constructors")] -#[diagnostic(severity(warning), help("Use array literal instead"))] -struct NoArrayConstructorDiagnostic(#[label] pub Span); +fn no_array_constructor_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-array-constructor): Disallow `Array` constructors") + .with_help("Use array literal instead") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoArrayConstructor; @@ -58,7 +57,7 @@ impl Rule for NoArrayConstructor { && type_parameters.is_none() && !optional { - ctx.diagnostic(NoArrayConstructorDiagnostic(span)); + ctx.diagnostic(no_array_constructor_diagnostic(span)); } } } 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 7ea55526f245..1234f8e086a2 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 @@ -2,19 +2,19 @@ use oxc_ast::{ ast::{Argument, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-async-promise-executor): Promise executor functions should not be `async`.")] -#[diagnostic(severity(warning))] -struct NoAsyncPromiseExecutorDiagnostic(#[label] pub Span); +fn no_async_promise_executor_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-async-promise-executor): Promise executor functions should not be `async`.", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoAsyncPromiseExecutor; @@ -51,7 +51,9 @@ declare_oxc_lint!( impl Rule for NoAsyncPromiseExecutor { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::NewExpression(new_expression) = node.kind() else { return }; + let AstKind::NewExpression(new_expression) = node.kind() else { + return; + }; if !new_expression.callee.is_specific_id("Promise") { return; } @@ -68,7 +70,7 @@ impl Rule for NoAsyncPromiseExecutor { span.end = span.start + 5; - ctx.diagnostic(NoAsyncPromiseExecutorDiagnostic(span)); + ctx.diagnostic(no_async_promise_executor_diagnostic(span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs index a573a29ac991..b51c49527169 100644 --- a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs +++ b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs @@ -2,19 +2,17 @@ use oxc_ast::{ ast::{Expression, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-await-in-loop): Unexpected `await` inside a loop.")] -#[diagnostic(severity(warning))] -struct NoAwaitInLoopDiagnostic(#[label] pub Span); +fn no_await_in_loop_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-await-in-loop): Unexpected `await` inside a loop.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoAwaitInLoop; @@ -84,7 +82,7 @@ impl Rule for NoAwaitInLoop { } if is_in_loop { - ctx.diagnostic(NoAwaitInLoopDiagnostic(span)); + ctx.diagnostic(no_await_in_loop_diagnostic(span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_bitwise.rs b/crates/oxc_linter/src/rules/eslint/no_bitwise.rs index cf9994138693..198c695b5113 100644 --- a/crates/oxc_linter/src/rules/eslint/no_bitwise.rs +++ b/crates/oxc_linter/src/rules/eslint/no_bitwise.rs @@ -1,21 +1,17 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::BinaryOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-bitwise): Unexpected use of {0:?}")] -#[diagnostic( - severity(warning), - help("bitwise operators are not allowed, maybe you mistyped `&&` or `||`") -)] -struct NoBitwiseDiagnostic(&'static str, #[label] pub Span); +fn no_bitwise_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-bitwise): Unexpected use of {x0:?}")) + .with_help("bitwise operators are not allowed, maybe you mistyped `&&` or `||`") + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoBitwise(Box); @@ -84,7 +80,7 @@ impl Rule for NoBitwise { && !allowed_operator(&self.allow, op) && !is_int32_hint(self.int32_hint, node) { - ctx.diagnostic(NoBitwiseDiagnostic(op, bin_expr.span)); + ctx.diagnostic(no_bitwise_diagnostic(op, bin_expr.span)); } } AstKind::UnaryExpression(unary_expr) => { @@ -94,7 +90,7 @@ impl Rule for NoBitwise { && !allowed_operator(&self.allow, op) && !is_int32_hint(self.int32_hint, node) { - ctx.diagnostic(NoBitwiseDiagnostic(op, unary_expr.span)); + ctx.diagnostic(no_bitwise_diagnostic(op, unary_expr.span)); } } AstKind::AssignmentExpression(assign_expr) => { @@ -104,7 +100,7 @@ impl Rule for NoBitwise { && !allowed_operator(&self.allow, op) && !is_int32_hint(self.int32_hint, node) { - ctx.diagnostic(NoBitwiseDiagnostic(op, assign_expr.span)); + ctx.diagnostic(no_bitwise_diagnostic(op, assign_expr.span)); } } _ => {} diff --git a/crates/oxc_linter/src/rules/eslint/no_caller.rs b/crates/oxc_linter/src/rules/eslint/no_caller.rs index 2690bfcccbca..b59a8001c8cc 100644 --- a/crates/oxc_linter/src/rules/eslint/no_caller.rs +++ b/crates/oxc_linter/src/rules/eslint/no_caller.rs @@ -1,22 +1,16 @@ use oxc_ast::{ast::MemberExpression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-caller): Disallow the use of arguments.caller or arguments.callee")] -#[diagnostic( - severity(warning), - help( - "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them" - ) -)] -struct NoCallerDiagnostic(#[label] pub Span); +fn no_caller_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-caller): Disallow the use of arguments.caller or arguments.callee") + .with_help("'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoCaller; @@ -57,7 +51,7 @@ impl Rule for NoCaller { if (expr.property.name == "callee" || expr.property.name == "caller") && expr.object.is_specific_id("arguments") { - ctx.diagnostic(NoCallerDiagnostic(expr.property.span)); + ctx.diagnostic(no_caller_diagnostic(expr.property.span)); } } } 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 9e48ea420ad9..6fbe2ba3fd8b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs @@ -2,19 +2,19 @@ use oxc_ast::{ ast::{Statement, VariableDeclarationKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-case-declarations): Unexpected lexical declaration in case block.")] -#[diagnostic(severity(warning))] -struct NoCaseDeclarationsDiagnostic(#[label] pub Span); +fn no_case_declarations_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-case-declarations): Unexpected lexical declaration in case block.", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoCaseDeclarations; @@ -58,12 +58,12 @@ impl Rule for NoCaseDeclarations { Statement::FunctionDeclaration(d) => { let start = d.span.start; let end = start + 8; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); + ctx.diagnostic(no_case_declarations_diagnostic(Span::new(start, end))); } Statement::ClassDeclaration(d) => { let start = d.span.start; let end = start + 5; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); + ctx.diagnostic(no_case_declarations_diagnostic(Span::new(start, end))); } Statement::VariableDeclaration(var) if var.kind.is_lexical() => { let start = var.span.start; @@ -73,7 +73,7 @@ impl Rule for NoCaseDeclarations { VariableDeclarationKind::Let => 3, }; let end = start + end; - ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end))); + ctx.diagnostic(no_case_declarations_diagnostic(Span::new(start, end))); } _ => {} }; diff --git a/crates/oxc_linter/src/rules/eslint/no_class_assign.rs b/crates/oxc_linter/src/rules/eslint/no_class_assign.rs index 0fd482fc7349..89d22c1eff81 100644 --- a/crates/oxc_linter/src/rules/eslint/no_class_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_class_assign.rs @@ -1,21 +1,20 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-class-assign): Unexpected re-assignment of class {0}")] -#[diagnostic(severity(warning))] -struct NoClassAssignDiagnostic( - CompactStr, - #[label("{0} is declared as class here")] pub Span, - #[label("{0} is re-assigned here")] pub Span, -); +fn no_class_assign_diagnostic(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-class-assign): Unexpected re-assignment of class {x0}" + )) + .with_labels([ + LabeledSpan::new_with_span(Some(format!("{x0} is declared as class here")), span1), + LabeledSpan::new_with_span(Some(format!("{x0} is re-assigned here")), span2), + ]) +} #[derive(Debug, Default, Clone)] pub struct NoClassAssign; @@ -44,8 +43,8 @@ impl Rule for NoClassAssign { if symbol_table.get_flag(symbol_id).is_class() { for reference in symbol_table.get_resolved_references(symbol_id) { if reference.is_write() { - ctx.diagnostic(NoClassAssignDiagnostic( - symbol_table.get_name(symbol_id).into(), + ctx.diagnostic(no_class_assign_diagnostic( + symbol_table.get_name(symbol_id), symbol_table.get_span(symbol_id), reference.span(), )); diff --git a/crates/oxc_linter/src/rules/eslint/no_compare_neg_zero.rs b/crates/oxc_linter/src/rules/eslint/no_compare_neg_zero.rs index 9184f4f4f0ed..ea0d75bba8fc 100644 --- a/crates/oxc_linter/src/rules/eslint/no_compare_neg_zero.rs +++ b/crates/oxc_linter/src/rules/eslint/no_compare_neg_zero.rs @@ -1,21 +1,19 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-compare-neg-zero): Do not use the {0} operator to compare against -0.")] -#[diagnostic( - severity(warning), - help("Use Object.is(x, -0) to test equality with -0 and use 0 for other cases") -)] -struct NoCompareNegZeroDiagnostic(&'static str, #[label] pub Span); +fn no_compare_neg_zero_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-compare-neg-zero): Do not use the {x0} operator to compare against -0." + )) + .with_help("Use Object.is(x, -0) to test equality with -0 and use 0 for other cases") + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoCompareNegZero; @@ -45,7 +43,7 @@ impl Rule for NoCompareNegZero { if Self::should_check(expr.operator) { let op = expr.operator.as_str(); if is_neg_zero(&expr.left) || is_neg_zero(&expr.right) { - ctx.diagnostic(NoCompareNegZeroDiagnostic(op, expr.span)); + ctx.diagnostic(no_compare_neg_zero_diagnostic(op, expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs index 1252376ae80b..fa22e7d7dc63 100644 --- a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs @@ -2,19 +2,20 @@ use oxc_ast::{ ast::{AssignmentExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-cond-assign): Expected a conditional expression and instead saw an assignment")] -#[diagnostic(severity(warning), help("Consider wrapping the assignment in additional parentheses"))] -struct NoCondAssignDiagnostic(#[label] pub Span); +fn no_cond_assign_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-cond-assign): Expected a conditional expression and instead saw an assignment", + ) + .with_help("Consider wrapping the assignment in additional parentheses") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoCondAssign { @@ -99,7 +100,7 @@ impl NoCondAssign { operator_span.start += start; operator_span.end = operator_span.start + expr.operator.as_str().len() as u32; - ctx.diagnostic(NoCondAssignDiagnostic(operator_span)); + ctx.diagnostic(no_cond_assign_diagnostic(operator_span)); } fn check_expression(&self, ctx: &LintContext<'_>, expr: &Expression<'_>) { let mut expr = expr; diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index 8e7196d57844..409caaaea956 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -1,17 +1,15 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-console): Unexpected console statement.")] -#[diagnostic(severity(warning))] -struct NoConsoleDiagnostic(#[label] pub Span); +fn no_console_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-console): Unexpected console statement.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoConsole(Box); @@ -83,7 +81,7 @@ impl Rule for NoConsole { .any(|s| mem.static_property_name().is_some_and(|f| f == s)) { if let Some(mem) = mem.static_property_info() { - ctx.diagnostic(NoConsoleDiagnostic(mem.0)); + ctx.diagnostic(no_console_diagnostic(mem.0)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_const_assign.rs b/crates/oxc_linter/src/rules/eslint/no_const_assign.rs index d9ac98549c0c..a18ef557d382 100644 --- a/crates/oxc_linter/src/rules/eslint/no_const_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_const_assign.rs @@ -1,21 +1,20 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-const-assign): Unexpected re-assignment of const variable {0}")] -#[diagnostic(severity(warning))] -struct NoConstAssignDiagnostic( - CompactStr, - #[label("{0} is declared here as const")] pub Span, - #[label("{0} is re-assigned here")] pub Span, -); +fn no_const_assign_diagnostic(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-const-assign): Unexpected re-assignment of const variable {x0}" + )) + .with_labels([ + LabeledSpan::new_with_span(Some(format!("{x0} is declared here as const")), span1), + LabeledSpan::new_with_span(Some(format!("{x0} is re-assigned here")), span2), + ]) +} #[derive(Debug, Default, Clone)] pub struct NoConstAssign; @@ -43,8 +42,8 @@ impl Rule for NoConstAssign { if symbol_table.get_flag(symbol_id).is_const_variable() { for reference in symbol_table.get_resolved_references(symbol_id) { if reference.is_write() { - ctx.diagnostic(NoConstAssignDiagnostic( - symbol_table.get_name(symbol_id).into(), + ctx.diagnostic(no_const_assign_diagnostic( + symbol_table.get_name(symbol_id), symbol_table.get_span(symbol_id), reference.span(), )); 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 8df5e757cf3b..f01d39909e4f 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 @@ -1,9 +1,7 @@ #[allow(clippy::wildcard_imports)] use oxc_ast::{ast::*, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator}; @@ -51,39 +49,38 @@ declare_oxc_lint!( correctness ); -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(no-constant-binary-expression): Unexpected constant {0:?} on the left-hand side of a {1:?} expression" -)] -#[diagnostic( - severity(warning), - help("This expression always evaluates to the constant on the left-hand side") -)] -struct ConstantShortCircuit(&'static str, &'static str, #[label] Span); +fn constant_short_circuit(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-constant-binary-expression): Unexpected constant {x0:?} on the left-hand side of a {x1:?} expression")) + .with_help("This expression always evaluates to the constant on the left-hand side") + .with_labels([span2.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-constant-binary-expression): Unexpected constant binary expression")] -#[diagnostic(severity(warning), help("This compares constantly with the {0}-hand side of the {1}"))] -struct ConstantBinaryOperand(&'static str, &'static str, #[label] Span); +fn constant_binary_operand(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-constant-binary-expression): Unexpected constant binary expression", + ) + .with_help(format!("This compares constantly with the {x0}-hand side of the {x1}")) + .with_labels([span2.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-constant-binary-expression): Unexpected comparison to newly constructed object")] -#[diagnostic(severity(warning), help("These two values can never be equal"))] -struct ConstantAlwaysNew(#[label] Span); +fn constant_always_new(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-constant-binary-expression): Unexpected comparison to newly constructed object", + ) + .with_help("These two values can never be equal") + .with_labels([span0.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(no-constant-binary-expression): Unexpected comparison of two newly constructed objects" -)] -#[diagnostic(severity(warning), help("These two values can never be equal"))] -struct ConstantBothAlwaysNew(#[label] Span); +fn constant_both_always_new(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-constant-binary-expression): Unexpected comparison of two newly constructed objects").with_help("These two values can never be equal").with_labels([span0.into()]) +} impl Rule for NoConstantBinaryExpression { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { AstKind::LogicalExpression(expr) => match expr.operator { LogicalOperator::Or | LogicalOperator::And if expr.left.is_constant(true, ctx) => { - ctx.diagnostic(ConstantShortCircuit( + ctx.diagnostic(constant_short_circuit( "truthiness", expr.operator.as_str(), expr.span, @@ -92,7 +89,7 @@ impl Rule for NoConstantBinaryExpression { LogicalOperator::Coalesce if Self::has_constant_nullishness(&expr.left, false, ctx) => { - ctx.diagnostic(ConstantShortCircuit( + ctx.diagnostic(constant_short_circuit( "nullishness", expr.operator.as_str(), expr.span, @@ -112,12 +109,12 @@ impl Rule for NoConstantBinaryExpression { Self::find_binary_expression_constant_operand(right, left, operator, ctx); if right_constant_operand.is_some() { - ctx.diagnostic(ConstantBinaryOperand("left", operator.as_str(), expr.span)); + ctx.diagnostic(constant_binary_operand("left", operator.as_str(), expr.span)); return; } if left_constant_operand.is_some() { - ctx.diagnostic(ConstantBinaryOperand("right", operator.as_str(), expr.span)); + ctx.diagnostic(constant_binary_operand("right", operator.as_str(), expr.span)); return; } @@ -126,7 +123,7 @@ impl Rule for NoConstantBinaryExpression { BinaryOperator::StrictEquality | BinaryOperator::StrictInequality ) && (Self::is_always_new(left, ctx) || Self::is_always_new(right, ctx)) { - ctx.diagnostic(ConstantAlwaysNew(expr.span)); + ctx.diagnostic(constant_always_new(expr.span)); return; } @@ -134,7 +131,7 @@ impl Rule for NoConstantBinaryExpression { && Self::is_always_new(left, ctx) && Self::is_always_new(right, ctx) { - ctx.diagnostic(ConstantBothAlwaysNew(expr.span)); + ctx.diagnostic(constant_both_always_new(expr.span)); } } _ => {} diff --git a/crates/oxc_linter/src/rules/eslint/no_constant_condition.rs b/crates/oxc_linter/src/rules/eslint/no_constant_condition.rs index 2b668917a3ec..dc2a3a7538b7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_constant_condition.rs +++ b/crates/oxc_linter/src/rules/eslint/no_constant_condition.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{ast_util::IsConstant, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-constant-condition): Unexpected constant condition")] -#[diagnostic(severity(warning), help("Constant expression as a test condition is not allowed"))] -struct NoConstantConditionDiagnostic(#[label] pub Span); +fn no_constant_condition_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-constant-condition): Unexpected constant condition") + .with_help("Constant expression as a test condition is not allowed") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoConstantCondition { @@ -54,12 +53,12 @@ impl Rule for NoConstantCondition { match node.kind() { AstKind::IfStatement(if_stmt) => { if if_stmt.test.is_constant(true, ctx) { - ctx.diagnostic(NoConstantConditionDiagnostic(if_stmt.test.span())); + ctx.diagnostic(no_constant_condition_diagnostic(if_stmt.test.span())); } } AstKind::ConditionalExpression(condition_expr) => { if condition_expr.test.is_constant(true, ctx) { - ctx.diagnostic(NoConstantConditionDiagnostic(condition_expr.test.span())); + ctx.diagnostic(no_constant_condition_diagnostic(condition_expr.test.span())); } } _ => {} diff --git a/crates/oxc_linter/src/rules/eslint/no_continue.rs b/crates/oxc_linter/src/rules/eslint/no_continue.rs index 9dc83e1098c4..00aafdc1639a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_continue.rs +++ b/crates/oxc_linter/src/rules/eslint/no_continue.rs @@ -1,16 +1,15 @@ use crate::{context::LintContext, rule::Rule, AstNode}; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-continue): Unexpected use of `continue` statement.")] -#[diagnostic(severity(warning), help("Do not use the `continue` statement."))] -struct NoContinueDiagnostic(#[label] pub Span); +fn no_continue_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-continue): Unexpected use of `continue` statement.") + .with_help("Do not use the `continue` statement.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoContinue; @@ -42,7 +41,7 @@ declare_oxc_lint!( impl Rule for NoContinue { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::ContinueStatement(continue_statement) = node.kind() { - ctx.diagnostic(NoContinueDiagnostic(Span::new( + ctx.diagnostic(no_continue_diagnostic(Span::new( continue_statement.span.start, continue_statement.span.start + 8, ))); @@ -61,9 +60,9 @@ fn test() { let fail = vec![ "var sum = 0, i; for(i = 0; i < 10; i++){ if(i <= 5) { continue; } sum += i; }", -"var sum = 0, i; myLabel: for(i = 0; i < 10; i++){ if(i <= 5) { continue myLabel; } sum += i; }", -"var sum = 0, i = 0; while(i < 10) { if(i <= 5) { i++; continue; } sum += i; i++; }", -"var sum = 0, i = 0; myLabel: while(i < 10) { if(i <= 5) { i++; continue myLabel; } sum += i; i++; }" + "var sum = 0, i; myLabel: for(i = 0; i < 10; i++){ if(i <= 5) { continue myLabel; } sum += i; }", + "var sum = 0, i = 0; while(i < 10) { if(i <= 5) { i++; continue; } sum += i; i++; }", + "var sum = 0, i = 0; myLabel: while(i < 10) { if(i <= 5) { i++; continue myLabel; } sum += i; i++; }", ]; Tester::new(NoContinue::NAME, pass, fail).test_and_snapshot(); 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 0bc45bc18884..cd5a259f1269 100644 --- a/crates/oxc_linter/src/rules/eslint/no_control_regex.rs +++ b/crates/oxc_linter/src/rules/eslint/no_control_regex.rs @@ -3,23 +3,19 @@ use oxc_ast::{ ast::{Argument, RegExpFlags}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{Atom, CompactStr, GetSpan, Span}; +use oxc_span::{Atom, GetSpan, Span}; use regex::{Matches, Regex}; use crate::{ast_util::extract_regex_flags, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-control-regex): Unexpected control character(s)")] -#[diagnostic( - severity(warning), - help("Unexpected control character(s) in regular expression: \"{0}\"") -)] -struct NoControlRegexDiagnostic(CompactStr, #[label] pub Span); +fn no_control_regex_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-control-regex): Unexpected control character(s)") + .with_help(format!("Unexpected control character(s) in regular expression: \"{x0}\"")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoControlRegex; @@ -135,7 +131,7 @@ impl Rule for NoControlRegex { if !violations.is_empty() { let violations = violations.join(", "); - context.diagnostic(NoControlRegexDiagnostic(violations.into(), span)); + context.diagnostic(no_control_regex_diagnostic(&violations, span)); } } } @@ -291,7 +287,6 @@ mod tests { r"let r = new RegExp('\\u000c');", r"let r = new RegExp('\\u000C');", r"let r = new RegExp('\\u001f');", - ], ) .test(); diff --git a/crates/oxc_linter/src/rules/eslint/no_debugger.rs b/crates/oxc_linter/src/rules/eslint/no_debugger.rs index 6db9c9811a56..a9e2dc435b9f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_debugger.rs +++ b/crates/oxc_linter/src/rules/eslint/no_debugger.rs @@ -1,17 +1,15 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-debugger): `debugger` statement is not allowed")] -#[diagnostic(severity(warning))] -struct NoDebuggerDiagnostic(#[label] pub Span); +fn no_debugger_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-debugger): `debugger` statement is not allowed") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDebugger; @@ -37,7 +35,7 @@ declare_oxc_lint!( impl Rule for NoDebugger { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::DebuggerStatement(stmt) = node.kind() { - ctx.diagnostic_with_fix(NoDebuggerDiagnostic(stmt.span), || Fix::delete(stmt.span)); + ctx.diagnostic_with_fix(no_debugger_diagnostic(stmt.span), || Fix::delete(stmt.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_delete_var.rs b/crates/oxc_linter/src/rules/eslint/no_delete_var.rs index 46e3e31c0d7e..b63198e42d2b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_delete_var.rs +++ b/crates/oxc_linter/src/rules/eslint/no_delete_var.rs @@ -1,18 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::UnaryOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-delete-var): variables should not be deleted")] -#[diagnostic(severity(warning))] -struct NoDeleteVarDiagnostic(#[label] pub Span); +fn no_delete_var_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-delete-var): variables should not be deleted") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDeleteVar; @@ -37,9 +35,11 @@ declare_oxc_lint!( impl Rule for NoDeleteVar { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::UnaryExpression(expr) = node.kind() else { return }; + let AstKind::UnaryExpression(expr) = node.kind() else { + return; + }; if expr.operator == UnaryOperator::Delete && expr.argument.is_identifier_reference() { - ctx.diagnostic(NoDeleteVarDiagnostic(expr.span)); + ctx.diagnostic(no_delete_var_diagnostic(expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_dupe_class_members.rs b/crates/oxc_linter/src/rules/eslint/no_dupe_class_members.rs index 64044fbc0aa2..fa9831094cad 100644 --- a/crates/oxc_linter/src/rules/eslint/no_dupe_class_members.rs +++ b/crates/oxc_linter/src/rules/eslint/no_dupe_class_members.rs @@ -1,26 +1,20 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use rustc_hash::FxHashMap; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-dupe-class-members): Duplicate class member: {0:?}")] -#[diagnostic( - severity(warning), - help( - "The last declaration overwrites previous ones, remove one of them or rename if both should be retained" - ) -)] -struct NoDupeClassMembersDiagnostic( - CompactStr, /*Class member name */ - #[label("{0:?} is previously declared here")] pub Span, - #[label("{0:?} is re-declared here")] pub Span, -); +fn no_dupe_class_members_diagnostic( + x0: &str, /*Class member name */ + span1: Span, + span2: Span, +) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-dupe-class-members): Duplicate class member: {x0:?}")) + .with_help("The last declaration overwrites previous ones, remove one of them or rename if both should be retained") + .with_labels([LabeledSpan::new_with_span(Some(format!("{x0:?} is previously declared here")), span1), LabeledSpan::new_with_span(Some(format!("{x0:?} is re-declared here")), span2)]) +} #[derive(Debug, Default, Clone)] pub struct NoDupeClassMembers; @@ -60,8 +54,8 @@ impl Rule for NoDupeClassMembers { && prev_element.kind.is_setter_or_getter()) || element.kind == prev_element.kind) { - ctx.diagnostic(NoDupeClassMembersDiagnostic( - element.name.clone(), + ctx.diagnostic(no_dupe_class_members_diagnostic( + &element.name, prev_element.span, element.span, )); diff --git a/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs b/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs index 999b82148abd..502b0269d34f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs +++ b/crates/oxc_linter/src/rules/eslint/no_dupe_else_if.rs @@ -2,25 +2,19 @@ use oxc_ast::{ ast::{Expression, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::LogicalOperator; use crate::{ast_util::calculate_hash, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-dupe-else-if): duplicate conditions in if-else-if chains")] -#[diagnostic( - severity(warning), - help( - "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain" - ) -)] -struct NoDupeElseIfDiagnostic(#[label] pub Span, #[label] pub Span); +fn no_dupe_else_if_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-dupe-else-if): duplicate conditions in if-else-if chains") + .with_help("This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain") + .with_labels([span0.into(), span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDupeElseIf; @@ -55,11 +49,15 @@ impl Rule for NoDupeElseIf { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { // if (a) {} else if (a) {} // ^^ get this if statement - let AstKind::IfStatement(if_stmt) = node.kind() else { return }; + let AstKind::IfStatement(if_stmt) = node.kind() else { + return; + }; let Some(AstKind::IfStatement(parent_if_stmt)) = ctx.nodes().parent_kind(node.id()) else { return; }; - let Some(Statement::IfStatement(child_if_stmt)) = &parent_if_stmt.alternate else { return }; + let Some(Statement::IfStatement(child_if_stmt)) = &parent_if_stmt.alternate else { + return; + }; if child_if_stmt.span != if_stmt.span { return; } @@ -79,7 +77,9 @@ impl Rule for NoDupeElseIf { let mut current_node = node; while let Some(parent_node) = ctx.nodes().parent_node(current_node.id()) { - let AstKind::IfStatement(stmt) = parent_node.kind() else { break }; + let AstKind::IfStatement(stmt) = parent_node.kind() else { + break; + }; if !stmt .alternate @@ -109,7 +109,7 @@ impl Rule for NoDupeElseIf { .collect(); if list_to_check.iter().any(Vec::is_empty) { - ctx.diagnostic(NoDupeElseIfDiagnostic(if_stmt.test.span(), stmt.test.span())); + ctx.diagnostic(no_dupe_else_if_diagnostic(if_stmt.test.span(), stmt.test.span())); break; } } diff --git a/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs b/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs index 4b962f1182bf..134ee77ebeb1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs +++ b/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs @@ -2,20 +2,19 @@ use oxc_ast::{ ast::{ObjectPropertyKind, PropertyKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use rustc_hash::FxHashMap; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-dupe-keys): Disallow duplicate keys in object literals")] -#[diagnostic(severity(warning), help("Consider removing the duplicated key"))] -struct NoDupeKeysDiagnostic(#[label] pub Span, #[label] pub Span); +fn no_dupe_keys_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-dupe-keys): Disallow duplicate keys in object literals") + .with_help("Consider removing the duplicated key") + .with_labels([span0.into(), span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDupeKeys; @@ -42,17 +41,23 @@ declare_oxc_lint!( impl Rule for NoDupeKeys { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::ObjectExpression(obj_expr) = node.kind() else { return }; + let AstKind::ObjectExpression(obj_expr) = node.kind() else { + return; + }; let mut map = FxHashMap::default(); for prop in &obj_expr.properties { - let ObjectPropertyKind::ObjectProperty(prop) = prop else { continue }; - let Some(name) = prop.key.static_name() else { return }; + let ObjectPropertyKind::ObjectProperty(prop) = prop else { + continue; + }; + let Some(name) = prop.key.static_name() else { + return; + }; if let Some((prev_kind, prev_span)) = map.insert(name, (prop.kind, prop.key.span())) { if prev_kind == PropertyKind::Init || prop.kind == PropertyKind::Init || prev_kind == prop.kind { - ctx.diagnostic(NoDupeKeysDiagnostic(prev_span, prop.key.span())); + ctx.diagnostic(no_dupe_keys_diagnostic(prev_span, prop.key.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_duplicate_case.rs b/crates/oxc_linter/src/rules/eslint/no_duplicate_case.rs index f47de9cf6259..23b2006ff65a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_duplicate_case.rs +++ b/crates/oxc_linter/src/rules/eslint/no_duplicate_case.rs @@ -1,18 +1,17 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use rustc_hash::FxHashMap; use crate::{ast_util::calculate_hash, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-duplicate-case): Disallow duplicate case labels")] -#[diagnostic(severity(warning), help("Remove the duplicated case"))] -struct NoDuplicateCaseDiagnostic(#[label] pub Span, #[label] pub Span); +fn no_duplicate_case_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-duplicate-case): Disallow duplicate case labels") + .with_help("Remove the duplicated case") + .with_labels([span0.into(), span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDuplicateCase; @@ -55,7 +54,7 @@ impl Rule for NoDuplicateCase { let hash = calculate_hash(test); if let Some(prev_span) = map.insert(hash, test.span()) { - ctx.diagnostic(NoDuplicateCaseDiagnostic(prev_span, test.span())); + ctx.diagnostic(no_duplicate_case_diagnostic(prev_span, test.span())); } } } @@ -72,100 +71,34 @@ fn test() { ("var a = 1; switch (a) {case 1: break; case '1': break; default: break;}", None), ("var a = 1; switch (a) {case 1: break; case true: break; default: break;}", None), ("var a = 1; switch (a) {default: break;}", None), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p.p.p2: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(b) { return b ? { p1: 1 } : { p1: 2 }; }; switch (a) {case f(true).p1: break; case f(true, false).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a + 2).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a == 1 ? 2 : 3).p1: break; case f(a === 1 ? 2 : 3).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f1 = function() { return { p1: 1 } }, f2 = function() { return { p1: 2 } }; switch (a) {case f1().p1: break; case f2().p1: break; default: break;}", - None, - ), - ( - "var a = [1,2]; switch(a.toString()){case ([1,2]).toString():break; case ([1]).toString():break; default:break;}", - None, - ), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p.p.p2: break; default: break;}", None), + ("var a = 1, f = function(b) { return b ? { p1: 1 } : { p1: 2 }; }; switch (a) {case f(true).p1: break; case f(true, false).p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a + 2).p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a == 1 ? 2 : 3).p1: break; case f(a === 1 ? 2 : 3).p1: break; default: break;}", None), + ("var a = 1, f1 = function() { return { p1: 1 } }, f2 = function() { return { p1: 2 } }; switch (a) {case f1().p1: break; case f2().p1: break; default: break;}", None), + ("var a = [1,2]; switch(a.toString()){case ([1,2]).toString():break; case ([1]).toString():break; default:break;}", None), ("switch(a) { case a: break; } switch(a) { case a: break; }", None), ("switch(a) { case toString: break; }", None), ]; let fail = vec![ - ( - "var a = 1; switch (a) {case 1: break; case 1: break; case 2: break; default: break;}", - None, - ), - ( - "var a = '1'; switch (a) {case '1': break; case '1': break; case '2': break; default: break;}", - None, - ), - ( - "var a = 1, one = 1; switch (a) {case one: break; case one: break; case 2: break; default: break;}", - None, - ), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p.p.p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(b) { return b ? { p1: 1 } : { p1: 2 }; }; switch (a) {case f(true).p1: break; case f(true).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a + 1).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a === 1 ? 2 : 3).p1: break; case f(a === 1 ? 2 : 3).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f1 = function() { return { p1: 1 } }; switch (a) {case f1().p1: break; case f1().p1: break; default: break;}", - None, - ), - ( - "var a = [1, 2]; switch(a.toString()){case ([1, 2]).toString():break; case ([1, 2]).toString():break; default:break;}", - None, - ), + ("var a = 1; switch (a) {case 1: break; case 1: break; case 2: break; default: break;}", None), + ("var a = '1'; switch (a) {case '1': break; case '1': break; case '2': break; default: break;}", None), + ("var a = 1, one = 1; switch (a) {case one: break; case one: break; case 2: break; default: break;}", None), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p.p.p1: break; default: break;}", None), + ("var a = 1, f = function(b) { return b ? { p1: 1 } : { p1: 2 }; }; switch (a) {case f(true).p1: break; case f(true).p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a + 1).p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a === 1 ? 2 : 3).p1: break; case f(a === 1 ? 2 : 3).p1: break; default: break;}", None), + ("var a = 1, f1 = function() { return { p1: 1 } }; switch (a) {case f1().p1: break; case f1().p1: break; default: break;}", None), + ("var a = [1, 2]; switch(a.toString()){case ([1, 2]).toString():break; case ([1, 2]).toString():break; default:break;}", None), ("switch (a) { case a: case a: }", None), - ( - "switch (a) { case a: break; case b: break; case a: break; case c: break; case a: break; }", - None, - ), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p. p // comment\n .p1: break; default: break;}", - None, - ), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p .p\n/* comment */\n.p1: break; case p.p.p1: break; default: break;}", - None, - ), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p .p\n/* comment */\n.p1: break; case p. p // comment\n .p1: break; default: break;}", - None, - ), - ( - "var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p. p // comment\n .p1: break; case p .p\n/* comment */\n.p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a+1).p1: break; default: break;}", - None, - ), - ( - "var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(\na + 1 // comment\n).p1: break; case f(a+1)\n.p1: break; default: break;}", - None, - ), + ("switch (a) { case a: break; case b: break; case a: break; case c: break; case a: break; }", None), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p. p // comment\n .p1: break; default: break;}", None), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p .p\n/* comment */\n.p1: break; case p.p.p1: break; default: break;}", None), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p .p\n/* comment */\n.p1: break; case p. p // comment\n .p1: break; default: break;}", None), + ("var a = 1, p = {p: {p1: 1, p2: 1}}; switch (a) {case p.p.p1: break; case p. p // comment\n .p1: break; case p .p\n/* comment */\n.p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(a + 1).p1: break; case f(a+1).p1: break; default: break;}", None), + ("var a = 1, f = function(s) { return { p1: s } }; switch (a) {case f(\na + 1 // comment\n).p1: break; case f(a+1)\n.p1: break; default: break;}", None), ]; Tester::new(NoDuplicateCase::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/eslint/no_empty.rs b/crates/oxc_linter/src/rules/eslint/no_empty.rs index f1a34fc7f283..f2be97735692 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-empty): Disallow empty block statements")] -#[diagnostic(severity(warning), help("Add comment inside empty {0} statement"))] -struct NoEmptyDiagnostic(&'static str, #[label("Empty {0} statement")] pub Span); +fn no_empty_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-empty): Disallow empty block statements") + .with_help(format!("Add comment inside empty {x0} statement")) + .with_labels([LabeledSpan::new_with_span(Some(format!("Empty {x0} statement")), span1)]) +} #[derive(Debug, Default, Clone)] pub struct NoEmpty { @@ -53,7 +52,7 @@ impl Rule for NoEmpty { if ctx.semantic().trivias().has_comments_between(block.span) { return; } - ctx.diagnostic(NoEmptyDiagnostic("block", block.span)); + ctx.diagnostic(no_empty_diagnostic("block", block.span)); } // The visitor does not visit the `BlockStatement` inside the `CatchClause`. // See `Visit::visit_catch_clause`. @@ -63,7 +62,7 @@ impl Rule for NoEmpty { if ctx.semantic().trivias().has_comments_between(catch_clause.body.span) { return; } - ctx.diagnostic(NoEmptyDiagnostic("block", catch_clause.body.span)); + ctx.diagnostic(no_empty_diagnostic("block", catch_clause.body.span)); } // The visitor does not visit the `BlockStatement` inside the `FinallyClause`. // See `Visit::visit_finally_clause`. @@ -71,10 +70,10 @@ impl Rule for NoEmpty { if ctx.semantic().trivias().has_comments_between(finally_clause.span) { return; } - ctx.diagnostic(NoEmptyDiagnostic("block", finally_clause.span)); + ctx.diagnostic(no_empty_diagnostic("block", finally_clause.span)); } AstKind::SwitchStatement(switch) if switch.cases.is_empty() => { - ctx.diagnostic(NoEmptyDiagnostic("switch", switch.span)); + ctx.diagnostic(no_empty_diagnostic("switch", switch.span)); } _ => {} } diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs b/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs index 8c85d3d6b433..c2a3d6a63777 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs @@ -1,20 +1,19 @@ +use oxc_diagnostics::OxcDiagnostic; + // Ported from https://github.com/eslint/eslint/blob/main/lib/rules/no-empty-character-class.js use lazy_static::lazy_static; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use regex::Regex; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-empty-character-class): Empty character class")] -#[diagnostic(severity(warning), help("Try to remove empty character class `[]` in regexp literal"))] -struct NoEmptyCharacterClassDiagnostic(#[label] pub Span); +fn no_empty_character_class_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-empty-character-class): Empty character class") + .with_help("Try to remove empty character class `[]` in regexp literal") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoEmptyCharacterClass; @@ -52,7 +51,7 @@ impl Rule for NoEmptyCharacterClass { if let AstKind::RegExpLiteral(lit) = node.kind() { if !NO_EMPTY_CLASS_REGEX_PATTERN.is_match(&lit.regex.pattern) { - ctx.diagnostic(NoEmptyCharacterClassDiagnostic(lit.span)); + ctx.diagnostic(no_empty_character_class_diagnostic(lit.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_function.rs b/crates/oxc_linter/src/rules/eslint/no_empty_function.rs index 73b39e1d5f66..a6209a5b6c26 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_function.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_function.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-empty-function): Disallow empty functions")] -#[diagnostic(severity(warning), help("Unexpected empty function block"))] -struct NoEmptyFunctionDiagnostic(#[label] pub Span); +fn no_empty_function_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-empty-function): Disallow empty functions") + .with_help("Unexpected empty function block") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoEmptyFunction; @@ -41,7 +40,7 @@ impl Rule for NoEmptyFunction { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::FunctionBody(fb) = node.kind() { if fb.is_empty() && !ctx.semantic().trivias().has_comments_between(fb.span) { - ctx.diagnostic(NoEmptyFunctionDiagnostic(fb.span)); + ctx.diagnostic(no_empty_function_diagnostic(fb.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_pattern.rs b/crates/oxc_linter/src/rules/eslint/no_empty_pattern.rs index 42c36adafa5d..b0a61bb4e91f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_pattern.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_pattern.rs @@ -1,20 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-empty-pattern): Disallow empty destructuring patterns.")] -#[diagnostic( - severity(warning), - help("Passing `null` or `undefined` will result in runtime error because `null` and `undefined` cannot be destructured.") -)] -struct NoEmptyPatternDiagnostic(&'static str, #[label("Empty {0} binding pattern")] pub Span); +fn no_empty_pattern_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-empty-pattern): Disallow empty destructuring patterns.") + .with_help("Passing `null` or `undefined` will result in runtime error because `null` and `undefined` cannot be destructured.") + .with_labels([LabeledSpan::new_with_span(Some(format!("Empty {x0} binding pattern")), span1)]) +} #[derive(Debug, Default, Clone)] pub struct NoEmptyPattern; @@ -84,7 +80,7 @@ impl Rule for NoEmptyPattern { AstKind::ObjectPattern(object) if object.is_empty() => ("object", object.span), _ => return, }; - ctx.diagnostic(NoEmptyPatternDiagnostic(pattern_type, span)); + ctx.diagnostic(no_empty_pattern_diagnostic(pattern_type, span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_static_block.rs b/crates/oxc_linter/src/rules/eslint/no_empty_static_block.rs index 93b14f0ede8c..1a5530518d97 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_static_block.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_static_block.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-empty-static-block): Disallow empty static blocks")] -#[diagnostic(severity(warning), help("Unexpected empty static block."))] -struct NoEmptyStaticBlockDiagnostic(#[label] pub Span); +fn no_empty_static_block_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-empty-static-block): Disallow empty static blocks") + .with_help("Unexpected empty static block.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoEmptyStaticBlock; @@ -44,7 +43,7 @@ impl Rule for NoEmptyStaticBlock { if ctx.semantic().trivias().has_comments_between(static_block.span) { return; } - ctx.diagnostic(NoEmptyStaticBlockDiagnostic(static_block.span)); + ctx.diagnostic(no_empty_static_block_diagnostic(static_block.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_eq_null.rs b/crates/oxc_linter/src/rules/eslint/no_eq_null.rs index 7868abd54bc7..80408e36e243 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eq_null.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eq_null.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::BinaryOperator; @@ -10,13 +8,11 @@ use std::fmt::Debug; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-eq-null): Use '===' to compare with null")] -#[diagnostic( - severity(warning), - help("Disallow `null` comparisons without type-checking operators.") -)] -struct NoEqNullDiagnostic(#[label] pub Span); +fn no_eq_null_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-eq-null): Use '===' to compare with null") + .with_help("Disallow `null` comparisons without type-checking operators.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoEqNull; @@ -53,7 +49,7 @@ impl Rule for NoEqNull { & binary_expression.left.is_null() & bad_operator { - ctx.diagnostic(NoEqNullDiagnostic(Span::new( + ctx.diagnostic(no_eq_null_diagnostic(Span::new( binary_expression.span.start, binary_expression.span.end, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index 10ad075b0257..ba5f1df04e44 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -1,20 +1,17 @@ +use oxc_diagnostics::OxcDiagnostic; + // Ported from https://github.com/eslint/eslint/tree/main/lib/rules/no-eval.js use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNode; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-eval): eval can be harmful.")] -#[diagnostic(severity(warning))] -struct NoEvalDiagnostic(#[label] pub Span); +fn no_eval_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-eval): eval can be harmful.").with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoEval { @@ -66,7 +63,7 @@ impl Rule for NoEval { if let AstKind::IdentifierReference(ident) = kind { if ident.name == "eval" { - ctx.diagnostic(NoEvalDiagnostic(ident.span)); + ctx.diagnostic(no_eval_diagnostic(ident.span)); } return; } @@ -116,7 +113,7 @@ impl Rule for NoEval { }; } - ctx.diagnostic(NoEvalDiagnostic(eval_span)); + ctx.diagnostic(no_eval_diagnostic(eval_span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs b/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs index 8a74c706dafc..9574d785e370 100644 --- a/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs @@ -1,17 +1,14 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-ex-assign): Do not assign to the exception parameter.")] -#[diagnostic(severity(warning), help("If a catch clause in a try statement accidentally (or purposely) assigns another value to the exception parameter, it is impossible to refer to the error from that point on. Since there is no arguments object to offer alternative access to this data, assignment of the parameter is absolutely destructive."))] -struct NoExAssignDiagnostic(#[label] pub Span); +fn no_ex_assign_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-ex-assign): Do not assign to the exception parameter.").with_help("If a catch clause in a try statement accidentally (or purposely) assigns another value to the exception parameter, it is impossible to refer to the error from that point on. Since there is no arguments object to offer alternative access to this data, assignment of the parameter is absolutely destructive.").with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoExAssign; @@ -45,7 +42,7 @@ impl Rule for NoExAssign { if symbol_table.get_flag(symbol_id).is_catch_variable() { for reference in symbol_table.get_resolved_references(symbol_id) { if reference.is_write() { - ctx.diagnostic(NoExAssignDiagnostic(reference.span())); + ctx.diagnostic(no_ex_assign_diagnostic(reference.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs b/crates/oxc_linter/src/rules/eslint/no_extra_boolean_cast.rs index d1ce266b882f..4896ae31deb2 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,30 +1,24 @@ use itertools::Itertools; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::{LogicalOperator, UnaryOperator}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-extra-boolean-cast): Redundant double negation")] -#[diagnostic( - severity(warning), - help("Remove the double negation as it will already be coerced to a boolean") -)] -struct NoExtraDoubleNegationCastDiagnostic(#[label] pub Span); +fn no_extra_double_negation_cast_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-extra-boolean-cast): Redundant double negation") + .with_help("Remove the double negation as it will already be coerced to a boolean") + .with_labels([span0.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-extra-boolean-cast): Redundant Boolean call")] -#[diagnostic( - severity(warning), - help("Remove the Boolean call as it will already be coerced to a boolean") -)] -struct NoExtraBooleanCastDiagnostic(#[label] pub Span); +fn no_extra_boolean_cast_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-extra-boolean-cast): Redundant Boolean call") + .with_help("Remove the Boolean call as it will already be coerced to a boolean") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoExtraBooleanCast { @@ -71,13 +65,15 @@ impl Rule for NoExtraBooleanCast { if expr.callee.is_specific_id("Boolean") && is_flagged_ctx(node, ctx, self.enforce_for_logical_operands) => { - ctx.diagnostic(NoExtraBooleanCastDiagnostic(expr.span)); + ctx.diagnostic(no_extra_boolean_cast_diagnostic(expr.span)); } AstKind::UnaryExpression(unary) if unary.operator == UnaryOperator::LogicalNot => { - let Some(parent) = get_real_parent(node, ctx) else { return }; + let Some(parent) = get_real_parent(node, ctx) else { + return; + }; if matches!(parent.kind(), AstKind::UnaryExpression(p) if p.operator == UnaryOperator::LogicalNot && is_flagged_ctx(parent, ctx, self.enforce_for_logical_operands)) { - ctx.diagnostic(NoExtraDoubleNegationCastDiagnostic(parent.kind().span())); + ctx.diagnostic(no_extra_double_negation_cast_diagnostic(parent.kind().span())); } } _ => {} diff --git a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs index 356c57823b84..3cd4fa97b89c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs +++ b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs @@ -1,7 +1,3 @@ -// use oxc_diagnostics::{ -// miette::{self, Diagnostic}, -// thiserror::Error, -// }; use oxc_macros::declare_oxc_lint; // use oxc_span::Span; @@ -70,10 +66,7 @@ fn test() { ("function foo() { switch(foo) { case 0: {return;}\n case 1: {return;} } }", None), ("switch(foo) { case 0: case 1: {break;} }", None), ("switch(foo) { }", None), - ( - "switch(foo) { case 0: switch(bar) { case 2: break; } /* falls through */ case 1: break; }", - None, - ), + ("switch(foo) { case 0: switch(bar) { case 2: break; } /* falls through */ case 1: break; }", None), ("function foo() { switch(foo) { case 1: return a; a++; }}", None), ("switch (foo) { case 0: a(); /* falls through */ default: b(); /* comment */ }", None), ("switch (foo) { case 0: a(); /* falls through */ default: /* comment */ b(); }", None), @@ -82,10 +75,7 @@ fn test() { ("switch (foo) { case 0: try {} finally { break; } default: b(); }", None), ("switch (foo) { case 0: try { throw 0; } catch (err) { break; } default: b(); }", None), ("switch (foo) { case 0: do { throw 0; } while(a); default: b(); }", None), - ( - "switch (foo) { case 0: a(); \n// eslint-disable-next-line no-fallthrough\n case 1: }", - None, - ), + ("switch (foo) { case 0: a(); \n// eslint-disable-next-line no-fallthrough\n case 1: }", None), ( "switch(foo) { case 0: a(); /* no break */ case 1: b(); }", Some(serde_json::json!([{ @@ -116,22 +106,10 @@ fn test() { "commentPattern": "break[\\s\\w]+omitted" }])), ), - ( - "switch(foo) { case 0: \n\n\n case 1: b(); }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch(foo) { case 0: \n /* with comments */ \n case 1: b(); }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch (a) {\n case 1: ; break; \n case 3: }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch (a) {\n case 1: ; break; \n case 3: }", - Some(serde_json::json!([{ "allowEmptyCase": false }])), - ), + ("switch(foo) { case 0: \n\n\n case 1: b(); }", Some(serde_json::json!([{ "allowEmptyCase": true }]))), + ("switch(foo) { case 0: \n /* with comments */ \n case 1: b(); }", Some(serde_json::json!([{ "allowEmptyCase": true }]))), + ("switch (a) {\n case 1: ; break; \n case 3: }", Some(serde_json::json!([{ "allowEmptyCase": true }]))), + ("switch (a) {\n case 1: ; break; \n case 3: }", Some(serde_json::json!([{ "allowEmptyCase": false }]))), ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/eslint/no_func_assign.rs b/crates/oxc_linter/src/rules/eslint/no_func_assign.rs index 8db5d9688df7..7e68cc475c5f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_func_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_func_assign.rs @@ -1,18 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-func-assign): '{0}' is a function.")] -#[diagnostic(severity(warning))] -struct NoFuncAssignDiagnostic(CompactStr, #[label("{0} is re-assigned here")] pub Span); +fn no_func_assign_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-func-assign): '{x0}' is a function.")) + .with_labels([LabeledSpan::new_with_span(Some(format!("{x0} is re-assigned here")), span1)]) +} #[derive(Debug, Default, Clone)] pub struct NoFuncAssign; @@ -41,8 +39,8 @@ impl Rule for NoFuncAssign { if let AstKind::Function(_) = ctx.nodes().kind(decl) { for reference in symbol_table.get_resolved_references(symbol_id) { if reference.is_write() { - ctx.diagnostic(NoFuncAssignDiagnostic( - symbol_table.get_name(symbol_id).into(), + ctx.diagnostic(no_func_assign_diagnostic( + symbol_table.get_name(symbol_id), reference.span(), )); } diff --git a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs index 79a3e1b67bca..c972f9ee2108 100644 --- a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs @@ -1,19 +1,19 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-global-assign): Read-only global '{0}' should not be modified.")] -#[diagnostic(severity(warning))] -struct NoGlobalAssignDiagnostic( - CompactStr, - #[label("Read-only global '{0}' should not be modified.")] pub Span, -); +fn no_global_assign_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-global-assign): Read-only global '{x0}' should not be modified." + )) + .with_labels([LabeledSpan::new_with_span( + Some(format!("Read-only global '{x0}' should not be modified.")), + span1, + )]) +} #[derive(Debug, Default, Clone)] pub struct NoGlobalAssign(Box); @@ -72,7 +72,7 @@ impl Rule for NoGlobalAssign { let name = reference.name(); if !self.excludes.contains(name) && ctx.env_contains_var(name) { - ctx.diagnostic(NoGlobalAssignDiagnostic(name.clone(), reference.span())); + ctx.diagnostic(no_global_assign_diagnostic(name, reference.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs index 9b096d2bb6b2..ceacc42dfad8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNodeId, SymbolId}; use oxc_span::{GetSpan, Span}; @@ -11,10 +9,11 @@ use phf::phf_set; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-import-assign): do not assign to imported bindings")] -#[diagnostic(severity(warning), help("imported bindings are readonly"))] -struct NoImportAssignDiagnostic(#[label] pub Span); +fn no_import_assign_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-import-assign): do not assign to imported bindings") + .with_help("imported bindings are readonly") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoImportAssign; @@ -77,7 +76,8 @@ impl Rule for NoImportAssign { { if let Some((span, _)) = expr.static_property_info() { if span != reference.span() { - return ctx.diagnostic(NoImportAssignDiagnostic(expr.span())); + return ctx + .diagnostic(no_import_assign_diagnostic(expr.span())); } } } @@ -88,7 +88,7 @@ impl Rule for NoImportAssign { || (is_namespace_specifier && is_argument_of_well_known_mutation_function(reference.node_id(), ctx)) { - ctx.diagnostic(NoImportAssignDiagnostic(reference.span())); + ctx.diagnostic(no_import_assign_diagnostic(reference.span())); } } } @@ -110,9 +110,13 @@ fn is_argument_of_well_known_mutation_function(node_id: AstNodeId, ctx: &LintCon let call_expression_node = ctx.nodes().parent_node(node_id).and_then(|node| ctx.nodes().parent_kind(node.id())); - let Some(AstKind::CallExpression(expr)) = call_expression_node else { return false }; + let Some(AstKind::CallExpression(expr)) = call_expression_node else { + return false; + }; - let Some(member_expr) = &expr.callee.get_member_expr() else { return false }; + let Some(member_expr) = &expr.callee.get_member_expr() else { + return false; + }; if let Expression::Identifier(ident) = member_expr.object() { let Some(property_name) = member_expr.static_property_name() else { diff --git a/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs b/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs index 329296095243..2a3219f4e99f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs @@ -1,19 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks" -)] -#[diagnostic(severity(warning), help("Move {0} declaration to {1} root"))] -struct NoInnerDeclarationsDiagnostic(&'static str, &'static str, #[label] pub Span); +fn no_inner_declarations_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks") + .with_help(format!("Move {x0} declaration to {x1} root")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoInnerDeclarations { @@ -123,7 +120,7 @@ impl Rule for NoInnerDeclarations { } } - ctx.diagnostic(NoInnerDeclarationsDiagnostic(decl_type, body, span)); + ctx.diagnostic(no_inner_declarations_diagnostic(decl_type, body, span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_irregular_whitespace.rs b/crates/oxc_linter/src/rules/eslint/no_irregular_whitespace.rs index 019c80ad9016..61b7ec61cd25 100644 --- a/crates/oxc_linter/src/rules/eslint/no_irregular_whitespace.rs +++ b/crates/oxc_linter/src/rules/eslint/no_irregular_whitespace.rs @@ -1,16 +1,15 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-irregular-whitespace): Unexpected irregular whitespace")] -#[diagnostic(severity(warning), help("Try to remove the irregular whitespace"))] -struct NoIrregularWhitespaceDiagnostic(#[label] pub Span); +fn no_irregular_whitespace_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-irregular-whitespace): Unexpected irregular whitespace") + .with_help("Try to remove the irregular whitespace") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoIrregularWhitespace; @@ -37,7 +36,7 @@ impl Rule for NoIrregularWhitespace { fn run_once(&self, ctx: &LintContext) { let irregular_whitespaces = ctx.semantic().trivias().irregular_whitespaces(); for irregular_whitespace in irregular_whitespaces { - ctx.diagnostic(NoIrregularWhitespaceDiagnostic(*irregular_whitespace)); + ctx.diagnostic(no_irregular_whitespace_diagnostic(*irregular_whitespace)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_iterator.rs b/crates/oxc_linter/src/rules/eslint/no_iterator.rs index 91a67b92088d..270b6d0d3e3c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_iterator.rs +++ b/crates/oxc_linter/src/rules/eslint/no_iterator.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-iterator): Reserved name '__iterator__'")] -#[diagnostic(severity(warning), help("Disallow the use of the `__iterator__` property."))] -struct NoIteratorDiagnostic(#[label] pub Span); +fn no_iterator_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-iterator): Reserved name '__iterator__'") + .with_help("Disallow the use of the `__iterator__` property.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoIterator; @@ -35,10 +34,12 @@ declare_oxc_lint!( impl Rule for NoIterator { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expression) = node.kind() else { return }; + let AstKind::MemberExpression(member_expression) = node.kind() else { + return; + }; if let Some(static_property_name) = member_expression.static_property_name() { if static_property_name == "__iterator__" { - ctx.diagnostic(NoIteratorDiagnostic(Span::new( + ctx.diagnostic(no_iterator_diagnostic(Span::new( member_expression.span().start, member_expression.span().end, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs b/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs index ef39f2fb6191..9c50c7759fe7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs +++ b/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs @@ -1,19 +1,19 @@ use oxc_ast::ast::NumericLiteral; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use std::borrow::Cow; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-loss-of-precision): This number literal will lose precision at runtime.")] -#[diagnostic(severity(warning))] -struct NoLossOfPrecisionDiagnostic(#[label] pub Span); +fn no_loss_of_precision_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-loss-of-precision): This number literal will lose precision at runtime.", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoLossOfPrecision; @@ -41,7 +41,7 @@ impl Rule for NoLossOfPrecision { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { AstKind::NumericLiteral(node) if Self::lose_precision(node) => { - ctx.diagnostic(NoLossOfPrecisionDiagnostic(node.span)); + ctx.diagnostic(no_loss_of_precision_diagnostic(node.span)); } _ => {} } @@ -211,7 +211,9 @@ impl NoLossOfPrecision { fn base_ten_loses_precision(node: &'_ NumericLiteral) -> bool { let raw = Self::get_raw(node); - let Some(raw) = Self::normalize(&raw) else { return true }; + let Some(raw) = Self::normalize(&raw) else { + return true; + }; if raw.frac.len() >= 100 { return true; @@ -221,7 +223,9 @@ impl NoLossOfPrecision { (false, 0) => node.value.to_string(), (false, precision) => format!("{:.1$}", node.value, precision), }; - let Some(stored) = Self::normalize(&stored) else { return true }; + let Some(stored) = Self::normalize(&stored) else { + return true; + }; raw != stored } diff --git a/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs b/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs index 877b81e856bb..c806cc640e57 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs @@ -1,17 +1,17 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-new-native-nonconstructor): `{0}` cannot be called as a constructor.")] -#[diagnostic(severity(warning))] -struct NoNewNativeNonconstructorDiagnostic(CompactStr, #[label] pub Span); +fn no_new_native_nonconstructor_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-new-native-nonconstructor): `{x0}` cannot be called as a constructor." + )) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoNewNativeNonconstructor; @@ -40,15 +40,19 @@ declare_oxc_lint!( impl Rule for NoNewNativeNonconstructor { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::NewExpression(expr) = node.kind() else { return }; - let Expression::Identifier(ident) = &expr.callee else { return }; + let AstKind::NewExpression(expr) = node.kind() else { + return; + }; + let Expression::Identifier(ident) = &expr.callee else { + return; + }; if matches!(ident.name.as_str(), "Symbol" | "BigInt") && ctx.semantic().is_reference_to_global_variable(ident) { let start = expr.span.start; let end = start + 3; - ctx.diagnostic(NoNewNativeNonconstructorDiagnostic( - ident.name.to_compact_str(), + ctx.diagnostic(no_new_native_nonconstructor_diagnostic( + ident.name.as_str(), Span::new(start, end), )); } diff --git a/crates/oxc_linter/src/rules/eslint/no_new_wrappers.rs b/crates/oxc_linter/src/rules/eslint/no_new_wrappers.rs index 6e45a329af9b..26ceb8fd51b9 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new_wrappers.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new_wrappers.rs @@ -1,22 +1,16 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects" -)] -#[diagnostic( - severity(warning), - help("do not use {0} as a constructor, consider removing the new operator.") -)] -struct NoNewWrappersDiagnostic(CompactStr, #[label] pub Span); +fn no_new_wrappers_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects") + .with_help(format!("do not use {x0} as a constructor, consider removing the new operator.")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoNewWrappers; @@ -42,12 +36,16 @@ declare_oxc_lint!( impl Rule for NoNewWrappers { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::NewExpression(expr) = node.kind() else { return }; - let Expression::Identifier(ident) = &expr.callee else { return }; + let AstKind::NewExpression(expr) = node.kind() else { + return; + }; + let Expression::Identifier(ident) = &expr.callee else { + return; + }; if (ident.name == "String" || ident.name == "Number" || ident.name == "Boolean") && ctx.semantic().is_reference_to_global_variable(ident) { - ctx.diagnostic(NoNewWrappersDiagnostic(ident.name.to_compact_str(), expr.span)); + ctx.diagnostic(no_new_wrappers_diagnostic(ident.name.as_str(), expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_nonoctal_decimal_escape.rs b/crates/oxc_linter/src/rules/eslint/no_nonoctal_decimal_escape.rs index fa8e18e84ad3..07d5573f4100 100644 --- a/crates/oxc_linter/src/rules/eslint/no_nonoctal_decimal_escape.rs +++ b/crates/oxc_linter/src/rules/eslint/no_nonoctal_decimal_escape.rs @@ -1,30 +1,26 @@ use lazy_static::lazy_static; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use regex::{Captures, Regex}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum NoNonoctalDecimalEscapeDiagnostic { - #[error("eslint(no-nonoctal-decimal-escape): Don't use '{0}' escape sequence.")] - #[diagnostic( - severity(warning), - help("Replace '{0}' with '{1}'. This maintains the current functionality.") - )] - Replacement(String, String, #[label] Span), - - #[error("eslint(no-nonoctal-decimal-escape): Don't use '{0}' escape sequence.")] - #[diagnostic( - severity(warning), - help("Replace '{0}' with '{1}' to include the actual backslash character.") - )] - EscapeBackslash(String, String, #[label] Span), +fn replacement(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-nonoctal-decimal-escape): Don't use '{x0}' escape sequence." + )) + .with_help(format!("Replace '{x0}' with '{x1}'. This maintains the current functionality.")) + .with_labels([span2.into()]) +} + +fn escape_backslash(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-nonoctal-decimal-escape): Don't use '{x0}' escape sequence." + )) + .with_help(format!("Replace '{x0}' with '{x1}' to include the actual backslash character.")) + .with_labels([span2.into()]) } #[derive(Debug, Default, Clone)] @@ -115,28 +111,24 @@ fn check_string(ctx: &LintContext<'_>, string: &str) { if let Some(prev_match) = previous_escape { if prev_match.as_str().eq("\\0") { - ctx.diagnostic(NoNonoctalDecimalEscapeDiagnostic::Replacement( - prev_match.as_str().to_string() + decimal_escape_str, - "\\u00008".to_string(), + ctx.diagnostic(replacement( + &(prev_match.as_str().to_string() + decimal_escape_str), + "\\u00008", Span::new(prev_match.start() as u32, decimal_escape_span.end), )); - ctx.diagnostic(NoNonoctalDecimalEscapeDiagnostic::Replacement( - decimal_escape_str.to_string(), - "\\u0038".to_string(), - decimal_escape_span, - )); + ctx.diagnostic(replacement(decimal_escape_str, "\\u0038", decimal_escape_span)); } } else { - ctx.diagnostic(NoNonoctalDecimalEscapeDiagnostic::Replacement( - decimal_escape_str.to_string(), - decimal_escape_str[1..].to_string(), + ctx.diagnostic(replacement( + decimal_escape_str, + &decimal_escape_str[1..], decimal_escape_span, )); } - ctx.diagnostic(NoNonoctalDecimalEscapeDiagnostic::EscapeBackslash( - decimal_escape_str.to_string(), - format!("\\{decimal_escape_str}"), + ctx.diagnostic(escape_backslash( + decimal_escape_str, + &format!("\\{decimal_escape_str}"), decimal_escape_span, )); 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 bcce8e1a73c4..1d5bdc32583b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs +++ b/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs @@ -2,23 +2,24 @@ use oxc_ast::{ ast::{match_member_expression, Expression, IdentifierReference, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, ScopeId}; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; const GLOBAL_THIS: &str = "globalThis"; const NON_CALLABLE_GLOBALS: [&str; 5] = ["Atomics", "Intl", "JSON", "Math", "Reflect"]; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-obj-calls): Disallow calling some global objects as functions")] -#[diagnostic(severity(warning), help("{0} is not a function."))] -struct NoObjCallsDiagnostic(CompactStr, #[label] pub Span); +fn no_obj_calls_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-obj-calls): Disallow calling some global objects as functions", + ) + .with_help(format!("{x0} is not a function.")) + .with_labels([span1.into()]) +} #[derive(Debug, Clone, PartialEq, Eq)] pub struct NoObjCalls; @@ -139,7 +140,7 @@ impl Rule for NoObjCalls { resolve_global_binding(ident, node.scope_id(), ctx) { if is_global_obj(top_level_reference) { - ctx.diagnostic(NoObjCallsDiagnostic(ident.name.to_compact_str(), span)); + ctx.diagnostic(no_obj_calls_diagnostic(ident.name.as_str(), span)); } } } @@ -148,7 +149,7 @@ impl Rule for NoObjCalls { // handle new globalThis.Math(), globalThis.Math(), etc 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)); + ctx.diagnostic(no_obj_calls_diagnostic(global_member, span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_proto.rs b/crates/oxc_linter/src/rules/eslint/no_proto.rs index b5cd4c8a78a3..a67992aebb4d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_proto.rs +++ b/crates/oxc_linter/src/rules/eslint/no_proto.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-proto): The '__proto__' property is deprecated")] -#[diagnostic(severity(warning), help("Disallow the use of the `__proto__` property."))] -struct NoProtoDiagnostic(#[label] pub Span); +fn no_proto_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-proto): The '__proto__' property is deprecated") + .with_help("Disallow the use of the `__proto__` property.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoProto; @@ -41,10 +40,12 @@ declare_oxc_lint!( impl Rule for NoProto { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expression) = node.kind() else { return }; + let AstKind::MemberExpression(member_expression) = node.kind() else { + return; + }; if let Some(static_property_name) = member_expression.static_property_name() { if static_property_name == "__proto__" { - ctx.diagnostic(NoProtoDiagnostic(Span::new( + ctx.diagnostic(no_proto_diagnostic(Span::new( member_expression.span().start, member_expression.span().end, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_prototype_builtins.rs b/crates/oxc_linter/src/rules/eslint/no_prototype_builtins.rs index c9e7fb5a724e..d4ef4cad328b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_prototype_builtins.rs +++ b/crates/oxc_linter/src/rules/eslint/no_prototype_builtins.rs @@ -1,22 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint(no-prototype-builtins): do not access Object.prototype method {0:?} from target object" -)] -#[diagnostic( - severity(warning), - help("to avoid prototype pollution, use `Object.prototype.{0}.call` instead") -)] -struct NoPrototypeBuiltinsDiagnostic(String, #[label] pub Span); +fn no_prototype_builtins_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-prototype-builtins): do not access Object.prototype method {x0:?} from target object")) + .with_help(format!("to avoid prototype pollution, use `Object.prototype.{x0}.call` instead")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoPrototypeBuiltins; @@ -51,14 +45,17 @@ const DISALLOWED_PROPS: &[&str; 3] = &["hasOwnProperty", "isPrototypeOf", "prope impl Rule for NoPrototypeBuiltins { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(expr) = node.kind() else { return }; - let Some(member_expr) = expr.callee.get_member_expr() else { return }; - let Some(prop_name) = member_expr.static_property_name() else { return }; + let AstKind::CallExpression(expr) = node.kind() else { + return; + }; + let Some(member_expr) = expr.callee.get_member_expr() else { + return; + }; + let Some(prop_name) = member_expr.static_property_name() else { + return; + }; if DISALLOWED_PROPS.contains(&prop_name) { - ctx.diagnostic(NoPrototypeBuiltinsDiagnostic( - prop_name.to_string(), - member_expr.span(), - )); + ctx.diagnostic(no_prototype_builtins_diagnostic(prop_name, member_expr.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_redeclare.rs b/crates/oxc_linter/src/rules/eslint/no_redeclare.rs index 70ece4bd66ec..7c74a78ee73e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_redeclare.rs +++ b/crates/oxc_linter/src/rules/eslint/no_redeclare.rs @@ -2,31 +2,31 @@ use oxc_ast::{ ast::{BindingIdentifier, BindingPatternKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-redeclare): '{0}' is already defined.")] -#[diagnostic(severity(warning))] -struct NoRedeclareDiagnostic( - CompactStr, - #[label("'{0}' is already defined.")] pub Span, - #[label("It can not be redeclare here.")] pub Span, -); +fn no_redeclare_diagnostic(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint(no-redeclare): '{x0}' is already defined.")).with_labels( + [ + LabeledSpan::new_with_span(Some(format!("'{x0}' is already defined.")), span1), + LabeledSpan::new_with_span(Some("It can not be redeclare here.".into()), span2), + ], + ) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-redeclare): '{0}' is already defined as a built-in global variable.")] -#[diagnostic(severity(warning))] -struct NoRedeclareAsBuiltiInDiagnostic( - CompactStr, - #[label("'{0}' is already defined as a built-in global variable.")] pub Span, -); +fn no_redeclare_as_builti_in_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-redeclare): '{x0}' is already defined as a built-in global variable." + )) + .with_labels([LabeledSpan::new_with_span( + Some(format!("'{x0}' is already defined as a built-in global variable.")), + span1, + )]) +} // #[derive(Debug, Error, Diagnostic)] // #[error("eslint(no-redeclare): '{0}' is already defined by a variable declaration.")] @@ -105,12 +105,9 @@ impl Rule for NoRedeclare { impl NoRedeclare { fn report_diagnostic(&self, ctx: &LintContext, span: Span, ident: &BindingIdentifier) { if self.built_in_globals && ctx.env_contains_var(&ident.name) { - ctx.diagnostic(NoRedeclareAsBuiltiInDiagnostic( - ident.name.to_compact_str(), - ident.span, - )); + ctx.diagnostic(no_redeclare_as_builti_in_diagnostic(ident.name.as_str(), ident.span)); } else { - ctx.diagnostic(NoRedeclareDiagnostic(ident.name.to_compact_str(), ident.span, span)); + ctx.diagnostic(no_redeclare_diagnostic(ident.name.as_str(), ident.span, 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 9286dfc3ecb5..972a4aa70098 100644 --- a/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs +++ b/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs @@ -3,19 +3,18 @@ use oxc_ast::{ ast::{Argument, CallExpression, NewExpression, RegExpLiteral}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-regex-spaces): Spaces are hard to count.")] -#[diagnostic(severity(warning), help("Use a quantifier, e.g. {{2}}"))] -struct NoRegexSpacesDiagnostic(#[label] pub Span); +fn no_regex_spaces_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-regex-spaces): Spaces are hard to count.") + .with_help("Use a quantifier, e.g. {2}") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoRegexSpaces; @@ -47,19 +46,19 @@ impl Rule for NoRegexSpaces { match node.kind() { AstKind::RegExpLiteral(lit) => { if let Some(span) = Self::find_literal_to_report(lit) { - ctx.diagnostic(NoRegexSpacesDiagnostic(span)); // /a b/ + ctx.diagnostic(no_regex_spaces_diagnostic(span)); // /a b/ } } AstKind::CallExpression(expr) if Self::is_regexp_call_expression(expr) => { if let Some(span) = Self::find_expr_to_report(&expr.arguments) { - ctx.diagnostic(NoRegexSpacesDiagnostic(span)); // RegExp('a b') + ctx.diagnostic(no_regex_spaces_diagnostic(span)); // RegExp('a b') } } AstKind::NewExpression(expr) if Self::is_regexp_new_expression(expr) => { if let Some(span) = Self::find_expr_to_report(&expr.arguments) { - ctx.diagnostic(NoRegexSpacesDiagnostic(span)); // new RegExp('a b') + ctx.diagnostic(no_regex_spaces_diagnostic(span)); // new RegExp('a b') } } diff --git a/crates/oxc_linter/src/rules/eslint/no_script_url.rs b/crates/oxc_linter/src/rules/eslint/no_script_url.rs index b90b6be7716e..4a2e43f13cc4 100644 --- a/crates/oxc_linter/src/rules/eslint/no_script_url.rs +++ b/crates/oxc_linter/src/rules/eslint/no_script_url.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-script-url): Script URL is a form of eval")] -#[diagnostic(severity(warning), help("Disallow `javascript:` urls"))] -struct NoScriptUrlDiagnostic(#[label] pub Span); +fn no_script_url_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-script-url): Script URL is a form of eval") + .with_help("Disallow `javascript:` urls") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoScriptUrl; @@ -65,7 +64,7 @@ impl Rule for NoScriptUrl { } fn emit_diagnostic(ctx: &LintContext, span: Span) { - ctx.diagnostic(NoScriptUrlDiagnostic(Span::new(span.start, span.end))); + ctx.diagnostic(no_script_url_diagnostic(Span::new(span.start, span.end))); } fn is_tagged_template_expression(ctx: &LintContext, node: &AstNode, literal_span: Span) -> bool { 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 c75836516c72..5490d5d74580 100644 --- a/crates/oxc_linter/src/rules/eslint/no_self_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_self_assign.rs @@ -6,20 +6,18 @@ use oxc_ast::{ }, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::AssignmentOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-self-assign): this expression is assigned to itself")] -#[diagnostic(severity(warning))] -struct NoSelfAssignDiagnostic(#[label] pub Span); +fn no_self_assign_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-self-assign): this expression is assigned to itself") + .with_labels([span0.into()]) +} #[derive(Debug, Clone)] pub struct NoSelfAssign { @@ -63,7 +61,9 @@ impl Rule for NoSelfAssign { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::AssignmentExpression(assignment) = node.kind() else { return }; + let AstKind::AssignmentExpression(assignment) = node.kind() else { + return; + }; if matches!( assignment.operator, AssignmentOperator::Assign @@ -91,14 +91,14 @@ impl NoSelfAssign { || matches!(simple_assignment_target, SimpleAssignmentTarget::AssignmentTargetIdentifier(id1) if id1.name == id2.name); if self_assign { - ctx.diagnostic(NoSelfAssignDiagnostic(right.span())); + ctx.diagnostic(no_self_assign_diagnostic(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())); + ctx.diagnostic(no_self_assign_diagnostic(member_expr.span())); } } } @@ -240,7 +240,7 @@ impl NoSelfAssign { if key.static_name().is_some_and(|name| name == id1.binding.name) { if let Expression::Identifier(id2) = expr.without_parenthesized() { if id1.binding.name == id2.name { - ctx.diagnostic(NoSelfAssignDiagnostic(*span)); + ctx.diagnostic(no_self_assign_diagnostic(*span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_self_compare.rs b/crates/oxc_linter/src/rules/eslint/no_self_compare.rs index b11c2f31123b..294847a6390c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_self_compare.rs +++ b/crates/oxc_linter/src/rules/eslint/no_self_compare.rs @@ -1,20 +1,18 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{ast_util::calculate_hash, context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-self-compare): Disallow comparisons where both sides are exactly the same")] -#[diagnostic( - severity(warning), - help("If you are testing for NaN, you can use Number.isNaN function.") -)] -struct NoSelfCompareDiagnostic(#[label] pub Span, #[label] pub Span); +fn no_self_compare_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-self-compare): Disallow comparisons where both sides are exactly the same", + ) + .with_help("If you are testing for NaN, you can use Number.isNaN function.") + .with_labels([span0.into(), span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoSelfCompare; @@ -43,7 +41,9 @@ declare_oxc_lint!( impl Rule for NoSelfCompare { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::BinaryExpression(binary_expr) = node.kind() else { return }; + let AstKind::BinaryExpression(binary_expr) = node.kind() else { + return; + }; if !binary_expr.operator.is_compare() && !binary_expr.operator.is_equality() { return; } @@ -51,7 +51,7 @@ impl Rule for NoSelfCompare { let right = calculate_hash(&binary_expr.right); if left == right { - ctx.diagnostic(NoSelfCompareDiagnostic( + ctx.diagnostic(no_self_compare_diagnostic( binary_expr.left.span(), binary_expr.right.span(), )); diff --git a/crates/oxc_linter/src/rules/eslint/no_setter_return.rs b/crates/oxc_linter/src/rules/eslint/no_setter_return.rs index 4bb5cdac69dc..565130dd3103 100644 --- a/crates/oxc_linter/src/rules/eslint/no_setter_return.rs +++ b/crates/oxc_linter/src/rules/eslint/no_setter_return.rs @@ -1,16 +1,15 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-setter-return): Setter cannot return a value")] -struct NoSetterReturnDiagnostic(#[label] pub Span); +fn no_setter_return_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-setter-return): Setter cannot return a value") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoSetterReturn; @@ -41,9 +40,11 @@ declare_oxc_lint!( impl Rule for NoSetterReturn { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::ReturnStatement(stmt) = node.kind() else { return }; + let AstKind::ReturnStatement(stmt) = node.kind() else { + return; + }; if stmt.argument.is_some() && ctx.scopes().get_flags(node.scope_id()).is_set_accessor() { - ctx.diagnostic(NoSetterReturnDiagnostic(stmt.span)); + ctx.diagnostic(no_setter_return_diagnostic(stmt.span)); } } } @@ -68,10 +69,7 @@ fn test() { ("return 1;", None), ("return 1;", None), ("return 1; function foo(){ return 1; } return 1;", None), - ( - "function foo(){} return 1; var bar = function*(){ return 1; }; return 1; var baz = () => {}; return 1;", - None, - ), + ("function foo(){} return 1; var bar = function*(){ return 1; }; return 1; var baz = () => {}; return 1;", None), ("({ set foo(val) { return; } })", None), ("({ set foo(val) { if (val) { return; } } })", None), ("class A { set foo(val) { return; } }", None), @@ -118,10 +116,7 @@ fn test() { ("(class { set foo(val = (v) => 1) {} })", None), ("Object.defineProperty(foo, 'bar', { set(val) { return; } })", None), ("Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return; } } })", None), - ( - "Object.defineProperties(foo, { bar: { set(val) { try { return; } catch(e){} } } })", - None, - ), + ("Object.defineProperties(foo, { bar: { set(val) { try { return; } catch(e){} } } })", None), ("Object.create(foo, { bar: { set: function(val) { return; } } })", None), ("x = { set(val) { return 1; } }", None), ("x = { foo: { set(val) { return 1; } } }", None), @@ -138,10 +133,7 @@ fn test() { ("Object.create(foo, { set: function(val) { return 1; } })", None), ("Object.defineProperty(foo, { set: (val) => 1 })", None), ("Object.defineProperty(foo, 'bar', { set(val) { function foo() { return 1; } } })", None), - ( - "Reflect.defineProperty(foo, 'bar', { set(val) { var foo = function() { return 1; } } })", - None, - ), + ("Reflect.defineProperty(foo, 'bar', { set(val) { var foo = function() { return 1; } } })", None), ("Object.defineProperties(foo, { bar: { set(val) { () => { return 1 }; } } })", None), ("Object.create(foo, { bar: { set: (val) => { (val) => 1; } } })", None), ("Object.defineProperty(foo, 'bar', 'baz', { set(val) { return 1; } })", None), @@ -156,44 +148,20 @@ fn test() { ("Object.create({ bar: { set(val) { return 1; } } }, foo)", None), ("Object.DefineProperty(foo, 'bar', { set(val) { return 1; } })", None), ("Reflect.DefineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), - ( - "Object.DefineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", - None, - ), + ("Object.DefineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", None), ("Object.Create(foo, { bar: { set: function(val) { return 1; } } })", None), ("object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), ("reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), - ( - "Reflect.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", - None, - ), + ("Reflect.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", None), ("object.create(foo, { bar: { set: function(val) { return 1; } } })", None), ("Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), - ( - "/* globals Object:off */ Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", - None, - ), - ( - "Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", - None, - ), + ("/* globals Object:off */ Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + ("Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", None), ("let Object; Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), - ( - "function f() { Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } }); var Reflect;}", - None, - ), - ( - "function f(Object) { Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } }) }", - None, - ), - ( - "if (x) { const Object = getObject(); Object.create(foo, { bar: { set: function(val) { return 1; } } }) }", - None, - ), - ( - "x = function Object() { Object.defineProperty(foo, 'bar', { set(val) { return 1; } }) }", - None, - ), + ("function f() { Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } }); var Reflect;}", None), + ("function f(Object) { Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } }) }", None), + ("if (x) { const Object = getObject(); Object.create(foo, { bar: { set: function(val) { return 1; } } }) }", None), + ("x = function Object() { Object.defineProperty(foo, 'bar', { set(val) { return 1; } }) }", None), ]; let fail = vec![ @@ -216,23 +184,11 @@ fn test() { ("class A { set a(val) { return 1; } set b(val) { return 1; } }", None), ("(class { set a(val) { return 1; } static set b(val) { return 1; } })", None), ("({ set a(val) { if(val) { return 1; } else { return 2 }; } })", None), - ( - "class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } }", - None, - ), - ( - "(class { static set a(val) { if (val > 0) { this._val = val; return val; } return false; } })", - None, - ), + ("class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } }", None), + ("(class { static set a(val) { if (val > 0) { this._val = val; return val; } return false; } })", None), ("({ set a(val) { if(val) { return 1; } else { return; }; } })", None), - ( - "class A { set a(val) { switch(val) { case 1: return x; case 2: return; default: return z } } }", - None, - ), - ( - "(class { static set a(val) { if (val > 0) { this._val = val; return; } return false; } })", - None, - ), + ("class A { set a(val) { switch(val) { case 1: return x; case 2: return; default: return z } } }", None), + ("(class { static set a(val) { if (val > 0) { this._val = val; return; } return false; } })", None), ("({ set a(val) { function b(){} return b(); } })", None), ("class A { set a(val) { return () => {}; } }", None), ("(class { set a(val) { function b(){ return 1; } return 2; } })", None), diff --git a/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs b/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs index 7c4a9153a816..982f927d6846 100644 --- a/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs +++ b/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, globals::PRE_DEFINE_VAR, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-shadow-restricted-names): Shadowing of global properties such as 'undefined' is not allowed.")] -#[diagnostic(severity(warning), help("Shadowing of global properties '{0}'."))] -struct NoShadowRestrictedNamesDiagnostic(CompactStr, #[label] pub Span); +fn no_shadow_restricted_names_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-shadow-restricted-names): Shadowing of global properties such as 'undefined' is not allowed.") + .with_help(format!("Shadowing of global properties '{x0}'.")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoShadowRestrictedNames; @@ -41,7 +40,7 @@ declare_oxc_lint!( #[inline] fn check_and_diagnostic(s: &str, span: Span, ctx: &LintContext) { if PRE_DEFINE_VAR.contains_key(s) { - ctx.diagnostic(NoShadowRestrictedNamesDiagnostic(s.into(), span)); + ctx.diagnostic(no_shadow_restricted_names_diagnostic(s, span)); } } @@ -125,23 +124,32 @@ fn test() { ("function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }", None), ("function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }", None), ("function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }", None), - ("var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }", Some(json!({ - "parserOptions": { - "ecmaVersion": 6 - } - }))), - ("var {undefined} = obj; var {a: undefined} = obj; var {a: {b: {undefined}}} = obj; var {a, ...undefined} = obj;", Some(json!({ - "parserOptions": { - "ecmaVersion": 9 - } - }))), + ( + "var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }", + Some(json!({ + "parserOptions": { + "ecmaVersion": 6 + } + })), + ), + ( + "var {undefined} = obj; var {a: undefined} = obj; var {a: {b: {undefined}}} = obj; var {a, ...undefined} = obj;", + Some(json!({ + "parserOptions": { + "ecmaVersion": 9 + } + })), + ), ("var normal, undefined; undefined = 5;", None), ("try {} catch(undefined: undefined) {}", None), - ("var [undefined] = [1]", Some(json!({ - "parserOptions": { - "ecmaVersion": 6 - } - }))), + ( + "var [undefined] = [1]", + Some(json!({ + "parserOptions": { + "ecmaVersion": 6 + } + })), + ), ("class undefined { }", None), ("class foo { undefined(undefined) { } }", None), ("class foo { #undefined(undefined) { } }", None), diff --git a/crates/oxc_linter/src/rules/eslint/no_sparse_arrays.rs b/crates/oxc_linter/src/rules/eslint/no_sparse_arrays.rs index 8baf3acecb76..a5a61a0b6eeb 100644 --- a/crates/oxc_linter/src/rules/eslint/no_sparse_arrays.rs +++ b/crates/oxc_linter/src/rules/eslint/no_sparse_arrays.rs @@ -1,6 +1,5 @@ -use miette::{miette, LabeledSpan}; use oxc_ast::{ast::ArrayExpressionElement, AstKind}; -use oxc_diagnostics::miette::{self, Severity}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use crate::{context::LintContext, rule::Rule, AstNode}; @@ -46,12 +45,13 @@ impl Rule for NoSparseArrays { if !violations.is_empty() { if violations.len() < 10 { - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = violations, - help = "remove the comma or insert `undefined`", - "eslint(no-sparse-arrays): Unexpected comma in middle of array" - )); + ctx.diagnostic( + OxcDiagnostic::warning( + "eslint(no-sparse-arrays): Unexpected comma in middle of array", + ) + .with_help("remove the comma or insert `undefined`") + .with_labels(violations), + ); } else { let span = if (array_expr.span.end - array_expr.span.start) < 50 { LabeledSpan::at(array_expr.span, "the array here") @@ -62,13 +62,14 @@ impl Rule for NoSparseArrays { ) }; - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = vec![span], - help = "remove the comma or insert `undefined`", - "eslint(no-sparse-arrays): {} unexpected commas in middle of array", - violations.len() - )); + ctx.diagnostic( + OxcDiagnostic::warning(format!( + "eslint(no-sparse-arrays): {} unexpected commas in middle of array", + violations.len() + )) + .with_help("remove the comma or insert `undefined`") + .with_label(span), + ); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs b/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs index 0bb530197f9b..bc81ae265c59 100644 --- a/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs +++ b/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs @@ -1,20 +1,18 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-template-curly-in-string): Unexpected template string expression")] -#[diagnostic( - severity(warning), - help("Disallow template literal placeholder syntax in regular strings") -)] -struct NoTemplateCurlyInStringDiagnostic(#[label] pub Span); +fn no_template_curly_in_string_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-template-curly-in-string): Unexpected template string expression", + ) + .with_help("Disallow template literal placeholder syntax in regular strings") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoTemplateCurlyInString; @@ -64,7 +62,7 @@ impl Rule for NoTemplateCurlyInString { .expect("Conversion from usize to u32 failed for match_start"); let match_end = u32::try_from(end_index + 1) .expect("Conversion from usize to u32 failed for match_end"); - ctx.diagnostic(NoTemplateCurlyInStringDiagnostic(Span::new( + ctx.diagnostic(no_template_curly_in_string_diagnostic(Span::new( literal_span_start + match_start, literal_span_start + match_end, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_ternary.rs b/crates/oxc_linter/src/rules/eslint/no_ternary.rs index af5c66ee835f..2003bcb037fd 100644 --- a/crates/oxc_linter/src/rules/eslint/no_ternary.rs +++ b/crates/oxc_linter/src/rules/eslint/no_ternary.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-ternary): Unexpected use of ternary expression")] -#[diagnostic(severity(warning), help("Do not use the ternary expression."))] -struct NoTernaryDiagnostic(#[label] pub Span); +fn no_ternary_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-ternary): Unexpected use of ternary expression") + .with_help("Do not use the ternary expression.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoTernary; @@ -38,7 +37,7 @@ declare_oxc_lint!( impl Rule for NoTernary { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::ConditionalExpression(cond_expr) = node.kind() { - ctx.diagnostic(NoTernaryDiagnostic(Span::new( + ctx.diagnostic(no_ternary_diagnostic(Span::new( cond_expr.span.start, cond_expr.span.end, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs index b6dcfe79a3e5..f079237ae118 100644 --- a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs +++ b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs @@ -1,13 +1,11 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::collections::{HashMap, HashSet}; use oxc_ast::{ ast::{Expression, MethodDefinitionKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::{ petgraph::stable_graph::NodeIndex, pg::neighbors_filtered_by_edge_weight, AstNodeId, EdgeType, @@ -16,10 +14,11 @@ use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-this-before-super): Expected to always call super() before this/super property access.")] -#[diagnostic(severity(warning), help("Call super() before this/super property access."))] -struct NoThisBeforeSuperDiagnostic(#[label] pub Span); +fn no_this_before_super_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-this-before-super): Expected to always call super() before this/super property access.") + .with_help("Call super() before this/super property access.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoThisBeforeSuper; @@ -138,7 +137,7 @@ impl Rule for NoThisBeforeSuper { // so the unwrap() is safe here. The parent node is the // AstKind::MethodDefinition for `constructor`. let parent_span = ctx.nodes().parent_node(node.id()).unwrap().kind().span(); - ctx.diagnostic(NoThisBeforeSuperDiagnostic(parent_span)); + ctx.diagnostic(no_this_before_super_diagnostic(parent_span)); } } } @@ -182,7 +181,6 @@ fn test() { ("class A { constructor() { this.b(); } }", None), ("class A extends null { }", None), ("class A extends null { constructor() { } }", None), - // allows `this`/`super` after `super()`. ("class A extends B { }", None), ("class A extends B { constructor() { super(); } }", None), @@ -194,31 +192,25 @@ fn test() { ("class A extends B { constructor() { foo += super().a; this.c(); } }", None), ("class A extends B { constructor() { foo |= super().a; this.c(); } }", None), ("class A extends B { constructor() { foo &= super().a; this.c(); } }", None), - // allows `this`/`super` in nested executable scopes, even if before `super()`. ("class A extends B { constructor() { class B extends C { constructor() { super(); this.d = 0; } } super(); } }", None), ("class A extends B { constructor() { var B = class extends C { constructor() { super(); this.d = 0; } }; super(); } }", None), ("class A extends B { constructor() { function c() { this.d(); } super(); } }", None), ("class A extends B { constructor() { var c = function c() { this.d(); }; super(); } }", None), ("class A extends B { constructor() { var c = () => this.d(); super(); } }", None), - // ignores out of constructors. ("class A { b() { this.c = 0; } }", None), ("class A extends B { c() { this.d = 0; } }", None), ("function a() { this.b = 0; }", None), - // multi code path. ("class A extends B { constructor() { if (a) { super(); this.a(); } else { super(); this.b(); } } }", None), ("class A extends B { constructor() { if (a) super(); else super(); this.a(); } }", None), ("class A extends B { constructor() { try { super(); } finally {} this.a(); } }", None), - // https://github.com/eslint/eslint/issues/5261 ("class A extends B { constructor(a) { super(); for (const b of a) { this.a(); } } }", None), ("class A extends B { constructor(a) { for (const b of a) { foo(b); } super(); } }", None), - // https://github.com/eslint/eslint/issues/5319 ("class A extends B { constructor(a) { super(); this.a = a && function(){} && this.foo; } }", None), - // https://github.com/eslint/eslint/issues/5394 ( r"class A extends Object { @@ -227,14 +219,15 @@ fn test() { for (let i = 0; i < 0; i++); this; } - }", None), - + }", + None, + ), // https://github.com/eslint/eslint/issues/5894 ("class A { constructor() { return; this; } }", None), ("class A extends B { constructor() { return; this; } }", None), - // https://github.com/eslint/eslint/issues/8848 - (r" + ( + r" class A extends B { constructor(props) { super(props); @@ -247,13 +240,14 @@ fn test() { } } } - ", None), - + ", + None, + ), // Class field initializers are always evaluated after `super()`. ("class C { field = this.toString(); }", None), ("class C extends B { field = this.foo(); }", None), ("class C extends B { field = this.foo(); constructor() { super(); } }", None), - ("class C extends B { field = this.foo(); constructor() { } }", None) // < in this case, initializers are never evaluated. + ("class C extends B { field = this.foo(); constructor() { } }", None), // < in this case, initializers are never evaluated. ]; let fail = vec![ @@ -280,7 +274,8 @@ fn test() { ("class A extends B { constructor() { foo ||= super().a; this.c(); } }", None), ("class A extends B { constructor() { foo ??= super().a; this.c(); } }", None), ("class A extends B { constructor() { if (foo) { if (bar) { } super(); } this.a(); }}", None), - ("class A extends B { + ( + "class A extends B { constructor() { if (foo) { } else { @@ -288,8 +283,11 @@ fn test() { } this.a(); } - }", None), - ("class A extends B { + }", + None, + ), + ( + "class A extends B { constructor() { try { call(); @@ -297,24 +295,33 @@ fn test() { this.a(); } } - }", None), - ("class A extends B { + }", + None, + ), + ( + "class A extends B { constructor() { while (foo) { super(); } this.a(); } - }", None), - ("class A extends B { + }", + None, + ), + ( + "class A extends B { constructor() { while (foo) { this.a(); super(); } } - }", None), - ("class A extends B { + }", + None, + ), + ( + "class A extends B { constructor() { while (foo) { if (init) { @@ -323,7 +330,9 @@ fn test() { } } } - }", None), + }", + None, + ), ]; Tester::new(NoThisBeforeSuper::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/eslint/no_undef.rs b/crates/oxc_linter/src/rules/eslint/no_undef.rs index 996cfd7ccefe..8791f036c757 100644 --- a/crates/oxc_linter/src/rules/eslint/no_undef.rs +++ b/crates/oxc_linter/src/rules/eslint/no_undef.rs @@ -1,18 +1,17 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use oxc_syntax::operator::UnaryOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-undef): Disallow the use of undeclared variables.")] -#[diagnostic(severity(warning), help("'{0}' is not defined."))] -struct NoUndefDiagnostic(CompactStr, #[label] pub Span); +fn no_undef_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-undef): Disallow the use of undeclared variables.") + .with_help(format!("'{x0}' is not defined.")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUndef { @@ -69,7 +68,7 @@ impl Rule for NoUndef { continue; } - ctx.diagnostic(NoUndefDiagnostic(name.clone(), reference.span())); + ctx.diagnostic(no_undef_diagnostic(name, reference.span())); } } } @@ -157,7 +156,7 @@ fn test() { "class C { static { let a; a; } }", "class C { static { a; let a; } }", "class C { static { function a() {} a; } }", - "class C { static { a; function a() {} } }" + "class C { static { a; function a() {} } }", ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs index 1ce28c51618d..944b4ed399ca 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs @@ -2,22 +2,18 @@ use oxc_ast::{ ast::{BreakStatement, ContinueStatement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-unsafe-finally): Unsafe finally block")] -#[diagnostic( - severity(warning), - help("Control flow inside try or catch blocks will be overwritten by this statement") -)] -struct NoUnsafeFinallyDiagnostic(#[label] Span); +fn no_unsafe_finally_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-unsafe-finally): Unsafe finally block") + .with_help("Control flow inside try or catch blocks will be overwritten by this statement") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUnsafeFinally; @@ -92,7 +88,7 @@ impl Rule for NoUnsafeFinally { if label_name.is_some() && label_inside { break; } - ctx.diagnostic(NoUnsafeFinallyDiagnostic(node.kind().span())); + ctx.diagnostic(no_unsafe_finally_diagnostic(node.kind().span())); return; } } @@ -124,100 +120,37 @@ fn test() { use crate::tester::Tester; let pass = vec![ - ( - "var foo = function() {\n try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n console.log('hola!') \n } \n }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { console.log('hola!') } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { function a(x) { return x } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { if(!x) { throw new Error() } } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { while(true) { if(x) { break } else { continue } } } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { label: while(true) { if(x) { break label; } else { continue } } } } }", - None, - ), + ("var foo = function() {\n try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n console.log('hola!') \n } \n }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { console.log('hola!') } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { function a(x) { return x } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { if(!x) { throw new Error() } } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { while(true) { if(x) { break } else { continue } } } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { var a = function(x) { label: while(true) { if(x) { break label; } else { continue } } } } }", None), ("var foo = function() { try {} finally { while (true) break; } }", None), ("var foo = function() { try {} finally { while (true) continue; } }", None), ("var foo = function() { try {} finally { switch (true) { case true: break; } } }", None), ("var foo = function() { try {} finally { do { break; } while (true) } }", None), - ( - "var foo = function() { try { return 1; } catch(err) { return 2; } finally { var bar = () => { throw new Error(); }; } };", - None, - ), - ( - "var foo = function() { try { return 1; } catch(err) { return 2 } finally { (x) => x } }", - None, - ), - ( - "var foo = function() { try { return 1; } finally { class bar { constructor() {} static ehm() { return 'Hola!'; } } } };", - None, - ), + ("var foo = function() { try { return 1; } catch(err) { return 2; } finally { var bar = () => { throw new Error(); }; } };", None), + ("var foo = function() { try { return 1; } catch(err) { return 2 } finally { (x) => x } }", None), + ("var foo = function() { try { return 1; } finally { class bar { constructor() {} static ehm() { return 'Hola!'; } } } };", None), ]; let fail = vec![ - ( - "var foo = function() { \n try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n return 3; \n } \n }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { if(true) { return 3 } else { return 2 } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { return 3 } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { return function(x) { return y } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { return { x: function(c) { return c } } } }", - None, - ), - ( - "var foo = function() { try { return 1 } catch(err) { return 2 } finally { throw new Error() } }", - None, - ), - ( - "var foo = function() { try { foo(); } finally { try { bar(); } finally { return; } } };", - None, - ), - ( - "var foo = function() { label: try { return 0; } finally { break label; } return 1; }", - None, - ), - ( - "var foo = function() { \n a: try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n break a; \n } \n }", - None, - ), + ("var foo = function() { \n try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n return 3; \n } \n }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { if(true) { return 3 } else { return 2 } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { return 3 } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { return function(x) { return y } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { return { x: function(c) { return c } } } }", None), + ("var foo = function() { try { return 1 } catch(err) { return 2 } finally { throw new Error() } }", None), + ("var foo = function() { try { foo(); } finally { try { bar(); } finally { return; } } };", None), + ("var foo = function() { label: try { return 0; } finally { break label; } return 1; }", None), + ("var foo = function() { \n a: try { \n return 1; \n } catch(err) { \n return 2; \n } finally { \n break a; \n } \n }", None), ("var foo = function() { while (true) try {} finally { break; } }", None), ("var foo = function() { while (true) try {} finally { continue; } }", None), ("var foo = function() { switch (true) { case true: try {} finally { break; } } }", None), - ( - "var foo = function() { a: while (true) try {} finally { switch (true) { case true: break a; } } }", - None, - ), - ( - "var foo = function() { a: while (true) try {} finally { switch (true) { case true: continue; } } }", - None, - ), - ( - "var foo = function() { a: switch (true) { case true: try {} finally { switch (true) { case true: break a; } } } }", - None, - ), + ("var foo = function() { a: while (true) try {} finally { switch (true) { case true: break a; } } }", None), + ("var foo = function() { a: while (true) try {} finally { switch (true) { case true: continue; } } }", None), + ("var foo = function() { a: switch (true) { case true: try {} finally { switch (true) { case true: break a; } } } }", None), ]; Tester::new(NoUnsafeFinally::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs index 12024952bc29..ac640272f12b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs @@ -2,25 +2,19 @@ use oxc_ast::{ ast::{BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("Unexpected logical not in the left hand side of '{0}' operator")] -#[diagnostic( - severity(warning), - help( - "use parenthesis to express the negation of the whole boolean expression, as '!' binds more closely than '{0}'" - ) -)] -struct NoUnsafeNegationDiagnostic(&'static str, #[label] pub Span); +fn no_unsafe_negation_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("Unexpected logical not in the left hand side of '{x0}' operator")) + .with_help(format!("use parenthesis to express the negation of the whole boolean expression, as '!' binds more closely than '{x0}'")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUnsafeNegation { @@ -84,7 +78,7 @@ impl NoUnsafeNegation { fn report_with_fix(expr: &BinaryExpression, ctx: &LintContext<'_>) { use oxc_codegen::{Context, Gen}; // Diagnostic points at the unexpected negation - let diagnostic = NoUnsafeNegationDiagnostic(expr.operator.as_str(), expr.left.span()); + let diagnostic = no_unsafe_negation_diagnostic(expr.operator.as_str(), expr.left.span()); let fix_producer = || { // modify `!a instance of B` to `!(a instanceof B)` 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 212cde835b88..9eb17e9c83ee 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 @@ -2,28 +2,27 @@ use oxc_ast::{ ast::{match_assignment_target_pattern, Argument, AssignmentTarget, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::LogicalOperator; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-unsafe-optional-chaining): Unsafe usage of optional chaining")] -#[diagnostic( - severity(warning), - help("If this short-circuits with 'undefined' the evaluation will throw TypeError") -)] -struct NoUnsafeOptionalChainingDiagnostic(#[label] pub Span); +fn no_unsafe_optional_chaining_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-unsafe-optional-chaining): Unsafe usage of optional chaining") + .with_help("If this short-circuits with 'undefined' the evaluation will throw TypeError") + .with_labels([span0.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-unsafe-optional-chaining): Unsafe arithmetic operation on optional chaining")] -#[diagnostic(severity(warning), help("This can result in NaN."))] -struct NoUnsafeArithmeticDiagnostic(#[label] pub Span); +fn no_unsafe_arithmetic_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(no-unsafe-optional-chaining): Unsafe arithmetic operation on optional chaining", + ) + .with_help("This can result in NaN.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUnsafeOptionalChaining { @@ -179,10 +178,10 @@ impl NoUnsafeOptionalChaining { Expression::ChainExpression(expr) => { match error_type { ErrorType::Usage => { - ctx.diagnostic(NoUnsafeOptionalChainingDiagnostic(expr.span)); + ctx.diagnostic(no_unsafe_optional_chaining_diagnostic(expr.span)); } ErrorType::Arithmetic => { - ctx.diagnostic(NoUnsafeArithmeticDiagnostic(expr.span)); + ctx.diagnostic(no_unsafe_arithmetic_diagnostic(expr.span)); } }; } @@ -232,10 +231,7 @@ fn test() { ("({foo: obj.bar = obj?.baz} = obj);", None), ("(foo?.bar, bar)();", None), ("(foo?.bar ? baz : qux)();", None), - ( - "\n async function func() {\n await obj?.foo();\n await obj?.foo?.();\n (await obj?.foo)?.();\n (await obj?.foo)?.bar;\n await bar?.baz;\n await (foo ?? obj?.foo.baz);\n (await bar?.baz ?? bar).baz;\n (await bar?.baz ?? await bar).baz;\n await (foo?.bar ? baz : qux);\n }\n ", - None, - ), + ("\n async function func() {\n await obj?.foo();\n await obj?.foo?.();\n (await obj?.foo)?.();\n (await obj?.foo)?.bar;\n await bar?.baz;\n await (foo ?? obj?.foo.baz);\n (await bar?.baz ?? bar).baz;\n (await bar?.baz ?? await bar).baz;\n await (foo?.bar ? baz : qux);\n }\n ", None), ("(obj?.foo ?? bar?.baz ?? qux)();", None), ("((obj?.foo ?? bar?.baz) || qux)();", None), ("((obj?.foo || bar?.baz) || qux)();", None), @@ -254,10 +250,7 @@ fn test() { ("bar **= obj?.foo;", None), ("bar *= obj?.boo", None), ("bar /= obj?.boo", None), - ( - "async function func() {\n await obj?.foo + await obj?.bar;\n await obj?.foo - await obj?.bar;\n await obj?.foo * await obj?.bar;\n +await obj?.foo;\n -await obj?.foo;\n bar += await obj?.foo;\n bar -= await obj?.foo;\n bar %= await obj?.foo;\n bar **= await obj?.foo;\n bar *= await obj?.boo;\n bar /= await obj?.boo;\n }\n ", - None, - ), + ("async function func() {\n await obj?.foo + await obj?.bar;\n await obj?.foo - await obj?.bar;\n await obj?.foo * await obj?.bar;\n +await obj?.foo;\n -await obj?.foo;\n bar += await obj?.foo;\n bar -= await obj?.foo;\n bar %= await obj?.foo;\n bar **= await obj?.foo;\n bar *= await obj?.boo;\n bar /= await obj?.boo;\n }\n ", None), ("obj?.foo - bar;", Some(serde_json::json!([{}]))), ( "obj?.foo - bar;", diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_labels.rs b/crates/oxc_linter/src/rules/eslint/no_unused_labels.rs index 4cc32d2945fe..f6b0bb3ba4a8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_labels.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_labels.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, fixer::Fix, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-unused-labels): Disallow unused labels")] -#[diagnostic(severity(warning), help("'{0}:' is defined but never used."))] -struct NoUnusedLabelsDiagnostic(CompactStr, #[label] pub Span); +fn no_unused_labels_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-unused-labels): Disallow unused labels") + .with_help(format!("'{x0}:' is defined but never used.")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUnusedLabels; @@ -51,7 +50,7 @@ impl Rule for NoUnusedLabels { // TODO: Ignore fix where comments exist between label and statement // e.g. A: /* Comment */ function foo(){} ctx.diagnostic_with_fix( - NoUnusedLabelsDiagnostic(stmt.label.name.to_compact_str(), stmt.label.span), + no_unused_labels_diagnostic(stmt.label.name.as_str(), stmt.label.span), || Fix::delete(stmt.label.span), ); } @@ -69,10 +68,7 @@ fn test() { ("A: if (a) { foo(); if (b) break A; bar(); }", None), ("A: for (var i = 0; i < 10; ++i) { foo(); if (a) break A; bar(); }", None), ("A: for (var i = 0; i < 10; ++i) { foo(); if (a) continue A; bar(); }", None), - ( - "A: { B: break B; C: for (var i = 0; i < 10; ++i) { foo(); if (a) break A; if (c) continue C; bar(); } }", - None, - ), + ("A: { B: break B; C: for (var i = 0; i < 10; ++i) { foo(); if (a) break A; if (c) continue C; bar(); } }", None), ("A: { var A = 0; console.log(A); break A; console.log(A); }", None), ]; diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs index 12677949d5e4..f291c92d3b92 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs @@ -1,20 +1,20 @@ use itertools::Itertools; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, AstNodeId, AstNodes}; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use oxc_syntax::class::ElementKind; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-unused-private-class-members): '{0}' is defined but never used.")] -#[diagnostic(severity(warning))] -struct NoUnusedPrivateClassMembersDiagnostic(CompactStr, #[label] pub Span); +fn no_unused_private_class_members_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-unused-private-class-members): '{x0}' is defined but never used." + )) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUnusedPrivateClassMembers; @@ -106,8 +106,8 @@ impl Rule for NoUnusedPrivateClassMembers { && ident.element_ids.contains(&element_id) }) { - ctx.diagnostic(NoUnusedPrivateClassMembersDiagnostic( - element.name.clone(), + ctx.diagnostic(no_unused_private_class_members_diagnostic( + &element.name, element.span, )); } diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_catch.rs b/crates/oxc_linter/src/rules/eslint/no_useless_catch.rs index f7837ccbdf7e..1b63292e5e26 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_catch.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_catch.rs @@ -2,30 +2,26 @@ use oxc_ast::{ ast::{BindingPatternKind, Expression, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-useless-catch): Unnecessary try/catch wrapper")] -#[diagnostic(severity(warning))] -struct NoUselessCatchDiagnostic( - #[label("is caught here")] pub Span, - #[label("and re-thrown here")] pub Span, -); +fn no_useless_catch_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-useless-catch): Unnecessary try/catch wrapper").with_labels([ + LabeledSpan::new_with_span(Some("is caught here".into()), span0), + LabeledSpan::new_with_span(Some("and re-thrown here".into()), span1), + ]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-useless-catch): Unnecessary catch clause")] -#[diagnostic(severity(warning))] -struct NoUselessCatchFinalizerDiagnostic( - #[label("is caught here")] pub Span, - #[label("and re-thrown here")] pub Span, -); +fn no_useless_catch_finalizer_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-useless-catch): Unnecessary catch clause").with_labels([ + LabeledSpan::new_with_span(Some("is caught here".into()), span0), + LabeledSpan::new_with_span(Some("and re-thrown here".into()), span1), + ]) +} #[derive(Debug, Default, Clone)] pub struct NoUselessCatch; @@ -56,8 +52,12 @@ declare_oxc_lint!( impl Rule for NoUselessCatch { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::TryStatement(try_stmt) = node.kind() else { return }; - let Some(catch_clause) = &try_stmt.handler else { return }; + let AstKind::TryStatement(try_stmt) = node.kind() else { + return; + }; + let Some(catch_clause) = &try_stmt.handler else { + return; + }; let Some(BindingPatternKind::BindingIdentifier(binding_ident)) = catch_clause.param.as_ref().map(|param| ¶m.pattern.kind) else { @@ -66,15 +66,17 @@ impl Rule for NoUselessCatch { let Some(Statement::ThrowStatement(throw_stmt)) = catch_clause.body.body.first() else { return; }; - let Expression::Identifier(throw_ident) = &throw_stmt.argument else { return }; + let Expression::Identifier(throw_ident) = &throw_stmt.argument else { + return; + }; if binding_ident.name == throw_ident.name { if try_stmt.finalizer.is_some() { - ctx.diagnostic(NoUselessCatchFinalizerDiagnostic( + ctx.diagnostic(no_useless_catch_finalizer_diagnostic( binding_ident.span, throw_stmt.span, )); } else { - ctx.diagnostic(NoUselessCatchDiagnostic(binding_ident.span, throw_stmt.span)); + ctx.diagnostic(no_useless_catch_diagnostic(binding_ident.span, throw_stmt.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs index 46896570d4f4..786bc8f60f49 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs @@ -1,20 +1,19 @@ use memchr::memmem; +use oxc_diagnostics::OxcDiagnostic; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNodeId; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode, Fix}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-useless-escape): Unnecessary escape character {0:?}")] -#[diagnostic(severity(warning))] -struct NoUselessEscapeDiagnostic(char, #[label] pub Span); +fn no_useless_escape_diagnostic(x0: char, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint(no-useless-escape): Unnecessary escape character {x0:?}" + )) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUselessEscape; @@ -87,7 +86,7 @@ fn check(ctx: &LintContext<'_>, node_id: AstNodeId, start: u32, offsets: &[usize if !is_within_jsx_attribute_item(node_id, ctx) { let span = Span::new(offset - 1, offset + len); - ctx.diagnostic_with_fix(NoUselessEscapeDiagnostic(c, span), || { + ctx.diagnostic_with_fix(no_useless_escape_diagnostic(c, span), || { Fix::new(c.to_string(), span) }); } 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 d8241bb31a6a..4710c808d7b8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_rename.rs @@ -2,20 +2,19 @@ use oxc_ast::{ ast::{AssignmentTarget, AssignmentTargetProperty, BindingPatternKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; use oxc_span::GetSpan; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-useless-rename): Disallow renaming import, export, and destructured assignments to the same name")] -#[diagnostic(severity(warning), help("Either remove the renaming or rename the variable."))] -struct NoUselessRenameDiagnostic(#[label] pub Span); +fn no_useless_rename_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-useless-rename): Disallow renaming import, export, and destructured assignments to the same name") + .with_help("Either remove the renaming or rename the variable.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUselessRename(Box); @@ -93,7 +92,9 @@ impl Rule for NoUselessRename { continue; } - let Some(key) = property.key.static_name() else { continue }; + let Some(key) = property.key.static_name() else { + continue; + }; let renamed_key = match &property.value.kind { BindingPatternKind::AssignmentPattern(assignment_pattern) => { @@ -110,7 +111,7 @@ impl Rule for NoUselessRename { }; if key == renamed_key { - ctx.diagnostic(NoUselessRenameDiagnostic(property.span)); + ctx.diagnostic(no_useless_rename_diagnostic(property.span)); } } } @@ -127,11 +128,15 @@ impl Rule for NoUselessRename { continue; }; - let Some(key) = property.name.static_name() else { continue }; - let Some(renamed_key) = property.binding.name() else { continue }; + let Some(key) = property.name.static_name() else { + continue; + }; + let Some(renamed_key) = property.binding.name() else { + continue; + }; if key == renamed_key { - ctx.diagnostic(NoUselessRenameDiagnostic(property.span)); + ctx.diagnostic(no_useless_rename_diagnostic(property.span)); } } } @@ -140,7 +145,7 @@ impl Rule for NoUselessRename { && import_specifier.imported.span() != import_specifier.local.span && import_specifier.local.name == import_specifier.imported.name() { - ctx.diagnostic(NoUselessRenameDiagnostic(import_specifier.local.span)); + ctx.diagnostic(no_useless_rename_diagnostic(import_specifier.local.span)); } } AstKind::ExportNamedDeclaration(export_named_decl) => { @@ -151,7 +156,7 @@ impl Rule for NoUselessRename { if specifier.local.span() != specifier.exported.span() && specifier.local.name() == specifier.exported.name() { - ctx.diagnostic(NoUselessRenameDiagnostic(specifier.local.span())); + ctx.diagnostic(no_useless_rename_diagnostic(specifier.local.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_var.rs b/crates/oxc_linter/src/rules/eslint/no_var.rs index 4ade71dbd529..a2bcad0e5427 100644 --- a/crates/oxc_linter/src/rules/eslint/no_var.rs +++ b/crates/oxc_linter/src/rules/eslint/no_var.rs @@ -1,17 +1,16 @@ use oxc_ast::{ast::VariableDeclarationKind, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-var): Unexpected var, use let or const instead.")] -#[diagnostic(severity(warning), help("Replace var with let or const"))] -struct NoVarDiagnostic(#[label] pub Span); +fn no_var_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-var): Unexpected var, use let or const instead.") + .with_help("Replace var with let or const") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoVar; @@ -46,7 +45,7 @@ impl Rule for NoVar { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::VariableDeclaration(dec) = node.kind() { if dec.kind == VariableDeclarationKind::Var { - ctx.diagnostic(NoVarDiagnostic(Span::new(dec.span.start, dec.span.start + 3))); + ctx.diagnostic(no_var_diagnostic(Span::new(dec.span.start, dec.span.start + 3))); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_void.rs b/crates/oxc_linter/src/rules/eslint/no_void.rs index e91aba8533af..53b9ef0d9f2a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_void.rs +++ b/crates/oxc_linter/src/rules/eslint/no_void.rs @@ -1,18 +1,16 @@ use crate::{context::LintContext, rule::Rule, AstNode}; +use oxc_diagnostics::OxcDiagnostic; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::UnaryOperator; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-void): Disallow `void` operators")] -#[diagnostic(severity(warning), help("Expected 'undefined' and instead saw 'void'."))] -struct NoVoidDiagnostic(#[label] pub Span); +fn no_void_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-void): Disallow `void` operators") + .with_help("Expected 'undefined' and instead saw 'void'.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoVoid { @@ -63,7 +61,7 @@ impl Rule for NoVoid { }; if unary_expr.operator == UnaryOperator::Void { - ctx.diagnostic(NoVoidDiagnostic(Span::new( + ctx.diagnostic(no_void_diagnostic(Span::new( unary_expr.span.start, unary_expr.span.start + 4, ))); diff --git a/crates/oxc_linter/src/rules/eslint/no_with.rs b/crates/oxc_linter/src/rules/eslint/no_with.rs index 32161ca5072e..26677c371697 100644 --- a/crates/oxc_linter/src/rules/eslint/no_with.rs +++ b/crates/oxc_linter/src/rules/eslint/no_with.rs @@ -1,17 +1,16 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(no-with): Unexpected use of `with` statement.")] -#[diagnostic(severity(warning), help("Do not use the `with` statement."))] -struct NoWithDiagnostic(#[label] pub Span); +fn no_with_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(no-with): Unexpected use of `with` statement.") + .with_help("Do not use the `with` statement.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoWith; @@ -36,7 +35,7 @@ declare_oxc_lint!( impl Rule for NoWith { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::WithStatement(with_statement) = node.kind() { - ctx.diagnostic(NoWithDiagnostic(Span::new( + ctx.diagnostic(no_with_diagnostic(Span::new( with_statement.span.start, with_statement.span.start + 4, ))); diff --git a/crates/oxc_linter/src/rules/eslint/radix.rs b/crates/oxc_linter/src/rules/eslint/radix.rs index ceafe1ccb108..b5fd551bbc81 100644 --- a/crates/oxc_linter/src/rules/eslint/radix.rs +++ b/crates/oxc_linter/src/rules/eslint/radix.rs @@ -2,32 +2,29 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum RadixDiagnostic { - #[diagnostic(severity(warning))] - #[error("eslint(radix): Missing parameters.")] - MissingParameters(#[label] Span), +fn missing_parameters(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(radix): Missing parameters.").with_labels([span0.into()]) +} - #[diagnostic(severity(warning))] - #[error("eslint(radix): Missing radix parameter.")] - MissingRadix(#[label] Span), +fn missing_radix(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(radix): Missing radix parameter.").with_labels([span0.into()]) +} - #[diagnostic(severity(warning))] - #[error("eslint(radix): Redundant radix parameter.")] - RedundantRadix(#[label] Span), +fn redundant_radix(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(radix): Redundant radix parameter.").with_labels([span0.into()]) +} - #[diagnostic(severity(warning))] - #[error("eslint(radix): Invalid radix parameter, must be an integer between 2 and 36.")] - InvalidRadix(#[label] Span), +fn invalid_radix(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint(radix): Invalid radix parameter, must be an integer between 2 and 36.", + ) + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -109,18 +106,18 @@ impl Rule for Radix { impl Radix { fn check_arguments(&self, call_expr: &CallExpression, ctx: &LintContext) { match call_expr.arguments.len() { - 0 => ctx.diagnostic(RadixDiagnostic::MissingParameters(call_expr.span)), + 0 => ctx.diagnostic(missing_parameters(call_expr.span)), 1 => { if matches!(&self.radix_type, RadixType::Always) { - ctx.diagnostic(RadixDiagnostic::MissingRadix(call_expr.span)); + ctx.diagnostic(missing_radix(call_expr.span)); } } _ => { let radix_arg = &call_expr.arguments[1]; if matches!(&self.radix_type, RadixType::AsNeeded) && is_default_radix(radix_arg) { - ctx.diagnostic(RadixDiagnostic::RedundantRadix(radix_arg.span())); + ctx.diagnostic(redundant_radix(radix_arg.span())); } else if !is_valid_radix(radix_arg) { - ctx.diagnostic(RadixDiagnostic::InvalidRadix(radix_arg.span())); + ctx.diagnostic(invalid_radix(radix_arg.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/require_yield.rs b/crates/oxc_linter/src/rules/eslint/require_yield.rs index 2c5fff6e0545..c7399f251198 100644 --- a/crates/oxc_linter/src/rules/eslint/require_yield.rs +++ b/crates/oxc_linter/src/rules/eslint/require_yield.rs @@ -1,17 +1,15 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(require-yield): This generator function does not have 'yield'")] -#[diagnostic(severity(warning))] -struct RequireYieldDiagnostic(#[label] pub Span); +fn require_yield_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(require-yield): This generator function does not have 'yield'") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequireYield; @@ -43,7 +41,7 @@ impl Rule for RequireYield { && func.body.as_ref().is_some_and(|body| !body.statements.is_empty()) { let span = func.id.as_ref().map_or_else(|| func.span, |ident| ident.span); - ctx.diagnostic(RequireYieldDiagnostic(span)); + ctx.diagnostic(require_yield_diagnostic(span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/use_isnan.rs b/crates/oxc_linter/src/rules/eslint/use_isnan.rs index 0d5ef82dc32d..e79bf9d18d4b 100644 --- a/crates/oxc_linter/src/rules/eslint/use_isnan.rs +++ b/crates/oxc_linter/src/rules/eslint/use_isnan.rs @@ -1,35 +1,34 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum UseIsnanDiagnostic { - #[error("eslint(use-isnan): Requires calls to isNaN() when checking for NaN")] - #[diagnostic(severity(warning), help("Use the isNaN function to compare with NaN."))] - ComparisonWithNaN(#[label] Span), - #[error("eslint(use-isnan): Requires calls to isNaN() when checking for NaN")] - #[diagnostic( - severity(warning), - help( - "'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch." +fn comparison_with_na_n(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(use-isnan): Requires calls to isNaN() when checking for NaN") + .with_help("Use the isNaN function to compare with NaN.") + .with_labels([span0.into()]) +} + +fn switch_na_n(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(use-isnan): Requires calls to isNaN() when checking for NaN") + .with_help( + "'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch.", ) - )] - SwitchNaN(#[label] Span), - #[error("eslint(use-isnan): Requires calls to isNaN() when checking for NaN")] - #[diagnostic( - severity(warning), - help("'case NaN' can never match. Use Number.isNaN before the switch.") - )] - CaseNaN(#[label] Span), - #[error("eslint(use-isnan): Requires calls to isNaN() when checking for NaN")] - #[diagnostic(severity(warning), help("Array prototype method '{0}' cannot find NaN."))] - IndexOfNaN(&'static str, #[label] Span), + .with_labels([span0.into()]) +} + +fn case_na_n(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(use-isnan): Requires calls to isNaN() when checking for NaN") + .with_help("'case NaN' can never match. Use Number.isNaN before the switch.") + .with_labels([span0.into()]) +} + +fn index_of_na_n(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(use-isnan): Requires calls to isNaN() when checking for NaN") + .with_help(format!("Array prototype method '{x0}' cannot find NaN.")) + .with_labels([span1.into()]) } #[derive(Debug, Clone)] @@ -81,22 +80,22 @@ impl Rule for UseIsnan { if expr.operator.is_compare() || expr.operator.is_equality() => { if is_nan_identifier(&expr.left) { - ctx.diagnostic(UseIsnanDiagnostic::ComparisonWithNaN(expr.left.span())); + ctx.diagnostic(comparison_with_na_n(expr.left.span())); } if is_nan_identifier(&expr.right) { - ctx.diagnostic(UseIsnanDiagnostic::ComparisonWithNaN(expr.right.span())); + ctx.diagnostic(comparison_with_na_n(expr.right.span())); } } AstKind::SwitchCase(case) if self.enforce_for_switch_case => { if let Some(test) = &case.test { if is_nan_identifier(test) { - ctx.diagnostic(UseIsnanDiagnostic::CaseNaN(test.span())); + ctx.diagnostic(case_na_n(test.span())); } } } AstKind::SwitchStatement(switch) if self.enforce_for_switch_case => { if is_nan_identifier(&switch.discriminant) { - ctx.diagnostic(UseIsnanDiagnostic::SwitchNaN(switch.discriminant.span())); + ctx.diagnostic(switch_na_n(switch.discriminant.span())); } } AstKind::CallExpression(call) if self.enforce_for_index_of => { @@ -105,7 +104,7 @@ impl Rule for UseIsnan { if call.arguments.len() == 1 { if let Some(expr) = call.arguments[0].as_expression() { if is_nan_identifier(expr) { - ctx.diagnostic(UseIsnanDiagnostic::IndexOfNaN(method, expr.span())); + ctx.diagnostic(index_of_na_n(method, expr.span())); } } } @@ -383,88 +382,31 @@ fn test() { ("switch(NaN) { case foo: break; }", Some(serde_json::json!([{}]))), ("switch(foo) { case NaN: break; }", Some(serde_json::json!([{}]))), ("switch(NaN) {}", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), - ( - "switch(NaN) { case foo: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(NaN) { default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(NaN) { case foo: break; default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), + ("switch(NaN) { case foo: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(NaN) { default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(NaN) { case foo: break; default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), ("switch(foo) { case NaN: }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), - ( - "switch(foo) { case NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case (NaN): break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: break; case NaN: break; default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: case NaN: default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: break; case NaN: break; case baz: break; case NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(NaN) { case NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), + ("switch(foo) { case NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case (NaN): break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: break; case NaN: break; default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: case NaN: default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: break; case NaN: break; case baz: break; case NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(NaN) { case NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), ("switch(Number.NaN) { case foo: break; }", None), ("switch(foo) { case Number.NaN: break; }", None), ("switch(Number.NaN) { case foo: break; }", Some(serde_json::json!([{}]))), ("switch(foo) { case Number.NaN: break; }", Some(serde_json::json!([{}]))), ("switch(Number.NaN) {}", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), - ( - "switch(Number.NaN) { case foo: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(Number.NaN) { default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(Number.NaN) { case foo: break; default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case Number.NaN: }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case Number.NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case (Number.NaN): break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: break; case Number.NaN: break; default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: case Number.NaN: default: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(foo) { case bar: break; case NaN: break; case baz: break; case Number.NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), - ( - "switch(Number.NaN) { case Number.NaN: break; }", - Some(serde_json::json!([{ "enforceForSwitchCase": true }])), - ), + ("switch(Number.NaN) { case foo: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(Number.NaN) { default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(Number.NaN) { case foo: break; default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case Number.NaN: }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case Number.NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case (Number.NaN): break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: break; case Number.NaN: break; default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: case Number.NaN: default: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(foo) { case bar: break; case NaN: break; case baz: break; case Number.NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), + ("switch(Number.NaN) { case Number.NaN: break; }", Some(serde_json::json!([{ "enforceForSwitchCase": true }]))), ("foo.indexOf(NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo.lastIndexOf(NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo['indexOf'](NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), @@ -477,15 +419,9 @@ fn test() { ("foo.indexOf(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo.lastIndexOf(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo['indexOf'](Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), - ( - "foo['lastIndexOf'](Number.NaN)", - Some(serde_json::json!([{ "enforceForIndexOf": true }])), - ), + ("foo['lastIndexOf'](Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo().indexOf(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), - ( - "foo.bar.lastIndexOf(Number.NaN)", - Some(serde_json::json!([{ "enforceForIndexOf": true }])), - ), + ("foo.bar.lastIndexOf(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo.indexOf?.(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("foo?.indexOf(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), ("(foo?.indexOf)(Number.NaN)", Some(serde_json::json!([{ "enforceForIndexOf": true }]))), diff --git a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs index 2a5da67d9e82..287398ca2b81 100644 --- a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs +++ b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs @@ -1,8 +1,5 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::UnaryOperator; @@ -10,14 +7,24 @@ use phf::{phf_set, Set}; use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum ValidTypeofDiagnostic { - #[error("eslint(valid-typeof): Typeof comparisons should be to string literals.")] - #[diagnostic(severity(warning))] - NotString(#[help] Option<&'static str>, #[label] Span), - #[error("eslint(valid-typeof): Invalid typeof comparison value.")] - #[diagnostic(severity(warning))] - InvalidValue(#[help] Option<&'static str>, #[label] Span), +fn not_string(x0: Option<&'static str>, span1: Span) -> OxcDiagnostic { + let mut d = OxcDiagnostic::warning( + "eslint(valid-typeof): Typeof comparisons should be to string literals.", + ) + .with_labels([span1.into()]); + if let Some(x) = x0 { + d = d.with_help(x); + } + d +} + +fn invalid_value(x0: Option<&'static str>, span1: Span) -> OxcDiagnostic { + let mut d = OxcDiagnostic::warning("eslint(valid-typeof): Invalid typeof comparison value.") + .with_labels([span1.into()]); + if let Some(x) = x0 { + d = d.with_help(x); + } + d } #[derive(Debug, Clone, Default)] @@ -75,7 +82,7 @@ impl Rule for ValidTypeof { if let Expression::StringLiteral(lit) = sibling { if !VALID_TYPES.contains(lit.value.as_str()) { - ctx.diagnostic(ValidTypeofDiagnostic::InvalidValue(None, sibling.span())); + ctx.diagnostic(invalid_value(None, sibling.span())); } return; } @@ -83,7 +90,7 @@ impl Rule for ValidTypeof { if let Expression::TemplateLiteral(template) = sibling { if template.expressions.is_empty() { if template.quasi().is_some_and(|value| !VALID_TYPES.contains(value.as_str())) { - ctx.diagnostic(ValidTypeofDiagnostic::InvalidValue(None, sibling.span())); + ctx.diagnostic(invalid_value(None, sibling.span())); } return; } @@ -93,12 +100,12 @@ impl Rule for ValidTypeof { if ident.name == "undefined" && ctx.semantic().is_reference_to_global_variable(ident) { ctx.diagnostic_with_fix( if self.require_string_literals { - ValidTypeofDiagnostic::NotString( + not_string( Some("Use `\"undefined\"` instead of `undefined`."), sibling.span(), ) } else { - ValidTypeofDiagnostic::InvalidValue( + invalid_value( Some("Use `\"undefined\"` instead of `undefined`."), sibling.span(), ) @@ -112,7 +119,7 @@ impl Rule for ValidTypeof { if self.require_string_literals && !matches!(sibling, Expression::UnaryExpression(unary) if unary.operator == UnaryOperator::Typeof) { - ctx.diagnostic(ValidTypeofDiagnostic::NotString(None, sibling.span())); + ctx.diagnostic(not_string(None, sibling.span())); } } diff --git a/crates/oxc_linter/src/rules/import/default.rs b/crates/oxc_linter/src/rules/import/default.rs index 6ad4830f5443..9f2f45827f9b 100644 --- a/crates/oxc_linter/src/rules/import/default.rs +++ b/crates/oxc_linter/src/rules/import/default.rs @@ -1,17 +1,18 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::module_record::ImportImportName; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(default): No default export found in imported module {0:?}")] -#[diagnostic(severity(warning), help("does {0:?} have the default export?"))] -struct DefaultDiagnostic(String, #[label] pub Span); +fn default_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(default): No default export found in imported module {x0:?}" + )) + .with_help(format!("does {x0:?} have the default export?")) + .with_labels([span1.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -53,7 +54,7 @@ impl Rule for Default { if remote_module_record_ref.export_default.is_none() && !remote_module_record_ref.exported_bindings.contains_key("default") { - ctx.diagnostic(DefaultDiagnostic(specifier.to_string(), default_span)); + ctx.diagnostic(default_diagnostic(specifier, default_span)); } } } diff --git a/crates/oxc_linter/src/rules/import/export.rs b/crates/oxc_linter/src/rules/import/export.rs index da72b4408224..904ae80ac002 100644 --- a/crates/oxc_linter/src/rules/import/export.rs +++ b/crates/oxc_linter/src/rules/import/export.rs @@ -1,10 +1,6 @@ use std::path::PathBuf; -use oxc_diagnostics::{ - miette::{self, miette, Diagnostic, LabeledSpan}, - thiserror::{self, Error}, - Severity, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use oxc_semantic::ModuleRecord; use oxc_span::{CompactStr, Span}; @@ -12,11 +8,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -enum ExportDiagnostic { - #[error("eslint-plugin-import(export): No named exports found in module '{1}'")] - #[diagnostic(severity(warning))] - NoNamedExport(#[label] Span, CompactStr), +fn no_named_export(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(export): No named exports found in module '{x1}'" + )) + .with_labels([span0.into()]) } /// @@ -48,7 +44,9 @@ impl Rule for Export { module_record.star_export_entries.iter().for_each(|star_export_entry| { let mut export_names = FxHashSet::default(); - let Some(module_request) = &star_export_entry.module_request else { return }; + let Some(module_request) = &star_export_entry.module_request else { + return; + }; let Some(remote_module_record_ref) = module_record.loaded_modules.get(module_request.name()) else { @@ -62,10 +60,7 @@ impl Rule for Export { ); if export_names.is_empty() { - ctx.diagnostic(ExportDiagnostic::NoNamedExport( - module_request.span(), - module_request.name().clone(), - )); + ctx.diagnostic(no_named_export(module_request.span(), module_request.name())); } else { all_export_names.insert(star_export_entry.span, export_names); } @@ -93,11 +88,12 @@ impl Rule for Export { spans.push(*span); let labels = spans.into_iter().map(LabeledSpan::underline).collect::>(); - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = labels, - "eslint-plugin-import(export): Multiple exports of name '{name}'." - )); + ctx.diagnostic( + OxcDiagnostic::warning(format!( + "eslint-plugin-import(export): Multiple exports of name '{name}'." + )) + .with_labels(labels), + ); } } @@ -106,12 +102,12 @@ impl Rule for Export { if let Some(span) = module_record.export_default { spans.push(span); let labels = spans.into_iter().map(LabeledSpan::underline).collect::>(); - - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = labels, - "eslint-plugin-import(export): Multiple default exports." - )); + ctx.diagnostic( + OxcDiagnostic::warning( + "eslint-plugin-import(export): Multiple default exports.", + ) + .with_labels(labels), + ); } } } diff --git a/crates/oxc_linter/src/rules/import/named.rs b/crates/oxc_linter/src/rules/import/named.rs index 5784daa1b438..ade4c5d3574d 100644 --- a/crates/oxc_linter/src/rules/import/named.rs +++ b/crates/oxc_linter/src/rules/import/named.rs @@ -1,17 +1,16 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::module_record::{ExportImportName, ImportImportName}; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(named): named import {0:?} not found")] -#[diagnostic(severity(warning), help("does {1:?} have the export {0:?}?"))] -struct NamedDiagnostic(String, String, #[label] pub Span); +fn named_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-import(named): named import {x0:?} not found")) + .with_help(format!("does {x1:?} have the export {x0:?}?")) + .with_labels([span2.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -67,11 +66,7 @@ impl Rule for Named { continue; } - ctx.diagnostic(NamedDiagnostic( - import_name.name().to_string(), - specifier.to_string(), - import_name.span(), - )); + ctx.diagnostic(named_diagnostic(import_name.name(), specifier, import_name.span())); } for export_entry in &module_record.indirect_export_entries { @@ -96,11 +91,7 @@ impl Rule for Named { if remote_module_record.exported_bindings.contains_key(name) { continue; } - ctx.diagnostic(NamedDiagnostic( - name.to_string(), - specifier.to_string(), - import_name.span(), - )); + ctx.diagnostic(named_diagnostic(name, specifier, import_name.span())); } } } diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 2d4b8bda530a..8941548f62c9 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -4,10 +4,7 @@ use oxc_ast::{ ast::{BindingPatternKind, ObjectPattern}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, ModuleRecord}; use oxc_span::{CompactStr, GetSpan, Span}; @@ -15,23 +12,29 @@ use oxc_syntax::module_record::{ExportExportName, ExportImportName, ImportImport use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -enum NamespaceDiagnostic { - #[error("eslint-plugin-import(namespace): {1:?} not found in imported namespace {2:?}.")] - #[diagnostic(severity(warning))] - NoExport(#[label] Span, CompactStr, String), - #[error( - "eslint-plugin-import(namespace): {1:?} not found in deeply imported namespace {2:?}." - )] - #[diagnostic(severity(warning))] - NoExportInDeeplyImportedNamespace(#[label] Span, CompactStr, String), - #[error("eslint-plugin-import(namespace): Unable to validate computed reference to imported namespace {1:?} - .")] - #[diagnostic(severity(warning))] - ComputedReference(#[label] Span, CompactStr), - #[error("eslint-plugin-import(namespace): Assignment to member of namespace {1:?}.'")] - #[diagnostic(severity(warning))] - Assignment(#[label] Span, CompactStr), +fn no_export(span0: Span, x1: &str, x2: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(namespace): {x1:?} not found in imported namespace {x2:?}." + )) + .with_labels([span0.into()]) +} + +fn no_export_in_deeply_imported_namespace(span0: Span, x1: &str, x2: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(namespace): {x1:?} not found in deeply imported namespace {x2:?}." + )) + .with_labels([span0.into()]) +} + +fn computed_reference(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-import(namespace): Unable to validate computed reference to imported namespace {x1:?}.")).with_labels([span0.into()]) +} + +fn assignment(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(namespace): Assignment to member of namespace {x1:?}.'" + )) + .with_labels([span0.into()]) } /// @@ -116,17 +119,11 @@ impl Rule for Namespace { ctx.nodes().parent_kind(node.id()), Some(AstKind::SimpleAssignmentTarget(_)) ) { - ctx.diagnostic(NamespaceDiagnostic::Assignment( - member.span(), - name.clone(), - )); + ctx.diagnostic(assignment(member.span(), name)); }; if !self.allow_computed && member.is_computed() { - return ctx.diagnostic(NamespaceDiagnostic::ComputedReference( - member.span(), - name.clone(), - )); + return ctx.diagnostic(computed_reference(member.span(), name)); } check_deep_namespace_for_node( @@ -143,13 +140,7 @@ impl Rule for Namespace { { check_binding_exported( &expr.property.name, - || { - NamespaceDiagnostic::NoExport( - expr.property.span, - expr.property.name.to_compact_str(), - source.clone(), - ) - }, + || no_export(expr.property.span, &expr.property.name, &source), &module, ctx, ); @@ -199,7 +190,7 @@ fn get_module_request_name(name: &str, module_record: &ModuleRecord) -> Option { - return name_span.name().as_str() == name + name_span.name().as_str() == name && module_record.import_entries.iter().any(|entry| { entry.local_name.name().as_str() == name && entry.import_name.is_namespace_object() @@ -211,13 +202,13 @@ fn get_module_request_name(name: &str, module_record: &ModuleRecord) -> Option 1 { - NamespaceDiagnostic::NoExportInDeeplyImportedNamespace( - span, - name.into(), - namespaces.join("."), - ) + no_export_in_deeply_imported_namespace(span, name, &namespaces.join(".")) } else { - NamespaceDiagnostic::NoExport(span, name.into(), source.to_string()) + no_export(span, name, source) } }, module, @@ -298,17 +285,13 @@ fn check_deep_namespace_for_object_pattern( &name, || { if namespaces.len() > 1 { - NamespaceDiagnostic::NoExportInDeeplyImportedNamespace( + no_export_in_deeply_imported_namespace( property.key.span(), - name.clone(), - namespaces.join("."), + &name, + &namespaces.join("."), ) } else { - NamespaceDiagnostic::NoExport( - property.key.span(), - name.clone(), - source.to_string(), - ) + no_export(property.key.span(), &name, source) } }, module, @@ -319,7 +302,7 @@ fn check_deep_namespace_for_object_pattern( fn check_binding_exported( name: &str, - get_diagnostic: impl FnOnce() -> NamespaceDiagnostic, + get_diagnostic: impl FnOnce() -> OxcDiagnostic, module: &ModuleRecord, ctx: &LintContext<'_>, ) { diff --git a/crates/oxc_linter/src/rules/import/no_amd.rs b/crates/oxc_linter/src/rules/import/no_amd.rs index 482598aab070..0c283d325b90 100644 --- a/crates/oxc_linter/src/rules/import/no_amd.rs +++ b/crates/oxc_linter/src/rules/import/no_amd.rs @@ -1,18 +1,19 @@ use oxc_ast::ast::{Argument, Expression}; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls.")] -#[diagnostic(severity(warning), help("Expected imports instead of AMD {1}()"))] -struct NoAmdDiagnostic(#[label] pub Span, CompactStr); +fn no_amd_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls.", + ) + .with_help(format!("Expected imports instead of AMD {x1}()")) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoAmd; @@ -53,10 +54,7 @@ impl Rule for NoAmd { } if let Argument::ArrayExpression(_) = call_expr.arguments[0] { - ctx.diagnostic(NoAmdDiagnostic( - identifier.span, - identifier.name.to_compact_str(), - )); + ctx.diagnostic(no_amd_diagnostic(identifier.span, identifier.name.as_str())); } } } diff --git a/crates/oxc_linter/src/rules/import/no_cycle.rs b/crates/oxc_linter/src/rules/import/no_cycle.rs index 183a3be7a620..de620ba3cbe0 100644 --- a/crates/oxc_linter/src/rules/import/no_cycle.rs +++ b/crates/oxc_linter/src/rules/import/no_cycle.rs @@ -1,11 +1,8 @@ #![allow(clippy::cast_possible_truncation)] +use oxc_diagnostics::OxcDiagnostic; use std::{ffi::OsStr, path::Component, sync::Arc}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; use oxc_syntax::{ @@ -15,10 +12,11 @@ use oxc_syntax::{ use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-cycle): Dependency cycle detected")] -#[diagnostic(severity(warning), help("These paths form a cycle: \n{1}"))] -struct NoCycleDiagnostic(#[label] Span, String); +fn no_cycle_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-import(no-cycle): Dependency cycle detected") + .with_help(format!("These paths form a cycle: \n{x1}")) + .with_labels([span0.into()]) +} /// #[derive(Debug, Clone)] @@ -154,7 +152,7 @@ impl Rule for NoCycle { }) .collect::>() .join("\n"); - ctx.diagnostic(NoCycleDiagnostic(span, help)); + ctx.diagnostic(no_cycle_diagnostic(span, &help)); } } } diff --git a/crates/oxc_linter/src/rules/import/no_default_export.rs b/crates/oxc_linter/src/rules/import/no_default_export.rs index 8d8daba9239d..b5c4e9bb79f8 100644 --- a/crates/oxc_linter/src/rules/import/no_default_export.rs +++ b/crates/oxc_linter/src/rules/import/no_default_export.rs @@ -1,16 +1,14 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-default-export): Prefer named exports")] -#[diagnostic(severity(warning))] -struct NoDefaultExportDiagnostic(#[label] Span); +fn no_default_export_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-import(no-default-export): Prefer named exports") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDefaultExport; @@ -53,7 +51,7 @@ impl Rule for NoDefaultExport { } fn write_diagnostic(ctx: &LintContext<'_>, span: Span) { - ctx.diagnostic(NoDefaultExportDiagnostic(span)); + ctx.diagnostic(no_default_export_diagnostic(span)); } fn write_diagnostic_optional(ctx: &LintContext<'_>, span_option: Option) { if let Some(span) = span_option { diff --git a/crates/oxc_linter/src/rules/import/no_deprecated.rs b/crates/oxc_linter/src/rules/import/no_deprecated.rs index 334dcb10f71d..61c511eda019 100644 --- a/crates/oxc_linter/src/rules/import/no_deprecated.rs +++ b/crates/oxc_linter/src/rules/import/no_deprecated.rs @@ -1,7 +1,4 @@ -// use oxc_diagnostics::{ -// miette::{self, Diagnostic}, -// thiserror::Error, -// }; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; // use oxc_span::{CompactStr, Span}; diff --git a/crates/oxc_linter/src/rules/import/no_duplicates.rs b/crates/oxc_linter/src/rules/import/no_duplicates.rs index 2fc727e879d8..1cb81ed8cb39 100644 --- a/crates/oxc_linter/src/rules/import/no_duplicates.rs +++ b/crates/oxc_linter/src/rules/import/no_duplicates.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use oxc_diagnostics::miette::{miette, LabeledSpan, Severity}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use oxc_syntax::module_record::{ImportImportName, RequestedModule}; @@ -51,11 +51,7 @@ impl Rule for NoDuplicates { .iter() .map(|requested_module| LabeledSpan::underline(requested_module.span())) .collect::>(); - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = labels, - "eslint-plugin-import(no-duplicates): Forbid repeated import of the same module in multiple places" - )); + ctx.diagnostic(OxcDiagnostic::warning("eslint-plugin-import(no-duplicates): Forbid repeated import of the same module in multiple places").with_labels(labels)); } } }; diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default.rs b/crates/oxc_linter/src/rules/import/no_named_as_default.rs index d453318514a9..53a73da93a3b 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default.rs @@ -1,17 +1,16 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::module_record::ImportImportName; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-named-as-default): Module {2:?} has named export {1:?}")] -#[diagnostic(severity(warning), help("Using default import as {1:?} can be confusing. Use another name for default import to avoid confusion."))] -struct NoNamedAsDefaultDiagnostic(#[label] pub Span, String, String); +fn no_named_as_default_diagnostic(span0: Span, x1: &str, x2: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-import(no-named-as-default): Module {x2:?} has named export {x1:?}")) + .with_help(format!("Using default import as {x1:?} can be confusing. Use another name for default import to avoid confusion.")) + .with_labels([span0.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -57,10 +56,10 @@ impl Rule for NoNamedAsDefault { let import_name = import_entry.local_name.name(); if remote_module_record_ref.exported_bindings.contains_key(import_name) { - ctx.diagnostic(NoNamedAsDefaultDiagnostic( + ctx.diagnostic(no_named_as_default_diagnostic( *import_span, - import_name.to_string(), - import_entry.module_request.name().to_string(), + import_name, + import_entry.module_request.name(), )); } } diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index 45edaf96dad9..1db024b49906 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -1,4 +1,6 @@ -#![allow(clippy::significant_drop_tightening)] +use oxc_diagnostics::OxcDiagnostic; +// #![allow(clippy::significant_drop_tightening)] + use std::collections::HashMap; use dashmap::mapref::one::Ref; @@ -6,10 +8,6 @@ use oxc_ast::{ ast::{BindingPatternKind, Expression, IdentifierReference, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; use oxc_span::{CompactStr, Span}; @@ -17,10 +15,18 @@ use oxc_syntax::module_record::ImportImportName; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-named-as-default-member): {1:?} also has a named export {2:?}")] -#[diagnostic(severity(warning), help("Check if you meant to write `import {{{2:}}} from {3:?}`"))] -struct NoNamedAsDefaultMemberDignostic(#[label] pub Span, String, String, String); +fn no_named_as_default_member_dignostic( + span0: Span, + x1: &str, + x2: &str, + x3: &str, +) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-import(no-named-as-default-member): {x1:?} also has a named export {x2:?}" + )) + .with_help(format!("Check if you meant to write `import {{{x2:}}} from {x3:?}`")) + .with_labels([span0.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -102,15 +108,15 @@ impl Rule for NoNamedAsDefaultMember { return; }; if let Some(module_name) = get_external_module_name_if_has_entry(ident, prop_str) { - ctx.diagnostic(NoNamedAsDefaultMemberDignostic( + ctx.diagnostic(no_named_as_default_member_dignostic( match member_expr { MemberExpression::ComputedMemberExpression(it) => it.span, MemberExpression::StaticMemberExpression(it) => it.span, MemberExpression::PrivateFieldExpression(it) => it.span, }, - ident.name.to_string(), - prop_str.to_string(), - module_name, + &ident.name, + prop_str, + &module_name, )); }; }; @@ -133,11 +139,11 @@ impl Rule for NoNamedAsDefaultMember { if let Some(module_name) = get_external_module_name_if_has_entry(ident, &name) { - ctx.diagnostic(NoNamedAsDefaultMemberDignostic( + ctx.diagnostic(no_named_as_default_member_dignostic( decl.span, - ident.name.to_string(), - name.to_string(), - module_name, + &ident.name, + &name, + &module_name, )); } } diff --git a/crates/oxc_linter/src/rules/import/no_self_import.rs b/crates/oxc_linter/src/rules/import/no_self_import.rs index c877d3ea0ec7..5c8e5ba0f568 100644 --- a/crates/oxc_linter/src/rules/import/no_self_import.rs +++ b/crates/oxc_linter/src/rules/import/no_self_import.rs @@ -1,16 +1,16 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-import(no-self-import): module importing itself is not allowed")] -#[diagnostic(severity(warning))] -struct NoSelfImportDiagnostic(#[label] pub Span); +fn no_self_import_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-import(no-self-import): module importing itself is not allowed", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoSelfImport; @@ -41,7 +41,7 @@ impl Rule for NoSelfImport { }; if remote_module_record_ref.value().resolved_absolute_path == *resolved_absolute_path { for requested_module in requested_modules { - ctx.diagnostic(NoSelfImportDiagnostic(requested_module.span())); + ctx.diagnostic(no_self_import_diagnostic(requested_module.span())); } } } diff --git a/crates/oxc_linter/src/rules/import/no_unused_modules.rs b/crates/oxc_linter/src/rules/import/no_unused_modules.rs index 85aca2301d3b..e38d87470baf 100644 --- a/crates/oxc_linter/src/rules/import/no_unused_modules.rs +++ b/crates/oxc_linter/src/rules/import/no_unused_modules.rs @@ -1,17 +1,11 @@ -use oxc_diagnostics::{ - miette::{self, diagnostic, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -enum NoUnusedModulesDiagnostic { - #[error("eslint-plugin-import(no-unused-modules): No exports found")] - #[diagnostic(severity(warning))] - NoExportsFound(#[label] Span), +fn no_exports_found(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-import(no-unused-modules): No exports found") + .with_labels([span0.into()]) } /// @@ -50,7 +44,7 @@ impl Rule for NoUnusedModules { fn run_once(&self, ctx: &LintContext<'_>) { let module_record = ctx.semantic().module_record(); if self.missing_exports && module_record.local_export_entries.is_empty() { - ctx.diagnostic(NoUnusedModulesDiagnostic::NoExportsFound(Span::new(0, 0))); + ctx.diagnostic(no_exports_found(Span::new(0, 0))); } if self.unused_exports { // TODO: implement unused exports diff --git a/crates/oxc_linter/src/rules/jest/expect_expect.rs b/crates/oxc_linter/src/rules/jest/expect_expect.rs index eda55d8f35b5..27a33daa1aab 100644 --- a/crates/oxc_linter/src/rules/jest/expect_expect.rs +++ b/crates/oxc_linter/src/rules/jest/expect_expect.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{CallExpression, Expression, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use regex::Regex; @@ -20,10 +18,11 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(expect-expect): Test has no assertions")] -#[diagnostic(severity(warning), help("Add assertion(s) in this Test"))] -struct ExpectExpectDiagnostic(#[label] pub Span); +fn expect_expect_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(expect-expect): Test has no assertions") + .with_help("Add assertion(s) in this Test") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct ExpectExpect(Box); @@ -131,7 +130,7 @@ fn run<'a>( let has_assert_function = check_arguments(call_expr, &rule.assert_function_names, ctx); if !has_assert_function { - ctx.diagnostic(ExpectExpectDiagnostic(call_expr.callee.span())); + ctx.diagnostic(expect_expect_diagnostic(call_expr.callee.span())); } } } @@ -251,18 +250,9 @@ fn test() { ", Some(serde_json::json!([{ "assertFunctionNames": ["expect", "foo"] }])), ), - ( - "it('should return undefined',() => expectSaga(mySaga).returns());", - Some(serde_json::json!([{ "assertFunctionNames": ["expectSaga"] }])), - ), - ( - "test('verifies expect method call', () => expect$(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["expect\\$"] }])), - ), - ( - "test('verifies expect method call', () => new Foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["Foo.expect"] }])), - ), + ("it('should return undefined',() => expectSaga(mySaga).returns());", Some(serde_json::json!([{ "assertFunctionNames": ["expectSaga"] }]))), + ("test('verifies expect method call', () => expect$(123));", Some(serde_json::json!([{ "assertFunctionNames": ["expect\\$"] }]))), + ("test('verifies expect method call', () => new Foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["Foo.expect"] }]))), ( " test('verifies deep expect method call', () => { @@ -308,46 +298,16 @@ fn test() { ", Some(serde_json::json!([{ "additionalTestBlockFunctions": ["theoretically"] }])), ), - ( - "test('should pass *', () => expect404ToBeLoaded());", - Some(serde_json::json!([{ "assertFunctionNames": ["expect*"] }])), - ), - ( - "test('should pass *', () => expect.toHaveStatus404());", - Some(serde_json::json!([{ "assertFunctionNames": ["expect.**"] }])), - ), - ( - "test('should pass', () => tester.foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["tester.*.expect"] }])), - ), - ( - "test('should pass **', () => tester.foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["**"] }])), - ), - ( - "test('should pass *', () => tester.foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["*"] }])), - ), - ( - "test('should pass', () => tester.foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["tester.**"] }])), - ), - ( - "test('should pass', () => tester.foo().expect(123));", - Some(serde_json::json!([{ "assertFunctionNames": ["tester.*"] }])), - ), - ( - "test('should pass', () => tester.foo().bar().expectIt(456));", - Some(serde_json::json!([{ "assertFunctionNames": ["tester.**.expect*"] }])), - ), - ( - "test('should pass', () => request.get().foo().expect(456));", - Some(serde_json::json!([{ "assertFunctionNames": ["request.**.expect"] }])), - ), - ( - "test('should pass', () => request.get().foo().expect(456));", - Some(serde_json::json!([{ "assertFunctionNames": ["request.**.e*e*t"] }])), - ), + ("test('should pass *', () => expect404ToBeLoaded());", Some(serde_json::json!([{ "assertFunctionNames": ["expect*"] }]))), + ("test('should pass *', () => expect.toHaveStatus404());", Some(serde_json::json!([{ "assertFunctionNames": ["expect.**"] }]))), + ("test('should pass', () => tester.foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["tester.*.expect"] }]))), + ("test('should pass **', () => tester.foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["**"] }]))), + ("test('should pass *', () => tester.foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["*"] }]))), + ("test('should pass', () => tester.foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["tester.**"] }]))), + ("test('should pass', () => tester.foo().expect(123));", Some(serde_json::json!([{ "assertFunctionNames": ["tester.*"] }]))), + ("test('should pass', () => tester.foo().bar().expectIt(456));", Some(serde_json::json!([{ "assertFunctionNames": ["tester.**.expect*"] }]))), + ("test('should pass', () => request.get().foo().expect(456));", Some(serde_json::json!([{ "assertFunctionNames": ["request.**.expect"] }]))), + ("test('should pass', () => request.get().foo().expect(456));", Some(serde_json::json!([{ "assertFunctionNames": ["request.**.e*e*t"] }]))), ( " import { test } from '@jest/globals'; diff --git a/crates/oxc_linter/src/rules/jest/max_expects.rs b/crates/oxc_linter/src/rules/jest/max_expects.rs index b69a56f66a83..afc322327fbf 100644 --- a/crates/oxc_linter/src/rules/jest/max_expects.rs +++ b/crates/oxc_linter/src/rules/jest/max_expects.rs @@ -1,3 +1,5 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::{collections::HashMap, hash::BuildHasherDefault}; use crate::{ @@ -6,23 +8,15 @@ use crate::{ utils::{collect_possible_jest_call_node, PossibleJestNode}, }; use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use rustc_hash::{FxHashMap, FxHasher}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body." -)] -#[diagnostic( - severity(warning), - help("Too many assertion calls ({0:?}) - maximum allowed is {1:?}") -)] -pub struct ExceededMaxAssertion(pub usize, pub usize, #[label] pub Span); +fn exceeded_max_assertion(x0: usize, x1: usize, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body.") + .with_help(format!("Too many assertion calls ({x0:?}) - maximum allowed is {x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Clone)] pub struct MaxExpects { @@ -113,7 +107,7 @@ impl MaxExpects { if let Some(count) = count_map.get(&position) { if count > &self.max { - ctx.diagnostic(ExceededMaxAssertion(*count, self.max, ident.span)); + ctx.diagnostic(exceeded_max_assertion(*count, self.max, ident.span)); } else { count_map.insert(position, count + 1); } diff --git a/crates/oxc_linter/src/rules/jest/no_alias_methods.rs b/crates/oxc_linter/src/rules/jest/no_alias_methods.rs index 6e6eb1037501..b38089961a4c 100644 --- a/crates/oxc_linter/src/rules/jest/no_alias_methods.rs +++ b/crates/oxc_linter/src/rules/jest/no_alias_methods.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -13,10 +11,11 @@ use crate::{ utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-alias-methods): Unexpected alias {0:?}")] -#[diagnostic(severity(warning), help("Replace {0:?} with its canonical name of {1:?}"))] -struct NoAliasMethodsDiagnostic(pub &'static str, pub &'static str, #[label] pub Span); +fn no_alias_methods_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(no-alias-methods): Unexpected alias {x0:?}")) + .with_help(format!("Replace {x0:?} with its canonical name of {x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoAliasMethods; @@ -82,7 +81,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) } ctx.diagnostic_with_fix( - NoAliasMethodsDiagnostic(name, canonical_name, matcher.span), + no_alias_methods_diagnostic(name, canonical_name, matcher.span), || Fix::new(canonical_name, Span::new(start, end)), ); } diff --git a/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs b/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs index 375d9dfddeb4..4a6bb05830f8 100644 --- a/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs +++ b/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs @@ -1,18 +1,19 @@ use lazy_static::lazy_static; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use regex::Regex; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-commented-out-tests): Some tests seem to be commented")] -#[diagnostic(severity(warning), help("Remove or uncomment this comment"))] -struct NoCommentedOutTestsDiagnostic(#[label] pub Span); +fn no_commented_out_tests_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-commented-out-tests): Some tests seem to be commented", + ) + .with_help("Remove or uncomment this comment") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoCommentedOutTests; @@ -62,7 +63,7 @@ impl Rule for NoCommentedOutTests { }); for span in commented_tests { - ctx.diagnostic(NoCommentedOutTestsDiagnostic(span)); + ctx.diagnostic(no_commented_out_tests_diagnostic(span)); } } } 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 2ef41084c6ad..56e6287f48df 100644 --- a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs +++ b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs @@ -1,10 +1,8 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::collections::HashMap; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, AstNodeId}; use oxc_span::Span; @@ -18,10 +16,13 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-conditional-expect): Unexpected conditional expect")] -#[diagnostic(severity(warning), help("Avoid calling `expect` conditionally`"))] -struct NoConditionalExpectDiagnostic(#[label] pub Span); +fn no_conditional_expect_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-conditional-expect): Unexpected conditional expect", + ) + .with_help("Avoid calling `expect` conditionally`") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoConditionalExpect; @@ -84,7 +85,7 @@ fn run<'a>( let has_condition_or_catch = check_parents(node, id_nodes_mapping, ctx, false); if has_condition_or_catch { - ctx.diagnostic(NoConditionalExpectDiagnostic(jest_fn_call.head.span)); + ctx.diagnostic(no_conditional_expect_diagnostic(jest_fn_call.head.span)); } } } diff --git a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs index 77de4d93045e..f47ec38ab871 100644 --- a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs +++ b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs @@ -1,10 +1,8 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::collections::HashMap; use oxc_ast::{ast::MemberExpression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, AstNodeId, ReferenceId}; use oxc_span::{GetSpan, Span}; @@ -15,28 +13,23 @@ use crate::{ utils::{collect_possible_jest_call_node, parse_jest_fn_call, PossibleJestNode}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-confusing-set-timeout)")] -#[diagnostic(severity(warning), help("`jest.setTimeout` should be call in `global` scope"))] -struct NoGlobalSetTimeoutDiagnostic(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-confusing-set-timeout)")] -#[diagnostic( - severity(warning), - help( - "Do not call `jest.setTimeout` multiple times, as only the last call will have an effect" - ) -)] -struct NoMultipleSetTimeoutsDiagnostic(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-confusing-set-timeout)")] -#[diagnostic( - severity(warning), - help("`jest.setTimeout` should be placed before any other jest methods") -)] -struct NoUnorderSetTimeoutDiagnostic(#[label] pub Span); +fn no_global_set_timeout_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-confusing-set-timeout)") + .with_help("`jest.setTimeout` should be call in `global` scope") + .with_labels([span0.into()]) +} + +fn no_multiple_set_timeouts_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-confusing-set-timeout)") + .with_help("Do not call `jest.setTimeout` multiple times, as only the last call will have an effect") + .with_labels([span0.into()]) +} + +fn no_unorder_set_timeout_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-confusing-set-timeout)") + .with_help("`jest.setTimeout` should be placed before any other jest methods") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoConfusingSetTimeout; @@ -173,7 +166,7 @@ fn handle_jest_set_time_out<'a>( if is_jest_fn_call(parent_node, id_to_jest_node_map, ctx) { for (jest_reference_id, span) in jest_reference_id_list { if jest_reference_id > &reference_id { - ctx.diagnostic(NoUnorderSetTimeoutDiagnostic(*span)); + ctx.diagnostic(no_unorder_set_timeout_diagnostic(*span)); } } } @@ -190,11 +183,11 @@ fn handle_jest_set_time_out<'a>( if expr.property.name == "setTimeout" { if !scopes.get_flags(parent_node.scope_id()).is_top() { - ctx.diagnostic(NoGlobalSetTimeoutDiagnostic(member_expr.span())); + ctx.diagnostic(no_global_set_timeout_diagnostic(member_expr.span())); } if *seen_jest_set_timeout { - ctx.diagnostic(NoMultipleSetTimeoutsDiagnostic(member_expr.span())); + ctx.diagnostic(no_multiple_set_timeouts_diagnostic(member_expr.span())); } else { *seen_jest_set_timeout = true; } @@ -447,7 +440,7 @@ fn test() { jest.setTimeout(800); jest.setTimeout(900); ", - None + None, ), ( " diff --git a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs index 2cadbd68a519..a6f8626428d0 100644 --- a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs +++ b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use phf::{phf_map, Map}; @@ -10,10 +8,13 @@ use std::borrow::Cow; use crate::{context::LintContext, fixer::Fix, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-deprecated-functions): Disallow use of deprecated functions")] -#[diagnostic(severity(warning), help("{0:?} has been deprecated in favor of {1:?}"))] -pub struct DeprecatedFunction(pub String, pub String, #[label] pub Span); +fn deprecated_function(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-deprecated-functions): Disallow use of deprecated functions", + ) + .with_help(format!("{x0:?} has been deprecated in favor of {x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct JestConfig { @@ -127,7 +128,7 @@ impl Rule for NoDeprecatedFunctions { if let Some((base_version, replacement)) = DEPRECATED_FUNCTIONS_MAP.get(&node_name) { if jest_version_num >= *base_version { ctx.diagnostic_with_fix( - DeprecatedFunction(node_name, (*replacement).to_string(), mem_expr.span()), + deprecated_function(&node_name, replacement, mem_expr.span()), || Fix::new(*replacement, mem_expr.span()), ); } diff --git a/crates/oxc_linter/src/rules/jest/no_disabled_tests.rs b/crates/oxc_linter/src/rules/jest/no_disabled_tests.rs index 06e416df5e30..eb03a57c98c3 100644 --- a/crates/oxc_linter/src/rules/jest/no_disabled_tests.rs +++ b/crates/oxc_linter/src/rules/jest/no_disabled_tests.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -55,10 +53,11 @@ declare_oxc_lint!( correctness ); -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-disabled-tests): {0:?}")] -#[diagnostic(severity(warning), help("{1:?}"))] -struct NoDisabledTestsDiagnostic(&'static str, &'static str, #[label] pub Span); +fn no_disabled_tests_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(no-disabled-tests): {x0:?}")) + .with_help(format!("{x1:?}")) + .with_labels([span2.into()]) +} enum Message { MissingFunction, @@ -105,7 +104,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) && members.iter().all(|member| member.is_name_unequal("todo")) { let (error, help) = Message::MissingFunction.details(); - ctx.diagnostic(NoDisabledTestsDiagnostic(error, help, call_expr.span)); + ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.span)); return; } @@ -117,7 +116,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) } else { Message::DisabledTestWithX.details() }; - ctx.diagnostic(NoDisabledTestsDiagnostic(error, help, call_expr.callee.span())); + ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.callee.span())); return; } @@ -129,7 +128,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) } else { Message::DisabledTestWithSkip.details() }; - ctx.diagnostic(NoDisabledTestsDiagnostic(error, help, call_expr.callee.span())); + ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.callee.span())); } } else if let Expression::Identifier(ident) = &call_expr.callee { if ident.name.as_str() == "pending" @@ -137,7 +136,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { // `describe('foo', function () { pending() })` let (error, help) = Message::Pending.details(); - ctx.diagnostic(NoDisabledTestsDiagnostic(error, help, call_expr.span)); + ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.span)); } } } @@ -167,22 +166,10 @@ fn test() { ("testSomething()", None), ("xitSomethingElse()", None), ("xitiViewMap()", None), - ( - "import { pending } from 'actions'; test('foo', () => { expect(pending()).toEqual({}) })", - None, - ), - ( - "const { pending } = require('actions'); test('foo', () => { expect(pending()).toEqual({}) })", - None, - ), - ( - "test('foo', () => { const pending = getPending(); expect(pending()).toEqual({}) })", - None, - ), - ( - "test('foo', () => { expect(pending()).toEqual({}) }); function pending() { return {} }", - None, - ), + ("import { pending } from 'actions'; test('foo', () => { expect(pending()).toEqual({}) })", None), + ("const { pending } = require('actions'); test('foo', () => { expect(pending()).toEqual({}) })", None), + ("test('foo', () => { const pending = getPending(); expect(pending()).toEqual({}) })", None), + ("test('foo', () => { expect(pending()).toEqual({}) }); function pending() { return {} }", None), ("import { test } from './test-utils'; test('something');", None), ]; 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 c2ac21378de4..381e57e50a36 100644 --- a/crates/oxc_linter/src/rules/jest/no_done_callback.rs +++ b/crates/oxc_linter/src/rules/jest/no_done_callback.rs @@ -2,10 +2,7 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression, FormalParameters}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -18,18 +15,20 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -enum NoDoneCallbackDiagnostic { - #[error("eslint-plugin-jest(no-done-callback): Function parameter(s) use the `done` argument")] - #[diagnostic( - severity(warning), - help("Return a Promise instead of relying on callback parameter") - )] - NoDoneCallback(#[label] Span), +fn no_done_callback(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-done-callback): Function parameter(s) use the `done` argument", + ) + .with_help("Return a Promise instead of relying on callback parameter") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jest(no-done-callback): Function parameter(s) use the `done` argument")] - #[diagnostic(severity(warning), help("Use await instead of callback in async functions"))] - UseAwaitInsteadOfCallback(#[label] Span), +fn use_await_instead_of_callback(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-done-callback): Function parameter(s) use the `done` argument", + ) + .with_help("Use await instead of callback in async functions") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -127,11 +126,11 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) }; if func_expr.r#async { - ctx.diagnostic(NoDoneCallbackDiagnostic::UseAwaitInsteadOfCallback(span)); + ctx.diagnostic(use_await_instead_of_callback(span)); return; } - ctx.diagnostic(NoDoneCallbackDiagnostic::NoDoneCallback(span)); + ctx.diagnostic(no_done_callback(span)); } Argument::ArrowFunctionExpression(arrow_expr) => { if arrow_expr.params.parameters_count() != 1 + callback_arg_index { @@ -143,11 +142,11 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) }; if arrow_expr.r#async { - ctx.diagnostic(NoDoneCallbackDiagnostic::UseAwaitInsteadOfCallback(span)); + ctx.diagnostic(use_await_instead_of_callback(span)); return; } - ctx.diagnostic(NoDoneCallbackDiagnostic::NoDoneCallback(span)); + ctx.diagnostic(no_done_callback(span)); } _ => {} } diff --git a/crates/oxc_linter/src/rules/jest/no_export.rs b/crates/oxc_linter/src/rules/jest/no_export.rs index 565b7b70645c..991468b76e24 100644 --- a/crates/oxc_linter/src/rules/jest/no_export.rs +++ b/crates/oxc_linter/src/rules/jest/no_export.rs @@ -1,16 +1,15 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, utils::is_jest_file}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-export): Do not export from a test file.")] -#[diagnostic(severity(warning), help("If you want to share code between tests, move it into a separate file and import it from there."))] -struct NoExportDiagnostic(#[label] pub Span); +fn no_export_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-export): Do not export from a test file.") + .with_help("If you want to share code between tests, move it into a separate file and import it from there.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoExport; @@ -45,11 +44,11 @@ impl Rule for NoExport { } for span in ctx.semantic().module_record().exported_bindings.values() { - ctx.diagnostic(NoExportDiagnostic(*span)); + ctx.diagnostic(no_export_diagnostic(*span)); } if let Some(span) = ctx.semantic().module_record().export_default { - ctx.diagnostic(NoExportDiagnostic(span)); + ctx.diagnostic(no_export_diagnostic(span)); } } } diff --git a/crates/oxc_linter/src/rules/jest/no_focused_tests.rs b/crates/oxc_linter/src/rules/jest/no_focused_tests.rs index 344749b11585..41ee97a2a3b5 100644 --- a/crates/oxc_linter/src/rules/jest/no_focused_tests.rs +++ b/crates/oxc_linter/src/rules/jest/no_focused_tests.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,10 +14,11 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-focused-tests): Unexpected focused test.")] -#[diagnostic(severity(warning), help("Remove focus from test."))] -struct NoFocusedTestsDiagnostic(#[label] pub Span); +fn no_focused_tests_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-focused-tests): Unexpected focused test.") + .with_help("Remove focus from test.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoFocusedTests; @@ -66,7 +65,9 @@ impl Rule for NoFocusedTests { fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { let node = possible_jest_node.node; - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; let Some(jest_fn_call) = parse_general_jest_fn_call(call_expr, possible_jest_node, ctx) else { return; }; @@ -76,7 +77,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) } if name.starts_with('f') { - ctx.diagnostic_with_fix(NoFocusedTestsDiagnostic(call_expr.span), || { + ctx.diagnostic_with_fix(no_focused_tests_diagnostic(call_expr.span), || { let start = call_expr.span.start; Fix::delete(Span::new(start, start + 1)) }); @@ -86,7 +87,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) let only_node = members.iter().find(|member| member.is_name_equal("only")); if let Some(only_node) = only_node { - ctx.diagnostic_with_fix(NoFocusedTestsDiagnostic(call_expr.span), || { + ctx.diagnostic_with_fix(no_focused_tests_diagnostic(call_expr.span), || { let span = only_node.span; let start = span.start - 1; let end = if matches!(only_node.element, MemberExpressionElement::IdentName(_)) { diff --git a/crates/oxc_linter/src/rules/jest/no_hooks.rs b/crates/oxc_linter/src/rules/jest/no_hooks.rs index 0e3fa9705e9a..b974bec2257b 100644 --- a/crates/oxc_linter/src/rules/jest/no_hooks.rs +++ b/crates/oxc_linter/src/rules/jest/no_hooks.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -15,10 +13,10 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-hooks): Disallow setup and teardown hooks.")] -#[diagnostic(severity(warning))] -pub struct UnexpectedHookDiagonsitc(#[label] pub Span); +fn unexpected_hook_diagonsitc(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-hooks): Disallow setup and teardown hooks.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoHooks(Box); @@ -122,7 +120,7 @@ impl NoHooks { if let Expression::Identifier(ident) = &call_expr.callee { if !self.allow.contains(&ident.name.to_string()) { - ctx.diagnostic(UnexpectedHookDiagonsitc(call_expr.callee.span())); + ctx.diagnostic(unexpected_hook_diagonsitc(call_expr.callee.span())); } } } 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 db433bd4e9d3..96451c5d8b56 100644 --- a/crates/oxc_linter/src/rules/jest/no_identical_title.rs +++ b/crates/oxc_linter/src/rules/jest/no_identical_title.rs @@ -4,10 +4,7 @@ use oxc_ast::{ ast::{Argument, CallExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNodeId; use oxc_span::{Atom, Span}; @@ -22,15 +19,14 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum NoIdenticalTitleDiagnostic { - #[error("eslint-plugin-jest(no-identical-title): Describe block title is used multiple times in the same describe block.")] - #[diagnostic(severity(warning), help("Change the title of describe block."))] - DescribeRepeat(#[label] Span), +fn describe_repeat(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-identical-title): Describe block title is used multiple times in the same describe block.") + .with_help("Change the title of describe block.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jest(no-identical-title): Test title is used multiple times in the same describe block.")] - #[diagnostic(severity(warning), help("Change the title of test."))] - TestRepeat(#[label] Span), +fn test_repeat(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-identical-title): Test title is used multiple times in the same describe block.").with_help("Change the title of test.").with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -103,10 +99,10 @@ impl Rule for NoIdenticalTitle { if kind == prev_kind && parent_id == prev_parent { match kind { JestFnKind::General(JestGeneralFnKind::Describe) => { - ctx.diagnostic(NoIdenticalTitleDiagnostic::DescribeRepeat(span)); + ctx.diagnostic(describe_repeat(span)); } JestFnKind::General(JestGeneralFnKind::Test) => { - ctx.diagnostic(NoIdenticalTitleDiagnostic::TestRepeat(span)); + ctx.diagnostic(test_repeat(span)); } _ => {} } 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 017f9cdc8d44..0435cd165b03 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,8 +1,6 @@ use oxc_ast::{ast::Argument, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -12,10 +10,11 @@ use crate::{ utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-interpolation-in-snapshots): Do not use string interpolation inside of snapshots")] -#[diagnostic(severity(warning), help("Remove string interpolation from snapshots"))] -struct NoInterpolationInSnapshotsDiagnostic(#[label] pub Span); +fn no_interpolation_in_snapshots_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-interpolation-in-snapshots): Do not use string interpolation inside of snapshots") + .with_help("Remove string interpolation from snapshots") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoInterpolationInSnapshots; @@ -66,7 +65,9 @@ impl Rule for NoInterpolationInSnapshots { fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { let node = possible_jest_node.node; - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; let Some(jest_fn_call) = parse_expect_jest_fn_call(call_expr, possible_jest_node, ctx) else { return; }; @@ -85,7 +86,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) for arg in jest_fn_call.args { if let Argument::TemplateLiteral(template_lit) = arg { if !template_lit.expressions.is_empty() { - ctx.diagnostic(NoInterpolationInSnapshotsDiagnostic(template_lit.span)); + ctx.diagnostic(no_interpolation_in_snapshots_diagnostic(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 1add8186a056..cae309555a8f 100644 --- a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs +++ b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs @@ -4,19 +4,18 @@ use oxc_ast::{ }, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, Fix}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-jasmine-globals): {0:?}")] -#[diagnostic(severity(warning), help("{1:?}"))] -struct NoJasmineGlobalsDiagnostic(pub &'static str, pub &'static str, #[label] pub Span); +fn no_jasmine_globals_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(no-jasmine-globals): {x0:?}")) + .with_help(format!("{x1:?}")) + .with_labels([span2.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -58,7 +57,7 @@ impl Rule for NoJasmineGlobals { for &reference_id in reference_ids { let reference = symbol_table.get_reference(reference_id); if let Some((error, help)) = get_non_jasmine_property_messages(name) { - ctx.diagnostic(NoJasmineGlobalsDiagnostic(error, help, reference.span())); + ctx.diagnostic(no_jasmine_globals_diagnostic(error, help, reference.span())); } } } @@ -79,13 +78,15 @@ fn diagnostic_assign_expr<'a>(expr: &'a AssignmentExpression<'a>, ctx: &LintCont .as_simple_assignment_target() .and_then(SimpleAssignmentTarget::as_member_expression) { - let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { return }; + let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { + return; + }; if property_name == "DEFAULT_TIMEOUT_INTERVAL" { // `jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000` we can fix it to `jest.setTimeout(5000)` if let Expression::NumericLiteral(number_literal) = &expr.right { ctx.diagnostic_with_fix( - NoJasmineGlobalsDiagnostic(COMMON_ERROR_TEXT, COMMON_HELP_TEXT, span), + no_jasmine_globals_diagnostic(COMMON_ERROR_TEXT, COMMON_HELP_TEXT, span), || { let content = format!("jest.setTimeout({})", number_literal.value); Fix::new(content, expr.span) @@ -95,17 +96,19 @@ fn diagnostic_assign_expr<'a>(expr: &'a AssignmentExpression<'a>, ctx: &LintCont } } - ctx.diagnostic(NoJasmineGlobalsDiagnostic(COMMON_ERROR_TEXT, COMMON_HELP_TEXT, span)); + ctx.diagnostic(no_jasmine_globals_diagnostic(COMMON_ERROR_TEXT, COMMON_HELP_TEXT, span)); } } fn diagnostic_call_expr<'a>(expr: &'a CallExpression<'a>, ctx: &LintContext) { if let Some(member_expr) = expr.callee.as_member_expression() { - let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { return }; + let Some((span, property_name)) = get_jasmine_property_name(member_expr) else { + return; + }; JasmineProperty::from_str(property_name).map_or_else( || { - ctx.diagnostic(NoJasmineGlobalsDiagnostic( + ctx.diagnostic(no_jasmine_globals_diagnostic( COMMON_ERROR_TEXT, COMMON_HELP_TEXT, span, @@ -114,11 +117,12 @@ fn diagnostic_call_expr<'a>(expr: &'a CallExpression<'a>, ctx: &LintContext) { |jasmine_property| { let (error, help) = jasmine_property.details(); if jasmine_property.available_in_jest_expect() { - ctx.diagnostic_with_fix(NoJasmineGlobalsDiagnostic(error, help, span), || { - Fix::new("expect", member_expr.object().span()) - }); + ctx.diagnostic_with_fix( + no_jasmine_globals_diagnostic(error, help, span), + || Fix::new("expect", member_expr.object().span()), + ); } else { - ctx.diagnostic(NoJasmineGlobalsDiagnostic(error, help, span)); + ctx.diagnostic(no_jasmine_globals_diagnostic(error, help, span)); } }, ); 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 11454c5e339e..fe9bea6e397d 100644 --- a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs +++ b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs @@ -1,22 +1,18 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::path::PathBuf; use oxc_ast::{ast::Argument, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-mocks-import): Mocks should not be manually imported from a `__mocks__` directory.")] -#[diagnostic( - severity(warning), - help("Instead use `jest.mock` and import from the original module path.") -)] -struct NoMocksImportDiagnostic(#[label] pub Span); +fn no_mocks_import_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-mocks-import): Mocks should not be manually imported from a `__mocks__` directory.") + .with_help("Instead use `jest.mock` and import from the original module path.") + .with_labels([span0.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -44,7 +40,7 @@ impl Rule for NoMocksImport { for import_entry in &module_records.import_entries { let module_specifier = import_entry.module_request.name().as_str(); if contains_mocks_dir(module_specifier) { - ctx.diagnostic(NoMocksImportDiagnostic(import_entry.module_request.span())); + ctx.diagnostic(no_mocks_import_diagnostic(import_entry.module_request.span())); } } @@ -67,7 +63,7 @@ impl Rule for NoMocksImport { }; if contains_mocks_dir(&string_literal.value) { - ctx.diagnostic(NoMocksImportDiagnostic(string_literal.span)); + ctx.diagnostic(no_mocks_import_diagnostic(string_literal.span)); } } } 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 2d2a4e188532..598652167cb4 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 @@ -8,23 +8,26 @@ use crate::{ }; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use rustc_hash::{FxHashMap, FxHasher}; use std::{collections::HashMap, hash::BuildHasherDefault}; -#[derive(Debug, Error, Diagnostic)] -enum NoRestrictedJestMethodsDiagnostic { - #[error("eslint-plugin-jest(no-restricted-jest-methods): Disallow specific `jest.` methods")] - #[diagnostic(severity(warning), help("Use of `{0:?}` is disallowed"))] - RestrictedJestMethod(String, #[label] Span), - #[error("eslint-plugin-jest(no-restricted-jest-methods): Disallow specific `jest.` methods")] - #[diagnostic(severity(warning), help("{0:?}"))] - RestrictedJestMethodWithMessage(String, #[label] Span), +fn restricted_jest_method(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-restricted-jest-methods): Disallow specific `jest.` methods", + ) + .with_help(format!("Use of `{x0:?}` is disallowed")) + .with_labels([span1.into()]) +} + +fn restricted_jest_method_with_message(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-restricted-jest-methods): Disallow specific `jest.` methods", + ) + .with_help(format!("{x0:?}")) + .with_labels([span1.into()]) } #[derive(Debug, Default, Clone)] @@ -126,23 +129,13 @@ impl NoRestrictedJestMethods { if self.contains(property_name) { self.get_message(property_name).map_or_else( || { - ctx.diagnostic(NoRestrictedJestMethodsDiagnostic::RestrictedJestMethod( - property_name.to_string(), - span, - )); + ctx.diagnostic(restricted_jest_method(property_name, span)); }, |message| { if message.trim() == "" { - ctx.diagnostic(NoRestrictedJestMethodsDiagnostic::RestrictedJestMethod( - property_name.to_string(), - span, - )); + ctx.diagnostic(restricted_jest_method(property_name, span)); } else { - ctx.diagnostic( - NoRestrictedJestMethodsDiagnostic::RestrictedJestMethodWithMessage( - message, span, - ), - ); + ctx.diagnostic(restricted_jest_method_with_message(&message, span)); } }, ); diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs index 4efaedc1e3df..987c2307e3d8 100644 --- a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs +++ b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs @@ -8,24 +8,27 @@ use crate::{ }; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::phf_set; use rustc_hash::{FxHashMap, FxHasher}; use std::{collections::HashMap, hash::BuildHasherDefault, path::Path}; -#[derive(Debug, Error, Diagnostic)] -enum NoRestrictedMatchersDiagnostic { - #[error("eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers")] - #[diagnostic(severity(warning), help("Use of `{0:?}` is disallowed`"))] - RestrictedChain(String, #[label] Span), - #[error("eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers")] - #[diagnostic(severity(warning), help("{0:?}"))] - RestrictedChainWithMessage(String, #[label] Span), +fn restricted_chain(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers", + ) + .with_help(format!("Use of `{x0:?}` is disallowed`")) + .with_labels([span1.into()]) +} + +fn restricted_chain_with_message(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers", + ) + .with_help(format!("{x0:?}")) + .with_labels([span1.into()]) } #[derive(Debug, Default, Clone)] @@ -128,15 +131,9 @@ impl NoRestrictedMatchers { for (restriction, message) in &self.restricted_matchers { if Self::check_restriction(chain_call.as_str(), restriction.as_str()) { if message.is_empty() { - ctx.diagnostic(NoRestrictedMatchersDiagnostic::RestrictedChain( - chain_call.clone(), - span, - )); + ctx.diagnostic(restricted_chain(&chain_call, span)); } else { - ctx.diagnostic(NoRestrictedMatchersDiagnostic::RestrictedChainWithMessage( - message.to_string(), - span, - )); + ctx.diagnostic(restricted_chain_with_message(message, span)); } } } diff --git a/crates/oxc_linter/src/rules/jest/no_standalone_expect.rs b/crates/oxc_linter/src/rules/jest/no_standalone_expect.rs index 020427e0ab7c..adf9c2dfb584 100644 --- a/crates/oxc_linter/src/rules/jest/no_standalone_expect.rs +++ b/crates/oxc_linter/src/rules/jest/no_standalone_expect.rs @@ -1,10 +1,8 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::collections::HashMap; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNodeId; use oxc_span::Span; @@ -20,10 +18,13 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-standalone-expect): Expect must be inside of a test block.")] -#[diagnostic(severity(warning), help("Did you forget to wrap `expect` in a `test` or `it` block?"))] -struct NoStandaloneExpectDiagnostic(#[label] pub Span); +fn no_standalone_expect_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-standalone-expect): Expect must be inside of a test block.", + ) + .with_help("Did you forget to wrap `expect` in a `test` or `it` block?") + .with_labels([span0.into()]) +} /// #[derive(Debug, Default, Clone)] @@ -122,7 +123,7 @@ impl NoStandaloneExpect { ) .is_none() { - ctx.diagnostic(NoStandaloneExpectDiagnostic(head.span)); + ctx.diagnostic(no_standalone_expect_diagnostic(head.span)); } } } @@ -261,17 +262,20 @@ fn test() { expect(value).toBe(true); }); ", - None + None, ), ("it.only('an only', value => { expect(value).toBe(true); });", None), ("it.concurrent('an concurrent', value => { expect(value).toBe(true); });", None), ("describe.each([1, true])('trues', value => { it('an it', () => expect(value).toBe(true) ); });", None), - (" + ( + " describe('scenario', () => { const t = Math.random() ? it.only : it; t('testing', () => expect(true)); }); - ", Some(serde_json::json!([{ "additionalTestBlockFunctions": ['t'] }]))), + ", + Some(serde_json::json!([{ "additionalTestBlockFunctions": ['t'] }])), + ), ( r" each([ @@ -281,8 +285,10 @@ fn test() { ]).test('returns the result of adding %d to %d', (a, b, expected) => { expect(a + b).toBe(expected); }); - ", Some(serde_json::json!([{ "additionalTestBlockFunctions": ["each.test"] }]))) - ]; + ", + Some(serde_json::json!([{ "additionalTestBlockFunctions": ["each.test"] }])), + ), + ]; let fail = vec![ ("(() => {})('testing', () => expect(true).toBe(false))", None), @@ -295,7 +301,7 @@ fn test() { t('testing', () => expect(true).toBe(false)); }); ", - None + None, ), ( " @@ -304,7 +310,7 @@ fn test() { t('testing', () => expect(true).toBe(false)); }); ", - None + None, ), ( " @@ -315,7 +321,9 @@ fn test() { ]).test('returns the result of adding %d to %d', (a, b, expected) => { expect(a + b).toBe(expected); }); - ", None), + ", + None, + ), ( " each([ @@ -326,7 +334,7 @@ fn test() { expect(a + b).toBe(expected); }); ", - Some(serde_json::json!([{ "additionalTestBlockFunctions": ["each"] }])) + Some(serde_json::json!([{ "additionalTestBlockFunctions": ["each"] }])), ), ( " @@ -338,7 +346,7 @@ fn test() { expect(a + b).toBe(expected); }); ", - Some(serde_json::json!([{ "additionalTestBlockFunctions": ["test"] }])) + Some(serde_json::json!([{ "additionalTestBlockFunctions": ["test"] }])), ), ("describe('a test', () => { expect(1).toBe(1); });", None), ("describe('a test', () => expect(1).toBe(1));", None), @@ -353,15 +361,15 @@ fn test() { import { expect as pleaseExpect } from '@jest/globals'; describe('a test', () => { pleaseExpect(1).toBe(1); }); ", - None + None, ), ( " import { expect as pleaseExpect } from '@jest/globals'; beforeEach(() => pleaseExpect.hasAssertions()); ", - None - ) + None, + ), ]; Tester::new(NoStandaloneExpect::NAME, pass, fail).with_jest_plugin(true).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/no_test_prefixes.rs b/crates/oxc_linter/src/rules/jest/no_test_prefixes.rs index 3b95fdfef296..a4e2e4a5aa42 100644 --- a/crates/oxc_linter/src/rules/jest/no_test_prefixes.rs +++ b/crates/oxc_linter/src/rules/jest/no_test_prefixes.rs @@ -1,10 +1,8 @@ use oxc_ast::{ast::Expression, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{GetSpan, Span}; use crate::{ context::LintContext, @@ -16,10 +14,10 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-test-prefixes): Use {0:?} instead.")] -#[diagnostic(severity(warning))] -struct NoTestPrefixesDiagnostic(CompactStr, #[label] pub Span); +fn no_test_prefixes_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(no-test-prefixes): Use {x0:?} instead.")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoTestPrefixes; @@ -60,12 +58,16 @@ impl Rule for NoTestPrefixes { fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { let node = possible_jest_node.node; - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; let Some(jest_fn_call) = parse_general_jest_fn_call(call_expr, possible_jest_node, ctx) else { return; }; let ParsedGeneralJestFnCall { kind, name, .. } = &jest_fn_call; - let Some(kind) = kind.to_general() else { return }; + let Some(kind) = kind.to_general() else { + return; + }; if !matches!(kind, JestGeneralFnKind::Describe | JestGeneralFnKind::Test) { return; @@ -84,14 +86,13 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) }; let preferred_node_name = get_preferred_node_names(&jest_fn_call); - let preferred_node_name_cloned = preferred_node_name.clone(); - ctx.diagnostic_with_fix(NoTestPrefixesDiagnostic(preferred_node_name, span), || { - Fix::new(preferred_node_name_cloned.to_string(), span) + ctx.diagnostic_with_fix(no_test_prefixes_diagnostic(&preferred_node_name, span), || { + Fix::new(preferred_node_name, span) }); } -fn get_preferred_node_names(jest_fn_call: &ParsedGeneralJestFnCall) -> CompactStr { +fn get_preferred_node_names(jest_fn_call: &ParsedGeneralJestFnCall) -> String { let ParsedGeneralJestFnCall { members, name, .. } = jest_fn_call; let preferred_modifier = if name.starts_with('f') { "only" } else { "skip" }; @@ -103,9 +104,9 @@ fn get_preferred_node_names(jest_fn_call: &ParsedGeneralJestFnCall) -> CompactSt let name_slice = &name[1..]; if member_names.is_empty() { - CompactStr::from(format!("{name_slice}.{preferred_modifier}")) + format!("{name_slice}.{preferred_modifier}") } else { - CompactStr::from(format!("{name_slice}.{preferred_modifier}.{member_names}")) + format!("{name_slice}.{preferred_modifier}.{member_names}") } } 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 3223bae3136e..d59849a27ad6 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 @@ -3,24 +3,23 @@ use crate::{ rule::Rule, utils::{is_type_of_jest_fn_call, JestFnKind, JestGeneralFnKind, PossibleJestNode}, }; +use oxc_diagnostics::OxcDiagnostic; use oxc_allocator::Box as OBox; use oxc_ast::{ ast::{CallExpression, Expression, FunctionBody, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNode; use oxc_span::{GetSpan, Span}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-test-return-statement): Jest tests should not return a value")] -#[diagnostic(severity(warning))] -pub struct NoTestReturnStatementDiagnostic(#[label] pub Span); +fn no_test_return_statement_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(no-test-return-statement): Jest tests should not return a value", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoTestReturnStatement; @@ -120,7 +119,7 @@ fn check_test_return_statement<'a>(func_body: &OBox<'_, FunctionBody<'a>>, ctx: return; } - ctx.diagnostic(NoTestReturnStatementDiagnostic(Span::new( + ctx.diagnostic(no_test_return_statement_diagnostic(Span::new( return_stmt.span().start, call_expr.span.start - 1, ))); 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 3943dcbe89f8..1faa53989a84 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 @@ -4,25 +4,20 @@ use crate::{ rule::Rule, utils::{collect_possible_jest_call_node, PossibleJestNode}, }; +use oxc_diagnostics::OxcDiagnostic; use oxc_ast::{ ast::{Argument, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(no-untyped-mock-factory): Disallow using `jest.mock()` factories without an explicit type parameter.")] -#[diagnostic( - severity(warning), - help("Add a type parameter to the mock factory such as `typeof import({0:?})`") -)] -struct AddTypeParameterToModuleMockDiagnostic(CompactStr, #[label] pub Span); +fn add_type_parameter_to_module_mock_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(no-untyped-mock-factory): Disallow using `jest.mock()` factories without an explicit type parameter.") + .with_help(format!("Add a type parameter to the mock factory such as `typeof import({x0:?})`")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoUntypedMockFactory; @@ -141,8 +136,8 @@ impl NoUntypedMockFactory { if let Expression::StringLiteral(string_literal) = expr { ctx.diagnostic_with_fix( - AddTypeParameterToModuleMockDiagnostic( - string_literal.value.to_compact_str(), + add_type_parameter_to_module_mock_diagnostic( + string_literal.value.as_str(), property_span, ), || { @@ -158,8 +153,8 @@ impl NoUntypedMockFactory { }, ); } else if let Expression::Identifier(ident) = expr { - ctx.diagnostic(AddTypeParameterToModuleMockDiagnostic( - ident.name.to_compact_str(), + ctx.diagnostic(add_type_parameter_to_module_mock_diagnostic( + ident.name.as_str(), property_span, )); } diff --git a/crates/oxc_linter/src/rules/jest/prefer_called_with.rs b/crates/oxc_linter/src/rules/jest/prefer_called_with.rs index 7b290f5faba5..bc8edef038fc 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_called_with.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_called_with.rs @@ -5,21 +5,20 @@ use crate::{ }; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -enum PreferCalledWithDiagnostic { - #[error("eslint-plugin-jest(prefer-called-with): Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()`.")] - #[diagnostic(severity(warning), help("Prefer toBeCalledWith(/* expected args */)"))] - UseToBeCalledWith(#[label] Span), - #[error("eslint-plugin-jest(prefer-called-with): Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()`.")] - #[diagnostic(severity(warning), help("Prefer toHaveBeenCalledWith(/* expected args */)"))] - UseHaveBeenCalledWith(#[label] Span), +fn use_to_be_called_with(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-called-with): Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()`.") + .with_help("Prefer toBeCalledWith(/* expected args */)") + .with_labels([span0.into()]) +} + +fn use_have_been_called_with(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-called-with): Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()`.") + .with_help("Prefer toHaveBeenCalledWith(/* expected args */)") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -78,13 +77,9 @@ impl PreferCalledWith { if let Some(matcher_property) = jest_fn_call.matcher() { if let Some(matcher_name) = matcher_property.name() { if matcher_name == "toBeCalled" { - ctx.diagnostic(PreferCalledWithDiagnostic::UseToBeCalledWith( - matcher_property.span, - )); + ctx.diagnostic(use_to_be_called_with(matcher_property.span)); } else if matcher_name == "toHaveBeenCalled" { - ctx.diagnostic(PreferCalledWithDiagnostic::UseHaveBeenCalledWith( - matcher_property.span, - )); + ctx.diagnostic(use_have_been_called_with(matcher_property.span)); } } } 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 73b66155d196..d8d534d27050 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs @@ -11,20 +11,17 @@ use oxc_ast::{ ast::{Argument, BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::BinaryOperator; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers" -)] -#[diagnostic(severity(warning), help("Prefer using `{0:?}` instead"))] -struct UseToBeComparison(&'static str, #[label] pub Span); +fn use_to_be_comparison(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers") + .with_help(format!("Prefer using `{x0:?}` instead")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferComparisonMatcher; @@ -116,7 +113,7 @@ impl PreferComparisonMatcher { return; }; - ctx.diagnostic_with_fix(UseToBeComparison(prefer_matcher_name, matcher.span), || { + ctx.diagnostic_with_fix(use_to_be_comparison(prefer_matcher_name, matcher.span), || { // This is to handle the case can be transform into the following case: // expect(value > 1,).toEqual(true,) => expect(value,).toBeGreaterThan(1,) // ^ ^ 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 972bf8e4abc2..c4b038e0c660 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs @@ -3,25 +3,21 @@ use crate::{ rule::Rule, utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, }; +use oxc_diagnostics::OxcDiagnostic; use oxc_ast::{ ast::{Argument, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use oxc_syntax::operator::BinaryOperator; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers." -)] -#[diagnostic(severity(warning), help("Prefer using one of the equality matchers instead"))] -struct UseEqualityMatcherDiagnostic(#[label] Span); +fn use_equality_matcher_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers.") + .with_help("Prefer using one of the equality matchers instead") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferEqualityMatcher; @@ -92,7 +88,7 @@ impl PreferEqualityMatcher { return; }; - ctx.diagnostic(UseEqualityMatcherDiagnostic(matcher.span)); + ctx.diagnostic(use_equality_matcher_diagnostic(matcher.span)); } } 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 363373f9882a..49d50c52847c 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -19,10 +17,11 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-expect-resolves): Prefer `await expect(...).resolves` over `expect(await ...)` syntax.")] -#[diagnostic(severity(warning), help("Use `await expect(...).resolves` instead"))] -struct ExpectResolves(#[label] pub Span); +fn expect_resolves(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-expect-resolves): Prefer `await expect(...).resolves` over `expect(await ...)` syntax.") + .with_help("Use `await expect(...).resolves` instead") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferExpectResolves; @@ -108,7 +107,7 @@ impl PreferExpectResolves { return; }; - ctx.diagnostic_with_fix(ExpectResolves(await_expr.span), || { + ctx.diagnostic_with_fix(expect_resolves(await_expr.span), || { let content = Self::build_code(&jest_expect_fn_call, call_expr, ident.span, ctx); Fix::new(content, call_expr.span) }); 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 a06b0e6f5b09..c5f97c0a12cf 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs @@ -1,10 +1,8 @@ use oxc_ast::{ast::Argument, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{ context::LintContext, @@ -16,10 +14,13 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names")] -#[diagnostic(severity(warning), help("`{0:?}`s should begin with lowercase"))] -struct UnexpectedLowercase(CompactStr, #[label] Span); +fn unexpected_lowercase(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names", + ) + .with_help(format!("`{x0:?}`s should begin with lowercase")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferLowercaseTitleConfig { @@ -216,7 +217,7 @@ impl PreferLowercaseTitle { } ctx.diagnostic_with_fix( - UnexpectedLowercase(string_expr.value.to_compact_str(), string_expr.span), + unexpected_lowercase(string_expr.value.as_str(), string_expr.span), || { let mut content = ctx.codegen(); content.print_str(first_char.to_ascii_lowercase().to_string().as_bytes()); @@ -246,7 +247,7 @@ impl PreferLowercaseTitle { } ctx.diagnostic_with_fix( - UnexpectedLowercase(template_string.to_compact_str(), template_expr.span), + unexpected_lowercase(template_string.as_str(), template_expr.span), || { let mut content = ctx.codegen(); content.print_str(first_char.to_ascii_lowercase().to_string().as_bytes()); 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 aae173986957..e4220e134bc0 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 @@ -2,19 +2,16 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{Atom, CompactStr, Span}; +use oxc_span::{Atom, Span}; use crate::{context::LintContext, fixer::Fix, rule::Rule, utils::get_node_name}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises")] -#[diagnostic(severity(warning), help("Prefer {0:?}"))] -struct UseMockShorthand(CompactStr, #[label] Span); +fn use_mock_shorthand(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises").with_help(format!("Prefer {x0:?}")).with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferMockPromiseShorthand; @@ -152,17 +149,14 @@ impl PreferMockPromiseShorthand { // if arguments is more than one, just report it instead of fixing it. if call_expr.arguments.len() <= 1 { ctx.diagnostic_with_fix( - UseMockShorthand(Atom::from(prefer_name).to_compact_str(), property_span), + use_mock_shorthand(Atom::from(prefer_name).as_str(), property_span), || { let content = Self::fix(prefer_name, call_expr, ctx); Fix::new(content, Span::new(property_span.start, fix_span.end)) }, ); } else { - ctx.diagnostic(UseMockShorthand( - Atom::from(prefer_name).to_compact_str(), - property_span, - )); + ctx.diagnostic(use_mock_shorthand(Atom::from(prefer_name).as_str(), property_span)); } } 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 d5ca2709dbae..4d115d1fafc1 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_spy_on.rs @@ -5,10 +5,8 @@ use oxc_ast::{ }, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNode; use oxc_span::Span; @@ -20,10 +18,11 @@ use crate::{ utils::{get_node_name, parse_general_jest_fn_call, PossibleJestNode}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-spy-on): Suggest using `jest.spyOn()`.")] -#[diagnostic(severity(warning), help("Use jest.spyOn() instead"))] -struct UseJestSpyOn(#[label] pub Span); +fn use_jest_spy_on(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-spy-on): Suggest using `jest.spyOn()`.") + .with_help("Use jest.spyOn() instead") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferSpyOn; @@ -111,7 +110,7 @@ impl PreferSpyOn { } ctx.diagnostic_with_fix( - UseJestSpyOn(Span::new(call_expr.span.start, first_fn_member.span.end)), + use_jest_spy_on(Span::new(call_expr.span.start, first_fn_member.span.end)), || { let (end, has_mock_implementation) = if jest_fn_call.members.len() > 1 { let second = &jest_fn_call.members[1]; @@ -234,10 +233,7 @@ fn tests() { ("obj['prop' + 1] = jest['fn']()", None), ("obj.one.two = jest.fn(); const test = 10;", None), ("obj.a = jest.fn(() => 10,)", None), - ( - "obj.a.b = jest.fn(() => ({})).mockReturnValue('default').mockReturnValueOnce('first call'); test();", - None, - ), + ("obj.a.b = jest.fn(() => ({})).mockReturnValue('default').mockReturnValueOnce('first call'); test();", None), ("window.fetch = jest.fn(() => ({})).one.two().three().four", None), ("foo[bar] = jest.fn().mockReturnValue(undefined)", None), ( @@ -250,43 +246,19 @@ fn tests() { ]; let fix = vec![ - ( - "obj.a = jest.fn(); const test = 10;", - "jest.spyOn(obj, 'a').mockImplementation(); const test = 10;", - None, - ), + ("obj.a = jest.fn(); const test = 10;", "jest.spyOn(obj, 'a').mockImplementation(); const test = 10;", None), ("Date['now'] = jest['fn']()", "jest.spyOn(Date, 'now').mockImplementation()", None), - ( - "window[`${name}`] = jest[`fn`]()", - "jest.spyOn(window, `${name}`).mockImplementation()", - None, - ), - ( - "obj['prop' + 1] = jest['fn']()", - "jest.spyOn(obj, 'prop' + 1).mockImplementation()", - None, - ), - ( - "obj.one.two = jest.fn(); const test = 10;", - "jest.spyOn(obj.one, 'two').mockImplementation(); const test = 10;", - None, - ), + ("window[`${name}`] = jest[`fn`]()", "jest.spyOn(window, `${name}`).mockImplementation()", None), + ("obj['prop' + 1] = jest['fn']()", "jest.spyOn(obj, 'prop' + 1).mockImplementation()", None), + ("obj.one.two = jest.fn(); const test = 10;", "jest.spyOn(obj.one, 'two').mockImplementation(); const test = 10;", None), ("obj.a = jest.fn(() => 10,)", "jest.spyOn(obj, 'a').mockImplementation(() => 10)", None), ( "obj.a.b = jest.fn(() => ({})).mockReturnValue('default').mockReturnValueOnce('first call'); test();", "jest.spyOn(obj.a, 'b').mockImplementation(() => ({})).mockReturnValue('default').mockReturnValueOnce('first call'); test();", None, ), - ( - "window.fetch = jest.fn(() => ({})).one.two().three().four", - "jest.spyOn(window, 'fetch').mockImplementation(() => ({})).one.two().three().four", - None, - ), - ( - "foo[bar] = jest.fn().mockReturnValue(undefined)", - "jest.spyOn(foo, bar).mockImplementation().mockReturnValue(undefined)", - None, - ), + ("window.fetch = jest.fn(() => ({})).one.two().three().four", "jest.spyOn(window, 'fetch').mockImplementation(() => ({})).one.two().three().four", None), + ("foo[bar] = jest.fn().mockReturnValue(undefined)", "jest.spyOn(foo, bar).mockImplementation().mockReturnValue(undefined)", None), ( " foo.bar = jest.fn().mockImplementation(baz => baz) diff --git a/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs index 1f0bc8db2441..2e02c0c23726 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs @@ -4,19 +4,19 @@ use crate::{ rule::Rule, utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, }; +use oxc_diagnostics::OxcDiagnostic; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`.")] -#[diagnostic(severity(warning), help("Use `toStrictEqual()` instead"))] -struct UseToStrictEqual(#[label] Span); +fn use_to_strict_equal(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`.", + ) + .with_help("Use `toStrictEqual()` instead") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferStrictEqual; @@ -67,7 +67,7 @@ impl PreferStrictEqual { }; if matcher_name.eq("toEqual") { - ctx.diagnostic_with_fix(UseToStrictEqual(matcher.span), || { + ctx.diagnostic_with_fix(use_to_strict_equal(matcher.span), || { let mut formatter = ctx.codegen(); formatter.print_str( matcher 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 61139dbd46f4..89abc142f189 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_be.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_be.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -19,23 +17,31 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -enum PreferToBeDiagnostic { - #[error("eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.")] - #[diagnostic(severity(warning))] - UseToBe(#[label] Span), - #[error("eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.")] - #[diagnostic(severity(warning))] - UseToBeUndefined(#[label] Span), - #[error("eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.")] - #[diagnostic(severity(warning))] - UseToBeDefined(#[label] Span), - #[error("eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.")] - #[diagnostic(severity(warning))] - UseToBeNull(#[label] Span), - #[error("eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.")] - #[diagnostic(severity(warning))] - UseToBeNaN(#[label] Span), +fn use_to_be(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.", + ) + .with_labels([span0.into()]) +} + +fn use_to_be_undefined(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.") + .with_labels([span0.into()]) +} + +fn use_to_be_defined(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.") + .with_labels([span0.into()]) +} + +fn use_to_be_null(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.") + .with_labels([span0.into()]) +} + +fn use_to_be_na_n(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -224,7 +230,7 @@ impl PreferToBe { let maybe_not_modifier = modifiers.iter().find(|modifier| modifier.is_name_equal("not")); if kind == &PreferToBeKind::Undefined { - ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeUndefined(span), || { + ctx.diagnostic_with_fix(use_to_be_undefined(span), || { let new_matcher = if is_cmp_mem_expr { "[\"toBeUndefined\"]()" } else { "toBeUndefined()" }; if let Some(not_modifier) = maybe_not_modifier { @@ -234,7 +240,7 @@ impl PreferToBe { } }); } else if kind == &PreferToBeKind::Defined { - ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeDefined(span), || { + ctx.diagnostic_with_fix(use_to_be_defined(span), || { let (new_matcher, start) = if is_cmp_mem_expr { ("[\"toBeDefined\"]()", modifiers.first().unwrap().span.end) } else { @@ -244,17 +250,17 @@ impl PreferToBe { Fix::new(new_matcher.to_string(), Span::new(start, end)) }); } else if kind == &PreferToBeKind::Null { - ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeNull(span), || { + ctx.diagnostic_with_fix(use_to_be_null(span), || { let new_matcher = if is_cmp_mem_expr { "\"toBeNull\"]()" } else { "toBeNull()" }; Fix::new(new_matcher.to_string(), Span::new(span.start, end)) }); } else if kind == &PreferToBeKind::NaN { - ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeNaN(span), || { + ctx.diagnostic_with_fix(use_to_be_na_n(span), || { let new_matcher = if is_cmp_mem_expr { "\"toBeNaN\"]()" } else { "toBeNaN()" }; Fix::new(new_matcher.to_string(), Span::new(span.start, end)) }); } else { - ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBe(span), || { + ctx.diagnostic_with_fix(use_to_be(span), || { let new_matcher = if is_cmp_mem_expr { "\"toBe\"" } else { "toBe" }; Fix::new(new_matcher.to_string(), span) }); 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 e35e799dcba5..a9ce8690bcd2 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_contain.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -18,10 +16,10 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-to-contain): Suggest using `toContain()`.")] -#[diagnostic(severity(warning))] -struct UseToContain(#[label] pub Span); +fn use_to_contain(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-to-contain): Suggest using `toContain()`.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferToContain; @@ -122,7 +120,7 @@ impl PreferToContain { return; } - ctx.diagnostic(UseToContain(matcher.span)); + ctx.diagnostic(use_to_contain(matcher.span)); } fn is_fixable_includes_call_expression(call_expr: &CallExpression) -> bool { @@ -171,10 +169,7 @@ fn tests() { ("expect(a.includes(b)).toBe(...true)", None), ("expect(a);", None), // typescript - ( - "(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()", - None, - ), + ("(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()", None), ("expect(a.includes(b)).toEqual(0 as boolean);", None), ]; 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 e5b3c5cbfa88..d83c2e33902c 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 @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{match_member_expression, CallExpression, Expression, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -19,10 +17,12 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-to-have-length): Suggest using `toHaveLength()`.")] -#[diagnostic(severity(warning))] -struct UseToHaveLength(#[label] pub Span); +fn use_to_have_length(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(prefer-to-have-length): Suggest using `toHaveLength()`.", + ) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferToHaveLength; @@ -146,7 +146,7 @@ impl PreferToHaveLength { return; } - ctx.diagnostic_with_fix(UseToHaveLength(matcher.span), || { + ctx.diagnostic_with_fix(use_to_have_length(matcher.span), || { let code = Self::build_code(static_mem_expr, kind, property_name, ctx); let end = if call_expr.arguments.len() > 0 { call_expr.arguments.first().unwrap().span().start diff --git a/crates/oxc_linter/src/rules/jest/prefer_todo.rs b/crates/oxc_linter/src/rules/jest/prefer_todo.rs index 9dda938ce62a..74b46fd0d5c8 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_todo.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_todo.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Argument, CallExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -19,15 +17,15 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-todo): Suggest using `test.todo`.")] -#[diagnostic(severity(warning))] -struct EmptyTest(#[label] pub Span); +fn empty_test(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-todo): Suggest using `test.todo`.") + .with_labels([span0.into()]) +} -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(prefer-todo): Suggest using `test.todo`.")] -#[diagnostic(severity(warning))] -struct UnImplementedTestDiagnostic(#[label] pub Span); +fn un_implemented_test_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jest(prefer-todo): Suggest using `test.todo`.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct PreferTodo; @@ -82,11 +80,13 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) if counts == 1 && !filter_todo_case(call_expr) { let (content, span) = get_fix_content(call_expr); - ctx.diagnostic_with_fix(UnImplementedTestDiagnostic(span), || Fix::new(content, span)); + ctx.diagnostic_with_fix(un_implemented_test_diagnostic(span), || { + Fix::new(content, span) + }); } if counts > 1 && is_empty_function(call_expr) { - ctx.diagnostic_with_fix(EmptyTest(call_expr.span), || { + ctx.diagnostic_with_fix(empty_test(call_expr.span), || { let (content, span) = build_code(call_expr, ctx); Fix::new(content, span) }); diff --git a/crates/oxc_linter/src/rules/jest/require_hook.rs b/crates/oxc_linter/src/rules/jest/require_hook.rs index 54d01690012c..135b07f862f5 100644 --- a/crates/oxc_linter/src/rules/jest/require_hook.rs +++ b/crates/oxc_linter/src/rules/jest/require_hook.rs @@ -3,10 +3,8 @@ use oxc_ast::{ ast::{Argument, Expression, Statement, VariableDeclarationKind}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNode; use oxc_span::Span; @@ -20,10 +18,13 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.")] -#[diagnostic(severity(warning), help("This should be done within a hook"))] -struct UseHook(#[label] pub Span); +fn use_hook(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.", + ) + .with_help("This should be done within a hook") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequireHookConfig { @@ -220,7 +221,7 @@ impl RequireHook { !init_call.is_null_or_undefined() }) { - ctx.diagnostic(UseHook(var_decl.span)); + ctx.diagnostic(use_hook(var_decl.span)); } } } @@ -239,7 +240,7 @@ impl RequireHook { || name.starts_with("jest.") || self.allowed_function_calls.contains(&name)) { - ctx.diagnostic(UseHook(call_expr.span)); + ctx.diagnostic(use_hook(call_expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs b/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs index 96d183ecdcef..f8be3c1ddabe 100644 --- a/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs +++ b/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -12,10 +10,13 @@ use crate::{ utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(require-to-throw-message): Require a message for {0:?}.")] -#[diagnostic(severity(warning), help("Add an error message to {0:?}"))] -struct RequireToThrowMessageDiagnostic(pub String, #[label] pub Span); +fn require_to_throw_message_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!( + "eslint-plugin-jest(require-to-throw-message): Require a message for {x0:?}." + )) + .with_help(format!("Add an error message to {x0:?}")) + .with_labels([span1.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequireToThrowMessage; @@ -81,7 +82,7 @@ impl RequireToThrowMessage { && (matcher_name == "toThrow" || matcher_name == "toThrowError") && !has_not { - ctx.diagnostic(RequireToThrowMessageDiagnostic(matcher_name.to_string(), matcher.span)); + ctx.diagnostic(require_to_throw_message_diagnostic(&matcher_name, matcher.span)); } } } 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 b5a43e68cb27..044e366589b0 100644 --- a/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs +++ b/crates/oxc_linter/src/rules/jest/valid_describe_callback.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{Argument, Expression, FunctionBody, Statement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -18,10 +16,11 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(valid-describe-callback): {0:?}")] -#[diagnostic(severity(warning), help("{1:?}"))] -struct ValidDescribeCallbackDiagnostic(&'static str, &'static str, #[label] pub Span); +fn valid_describe_callback_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(valid-describe-callback): {x0:?}")) + .with_help(format!("{x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct ValidDescribeCallback; @@ -74,7 +73,9 @@ impl Rule for ValidDescribeCallback { fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { let node = possible_jest_node.node; - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; let Some(jest_fn_call) = parse_general_jest_fn_call(call_expr, possible_jest_node, ctx) else { return; }; @@ -151,7 +152,7 @@ fn find_first_return_stmt_span(function_body: &FunctionBody) -> Option { fn diagnostic(ctx: &LintContext, span: Span, message: Message) { let (error, help) = message.details(); - ctx.diagnostic(ValidDescribeCallbackDiagnostic(error, help, span)); + ctx.diagnostic(valid_describe_callback_diagnostic(error, help, span)); } #[derive(Clone, Copy)] diff --git a/crates/oxc_linter/src/rules/jest/valid_expect.rs b/crates/oxc_linter/src/rules/jest/valid_expect.rs index 1ec09d8da2a8..7676f92f5af1 100644 --- a/crates/oxc_linter/src/rules/jest/valid_expect.rs +++ b/crates/oxc_linter/src/rules/jest/valid_expect.rs @@ -2,12 +2,10 @@ use oxc_ast::{ ast::{Expression, MemberExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{GetSpan, Span}; use crate::{ context::LintContext, @@ -18,10 +16,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(valid-expect): {0:?}")] -#[diagnostic(severity(warning), help("{1:?}"))] -struct ValidExpectDiagnostic(CompactStr, &'static str, #[label] Span); +fn valid_expect_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(valid-expect): {x0:?}")) + .with_help(format!("{x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct ValidExpect(Box); @@ -111,7 +110,9 @@ impl Rule for ValidExpect { impl ValidExpect { fn run<'a>(&self, possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { let node = possible_jest_node.node; - let AstKind::CallExpression(call_expr) = node.kind() else { return }; + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; let Some(jest_fn_call) = parse_expect_jest_fn_call(call_expr, possible_jest_node, ctx) else { return; @@ -123,42 +124,44 @@ impl ValidExpect { match jest_fn_call.expect_error { Some(ExpectError::MatcherNotFound) => { let (error, help) = Message::MatcherNotFound.details(); - ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span)); + ctx.diagnostic(valid_expect_diagnostic(error, help, reporting_span)); return; } Some(ExpectError::MatcherNotCalled) => { let (error, help) = Message::MatcherNotCalled.details(); - ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span)); + ctx.diagnostic(valid_expect_diagnostic(error, help, reporting_span)); return; } Some(ExpectError::ModifierUnknown) => { let (error, help) = Message::ModifierUnknown.details(); - ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span)); + ctx.diagnostic(valid_expect_diagnostic(error, help, reporting_span)); return; } None => {} } - let Some(Expression::CallExpression(call_expr)) = jest_fn_call.head.parent else { return }; + let Some(Expression::CallExpression(call_expr)) = jest_fn_call.head.parent else { + return; + }; if call_expr.arguments.len() < self.min_args { - let error = CompactStr::from(format!( + let error = format!( "Expect takes at most {} argument{} ", self.min_args, if self.min_args > 1 { "s" } else { "" } - )); + ); let help = "Remove the extra arguments."; - ctx.diagnostic(ValidExpectDiagnostic(error, help, call_expr.span)); + ctx.diagnostic(valid_expect_diagnostic(&error, help, call_expr.span)); return; } if call_expr.arguments.len() > self.max_args { - let error = CompactStr::from(format!( + let error = format!( "Expect requires at least {} argument{} ", self.max_args, if self.max_args > 1 { "s" } else { "" } - )); + ); let help = "Add the missing arguments."; - ctx.diagnostic(ValidExpectDiagnostic(error, help, call_expr.span)); + ctx.diagnostic(valid_expect_diagnostic(&error, help, call_expr.span)); return; } @@ -188,19 +191,25 @@ impl ValidExpect { let Some(final_node) = find_promise_call_expression_node(node, ctx, target_node) else { return; }; - let Some(parent) = ctx.nodes().parent_node(final_node.id()) else { return }; + let Some(parent) = ctx.nodes().parent_node(final_node.id()) else { + return; + }; if !is_acceptable_return_node(parent, !self.always_await, ctx) { let span; let (error, help) = if target_node.id() == final_node.id() { - let AstKind::CallExpression(call_expr) = target_node.kind() else { return }; + let AstKind::CallExpression(call_expr) = target_node.kind() else { + return; + }; span = call_expr.span; Message::AsyncMustBeAwaited.details() } else { - let AstKind::CallExpression(call_expr) = final_node.kind() else { return }; + let AstKind::CallExpression(call_expr) = final_node.kind() else { + return; + }; span = call_expr.span; Message::PromisesWithAsyncAssertionsMustBeAwaited.details() }; - ctx.diagnostic(ValidExpectDiagnostic(error, help, span)); + ctx.diagnostic(valid_expect_diagnostic(error, help, span)); } } } @@ -246,7 +255,9 @@ fn is_acceptable_return_node<'a, 'b>( | AstKind::Argument(_) | AstKind::ExpressionStatement(_) | AstKind::FunctionBody(_) => { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { return false }; + let Some(parent) = ctx.nodes().parent_node(node.id()) else { + return false; + }; node = parent; } AstKind::ArrowFunctionExpression(arrow_expr) => return arrow_expr.expression, @@ -338,10 +349,18 @@ fn get_parent_if_thenable<'a, 'b>( let grandparent = ctx.nodes().parent_node(node.id()).and_then(|node| ctx.nodes().parent_node(node.id())); - let Some(grandparent) = grandparent else { return node }; - let AstKind::CallExpression(call_expr) = grandparent.kind() 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 }; + let Some(grandparent) = grandparent else { + return node; + }; + let AstKind::CallExpression(call_expr) = grandparent.kind() 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) { return get_parent_if_thenable(grandparent, ctx); @@ -360,25 +379,24 @@ enum Message { } impl Message { - fn details(self) -> (CompactStr, &'static str) { + fn details(self) -> (&'static str, &'static str) { match self { Self::MatcherNotFound => ( - CompactStr::from("Expect must have a corresponding matcher call."), + "Expect must have a corresponding matcher call.", "Did you forget add a matcher(e.g. `toBe`, `toBeDefined`)", ), Self::MatcherNotCalled => ( - CompactStr::from("Matchers must be called to assert."), + "Matchers must be called to assert.", "You need call your matcher, e.g. `expect(true).toBe(true)`.", ), Self::ModifierUnknown => { - (CompactStr::from("Expect has an unknown modifier."), "Is it a spelling mistake?") + ("Expect has an unknown modifier.", "Is it a spelling mistake?") + } + Self::AsyncMustBeAwaited => { + ("Async assertions must be awaited.", "Add `await` to your assertion.") } - Self::AsyncMustBeAwaited => ( - CompactStr::from("Async assertions must be awaited."), - "Add `await` to your assertion.", - ), Self::PromisesWithAsyncAssertionsMustBeAwaited => ( - CompactStr::from("Promises which return async assertions must be awaited."), + "Promises which return async assertions must be awaited.", "Add `await` to your assertion.", ), } @@ -389,9 +407,7 @@ impl Message { fn test_1() { use crate::tester::Tester; - let pass = vec![ - ("test('valid-expect', async () => { await Promise.race([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); })", None) - ]; + let pass = vec![("test('valid-expect', async () => { await Promise.race([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); })", None)]; let fail = vec![]; Tester::new(ValidExpect::NAME, pass, fail).with_jest_plugin(true).test(); @@ -432,14 +448,26 @@ fn test() { ("test('valid-expect', async () => { await Promise.allSettled([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None), ("test('valid-expect', async () => { await Promise.any([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None), ("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')); });", None), - ("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", None), + ( + "test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", + None, + ), ("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().catch(() => console.log('valid-case')); });", None), - ("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", None), + ( + "test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", + None, + ), ("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => { expect(someMock).toHaveBeenCalledTimes(1); }); });", None), ("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')); });", None), - ("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", None), + ( + "test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", + None, + ), ("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().catch(() => console.log('valid-case')); });", None), - ("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", None), + ( + "test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", + None, + ), ("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => { expect(someMock).toHaveBeenCalledTimes(1); }); });", None), ( " @@ -449,7 +477,7 @@ fn test() { }); }); ", - None + None, ), ( " @@ -459,7 +487,7 @@ fn test() { }); }); ", - None + None, ), ( " @@ -467,7 +495,7 @@ fn test() { return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => expect(Promise.resolve(2)).resolves.toBe(1)); }); ", - None + None, ), ( " @@ -479,7 +507,7 @@ fn test() { } }); ", - None + None, ), ( " @@ -491,7 +519,7 @@ fn test() { } }); ", - None + None, ), ( " @@ -505,7 +533,7 @@ fn test() { } }); ", - None + None, ), ("expect(1).toBe(2);", Some(serde_json::json!([{ "maxArgs": 2 }]))), ("expect(1, '1 !== 2').toBe(2);", Some(serde_json::json!([{ "maxArgs": 2 }]))), @@ -513,7 +541,7 @@ fn test() { ("test('valid-expect', () => { expect(2).not.toBe(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toRejectWith"] }]))), ("test('valid-expect', () => { expect(Promise.reject(2)).toRejectWith(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))), ("test('valid-expect', async () => { await expect(Promise.resolve(2)).toResolve(); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))), - ("test('valid-expect', async () => { expect(Promise.resolve(2)).toResolve(); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))) + ("test('valid-expect', async () => { expect(Promise.resolve(2)).toResolve(); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))), ]; let fail = vec![ @@ -551,7 +579,7 @@ fn test() { } }); ", - None + None, ), ( " @@ -563,7 +591,7 @@ fn test() { } }); ", - None + None, ), ( " @@ -577,7 +605,7 @@ fn test() { } }); ", - None + None, ), ("test('valid-expect', () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });", None), ("test('valid-expect', () => { expect(Promise.resolve(2)).toResolve(); });", None), @@ -598,7 +626,7 @@ fn test() { expect(Promise.resolve(1)).rejects.toBeDefined(); }); ", - None + None, ), ( " @@ -607,7 +635,7 @@ fn test() { expect(Promise.resolve(1)).rejects.toBeDefined(); }); ", - None + None, ), ( " @@ -616,7 +644,7 @@ fn test() { return expect(Promise.resolve(1)).rejects.toBeDefined(); }); ", - Some(serde_json::json!([{ "alwaysAwait": true }])) + Some(serde_json::json!([{ "alwaysAwait": true }])), ), ( " @@ -625,7 +653,7 @@ fn test() { return expect(Promise.resolve(1)).rejects.toBeDefined(); }); ", - None + None, ), ( " @@ -634,7 +662,7 @@ fn test() { return expect(Promise.resolve(1)).rejects.toBeDefined(); }); ", - Some(serde_json::json!([{ "alwaysAwait": true }])) + Some(serde_json::json!([{ "alwaysAwait": true }])), ), ( " @@ -643,7 +671,7 @@ fn test() { return expect(Promise.resolve(1)).toReject(); }); ", - Some(serde_json::json!([{ "alwaysAwait": true }])) + Some(serde_json::json!([{ "alwaysAwait": true }])), ), ( " @@ -651,7 +679,7 @@ fn test() { Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined()); }); ", - None + None, ), ( " @@ -659,7 +687,7 @@ fn test() { Promise.reject(expect(Promise.resolve(2)).resolves.not.toBeDefined()); }); ", - None + None, ), ( " @@ -667,7 +695,7 @@ fn test() { Promise.x(expect(Promise.resolve(2)).resolves.not.toBeDefined()); }); ", - None + None, ), ( " @@ -675,7 +703,7 @@ fn test() { Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined()); }); ", - Some(serde_json::json!([{ "alwaysAwait": true }])) + Some(serde_json::json!([{ "alwaysAwait": true }])), ), ( " @@ -686,7 +714,7 @@ fn test() { ]); }); ", - None + None, ), ( " @@ -697,7 +725,7 @@ fn test() { ]); }); ", - None + None, ), ( " @@ -708,7 +736,7 @@ fn test() { ] }); ", - None + None, ), ( " @@ -719,7 +747,7 @@ fn test() { ] }); ", - None + None, ), ( " @@ -730,7 +758,7 @@ fn test() { ] }); ", - None + None, ), ("expect(Promise.resolve(2)).resolves.toBe;", None), ( @@ -741,7 +769,7 @@ fn test() { }); }); ", - None + None, ), ( " @@ -752,7 +780,7 @@ fn test() { }); }); ", - None + None, ), ( " @@ -760,8 +788,8 @@ fn test() { await expect(Promise.resolve(1)); }); ", - None - ) + None, + ), ]; Tester::new(ValidExpect::NAME, pass, fail).with_jest_plugin(true).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/valid_title.rs b/crates/oxc_linter/src/rules/jest/valid_title.rs index bcfce5f9cb7d..0b92851f5f81 100644 --- a/crates/oxc_linter/src/rules/jest/valid_title.rs +++ b/crates/oxc_linter/src/rules/jest/valid_title.rs @@ -1,15 +1,13 @@ +use oxc_diagnostics::OxcDiagnostic; + use std::{collections::HashMap, hash::Hash}; use oxc_ast::{ ast::{Argument, BinaryExpression, Expression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{GetSpan, Span}; use regex::Regex; use crate::{ @@ -21,10 +19,11 @@ use crate::{ }, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jest(valid-title): {0:?}")] -#[diagnostic(severity(warning), help("{1:?}"))] -struct ValidTitleDiagnostic(CompactStr, &'static str, #[label] pub Span); +fn valid_title_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jest(valid-title): {x0:?}")) + .with_help(format!("{x1:?}")) + .with_labels([span2.into()]) +} #[derive(Debug, Default, Clone)] pub struct ValidTitle(Box); @@ -292,8 +291,8 @@ fn validate_title( if let Some(matched) = disallowed_words_reg.find(title) { let error = format!("{} is not allowed in test title", matched.as_str()); - ctx.diagnostic(ValidTitleDiagnostic( - CompactStr::from(error), + ctx.diagnostic(valid_title_diagnostic( + &error, "It is included in the `disallowedWords` of your config file, try to remove it from your title", span, )); @@ -325,11 +324,11 @@ fn validate_title( if !regex.is_match(title) { let raw_pattern = regex.as_str(); let message = message.as_ref().map_or_else( - || CompactStr::from(format!("{un_prefixed_name} should match {raw_pattern}")), - |message| CompactStr::from(message.as_str()), + || format!("{un_prefixed_name} should match {raw_pattern}"), + std::clone::Clone::clone, ); - ctx.diagnostic(ValidTitleDiagnostic( - message, + ctx.diagnostic(valid_title_diagnostic( + &message, "Make sure the title matches the `mustMatch` of your config file", span, )); @@ -340,12 +339,12 @@ fn validate_title( if regex.is_match(title) { let raw_pattern = regex.as_str(); let message = message.as_ref().map_or_else( - || CompactStr::from(format!("{un_prefixed_name} should not match {raw_pattern}")), - |message| CompactStr::from(message.as_str()), + || format!("{un_prefixed_name} should not match {raw_pattern}"), + std::string::ToString::to_string, ); - ctx.diagnostic(ValidTitleDiagnostic( - message, + ctx.diagnostic(valid_title_diagnostic( + &message, "Make sure the title not matches the `mustNotMatch` of your config file", span, )); @@ -382,7 +381,7 @@ impl Message { } fn diagnostic(&self, ctx: &LintContext, span: Span) { let (error, help) = self.detail(); - ctx.diagnostic(ValidTitleDiagnostic(CompactStr::from(error), help, span)); + ctx.diagnostic(valid_title_diagnostic(error, help, span)); } } diff --git a/crates/oxc_linter/src/rules/jsdoc/check_access.rs b/crates/oxc_linter/src/rules/jsdoc/check_access.rs index d87def7857ee..2499cb0adb0a 100644 --- a/crates/oxc_linter/src/rules/jsdoc/check_access.rs +++ b/crates/oxc_linter/src/rules/jsdoc/check_access.rs @@ -1,7 +1,4 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::phf_set; @@ -9,21 +6,18 @@ use rustc_hash::FxHashSet; use crate::{context::LintContext, rule::Rule, utils::should_ignore_as_internal}; -#[derive(Debug, Error, Diagnostic)] -enum CheckAccessDiagnostic { - #[error("eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing.")] - #[diagnostic( - severity(warning), - help("Valid access levels are `package`, `private`, `protected`, and `public`.") - )] - InvalidAccessLevel(#[label] Span), +fn invalid_access_level(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing.", + ) + .with_help("Valid access levels are `package`, `private`, `protected`, and `public`.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jsdoc(check-access): Mixing of @access with @public, @private, @protected, or @package on the same doc block.")] - #[diagnostic( - severity(warning), - help("There should be only one instance of access tag in a JSDoc comment.") - )] - RedundantAccessTags(#[label] Span), +fn redundant_access_tags(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(check-access): Mixing of @access with @public, @private, @protected, or @package on the same doc block.") + .with_help("There should be only one instance of access tag in a JSDoc comment.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -94,14 +88,12 @@ impl Rule for CheckAccess { if tag_name == resolved_access_tag_name && !ACCESS_LEVELS.contains(&comment.parsed()) { - ctx.diagnostic(CheckAccessDiagnostic::InvalidAccessLevel( - comment.span_trimmed_first_line(), - )); + ctx.diagnostic(invalid_access_level(comment.span_trimmed_first_line())); } // Has redundant access level? if 1 < access_related_tags_count { - ctx.diagnostic(CheckAccessDiagnostic::RedundantAccessTags(tag.kind.span)); + ctx.diagnostic(redundant_access_tags(tag.kind.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/check_property_names.rs b/crates/oxc_linter/src/rules/jsdoc/check_property_names.rs index f8737417db9b..dbc61a9503fe 100644 --- a/crates/oxc_linter/src/rules/jsdoc/check_property_names.rs +++ b/crates/oxc_linter/src/rules/jsdoc/check_property_names.rs @@ -1,8 +1,4 @@ -use miette::{miette, LabeledSpan}; -use oxc_diagnostics::{ - miette::{self, Diagnostic, Severity}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use rustc_hash::{FxHashMap, FxHashSet}; @@ -13,14 +9,12 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -enum CheckPropertyNamesDiagnostic { - #[error("eslint-plugin-jsdoc(check-property-names): No root defined for @property path.")] - #[diagnostic( - severity(warning), - help("@property path declaration `{1}` appears before any real property.") - )] - NoRoot(#[label] Span, String), +fn no_root(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(check-property-names): No root defined for @property path.", + ) + .with_help(format!("@property path declaration `{x1}` appears before any real property.")) + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -95,10 +89,7 @@ impl Rule for CheckPropertyNames { let parent_name = parent_name.trim_end_matches("[]"); if !seen.contains_key(&parent_name) { - ctx.diagnostic(CheckPropertyNamesDiagnostic::NoRoot( - name_part.span, - type_name.to_string(), - )); + ctx.diagnostic(no_root(name_part.span, type_name)); } } @@ -107,18 +98,22 @@ impl Rule for CheckPropertyNames { } for (type_name, spans) in seen.iter().filter(|(_, spans)| 1 < spans.len()) { - ctx.diagnostic(miette!( - severity = Severity::Warning, - labels = spans - .iter() - .map(|span| LabeledSpan::at( + let labels = spans + .iter() + .map(|span| { + LabeledSpan::at( (span.start as usize)..(span.end as usize), "Duplicated property".to_string(), - )) - .collect::>(), - help = format!("@property `{type_name}` is duplicated on the same block."), - "eslint-plugin-jsdoc(check-property-names): Duplicate @property found." - )); + ) + }) + .collect::>(); + ctx.diagnostic( + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(check-property-names): Duplicate @property found.", + ) + .with_help(format!("@property `{type_name}` is duplicated on the same block.")) + .with_labels(labels), + ); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs b/crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs index 6f31fbf313aa..96cb87005d3d 100644 --- a/crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs +++ b/crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::phf_set; @@ -13,10 +11,11 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(check-tag-names): Invalid tag name found.")] -#[diagnostic(severity(warning), help("{1}"))] -struct CheckTagNamesDiagnostic(#[label] pub Span, String); +fn check_tag_names_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(check-tag-names): Invalid tag name found.") + .with_help(x1.to_string()) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct CheckTagNames(Box); @@ -235,39 +234,33 @@ impl Rule for CheckTagNames { // If user explicitly blocked, report if let Some(reason) = settings.check_blocked_tag_name(tag_name) { - ctx.diagnostic(CheckTagNamesDiagnostic(tag.kind.span, reason)); + ctx.diagnostic(check_tag_names_diagnostic(tag.kind.span, &reason)); continue; } // If preferred or default aliased, report to use it if let Some(reason) = settings.check_preferred_tag_name(tag_name) { - ctx.diagnostic(CheckTagNamesDiagnostic(tag.kind.span, reason)); + ctx.diagnostic(check_tag_names_diagnostic(tag.kind.span, &reason)); continue; } // Additional check for `typed` mode if config.typed { if ALWAYS_INVALID_TAGS_IF_TYPED.contains(tag_name) { - ctx.diagnostic(CheckTagNamesDiagnostic( + ctx.diagnostic(check_tag_names_diagnostic( tag.kind.span, - format!("`@{tag_name}` is redundant when using a type system."), + &format!("`@{tag_name}` is redundant when using a type system."), )); continue; } if tag.kind.parsed() == "template" && tag.comment().parsed().is_empty() { - ctx.diagnostic(CheckTagNamesDiagnostic( - tag.kind.span, - format!("`@{tag_name}` without a name is redundant when using a type system."), - )); + ctx.diagnostic(check_tag_names_diagnostic(tag.kind.span, &format!("`@{tag_name}` without a name is redundant when using a type system."))); continue; } if !is_ambient && OUTSIDE_AMBIENT_INVALID_TAGS_IF_TYPED.contains(tag_name) { - ctx.diagnostic(CheckTagNamesDiagnostic( - tag.kind.span, - format!("`@{tag_name}` is redundant outside of ambient(`declare` or `.d.ts`) contexts when using a type system."), - )); + ctx.diagnostic(check_tag_names_diagnostic(tag.kind.span, &format!("`@{tag_name}` is redundant outside of ambient(`declare` or `.d.ts`) contexts when using a type system."))); continue; } } @@ -276,9 +269,9 @@ impl Rule for CheckTagNames { let is_valid = (config.jsx_tags && JSX_TAGS.contains(tag_name)) || VALID_BLOCK_TAGS.contains(tag_name); if !is_valid { - ctx.diagnostic(CheckTagNamesDiagnostic( + ctx.diagnostic(check_tag_names_diagnostic( tag.kind.span, - format!("`@{tag_name}` is invalid tag name."), + &format!("`@{tag_name}` is invalid tag name."), )); continue; } @@ -292,124 +285,165 @@ fn test() { use crate::tester::Tester; let pass = vec![ -(" + ( + " /** * @param foo (pass: valid name) */ function quux (foo) { - + } - ", None, None), -(" + ", + None, + None, + ), + ( + " /** * @memberof! foo (pass: valid name) */ function quux (foo) { - + } - ", None, None), -(" + ", + None, + None, + ), + ( + " /** * @bar foo (pass: invalid name but defined) */ function quux (foo) { - + } - ", Some(serde_json::json!([ - { - "definedTags": [ - "bar", - ], - }, - ])), None), -(" + ", + Some(serde_json::json!([ + { + "definedTags": [ + "bar", + ], + }, + ])), + None, + ), + ( + " /** * @baz @bar foo (pass: invalid names but defined) */ function quux (foo) { - + } - ", Some(serde_json::json!([ - { - "definedTags": [ - "baz", "bar", - ], - }, - ])), None), -(" + ", + Some(serde_json::json!([ + { + "definedTags": [ + "baz", "bar", + ], + }, + ])), + None, + ), + ( + " /** * @baz @bar foo (pass: invalid names but user preferred) */ function quux (foo) { - + } - ", None, Some(serde_json::json!({ - "settings": { "jsdoc": { - "tagNamePreference": { - "param": "baz", - "returns": { - "message": "Prefer `bar`", - "replacement": "bar", - }, - "todo": false, - }, - }}, - }))), -(" + ", + None, + Some(serde_json::json!({ + "settings": { "jsdoc": { + "tagNamePreference": { + "param": "baz", + "returns": { + "message": "Prefer `bar`", + "replacement": "bar", + }, + "todo": false, + }, + }}, + })), + ), + ( + " /** * @arg foo (pass: invalid name but user preferred) */ function quux (foo) { - + } - ", None, Some(serde_json::json!({ - "settings" : { "jsdoc": { - "tagNamePreference": { - "param": "arg", - }, - }}, - }))), -(" + ", + None, + Some(serde_json::json!({ + "settings" : { "jsdoc": { + "tagNamePreference": { + "param": "arg", + }, + }}, + })), + ), + ( + " /** * @returns (pass: valid name) */ function quux (foo) {} - ", None, None), -("", None, None), -(" + ", + None, + None, + ), + ("", None, None), + ( + " /** * (pass: no tag) */ function quux (foo) { - + } - ", None, None), -(" + ", + None, + None, + ), + ( + " /** * @todo (pass: valid name) */ function quux () { - + } - ", None, None), -(" + ", + None, + None, + ), + ( + " /** * @extends Foo (pass: invalid name but user preferred) */ function quux () { - + } - ", None, Some(serde_json::json!({ - "settings" : { "jsdoc": { - "tagNamePreference": { - "augments": { - "message": "@extends is to be used over @augments.", - "replacement": "extends", - }, - }, - }}, - }))), -(" + ", + None, + Some(serde_json::json!({ + "settings" : { "jsdoc": { + "tagNamePreference": { + "augments": { + "message": "@extends is to be used over @augments.", + "replacement": "extends", + }, + }, + }}, + })), + ), + ( + " /** * (Set tag name preference to itself to get aliases to * work along with main tag name.) @@ -418,14 +452,18 @@ fn test() { */ function quux () { } - ", None, Some(serde_json::json!({ - "settings" : { "jsdoc": { - "tagNamePreference": { - "extends": "extends", - }, - }}, - }))), -(" + ", + None, + Some(serde_json::json!({ + "settings" : { "jsdoc": { + "tagNamePreference": { + "extends": "extends", + }, + }}, + })), + ), + ( + " /** * Registers the `target` class as a transient dependency; each time the dependency is resolved a new instance will be created. * @@ -440,32 +478,47 @@ fn test() { export function transient(target?: T): T { // ... } - ", None, None), -(" + ", + None, + None, + ), + ( + " /** @jsx h */ /** @jsxFrag Fragment */ /** @jsxImportSource preact */ /** @jsxRuntime automatic (pass: valid jsx names)*/ - ", Some(serde_json::json!([ - { - "jsxTags": true, - }, - ])), None), -(" + ", + Some(serde_json::json!([ + { + "jsxTags": true, + }, + ])), + None, + ), + ( + " /** * @internal (pass: valid name) */ - ", None, Some(serde_json::json!({ - "settings" : { "jsdoc": { }}, - }))), -(" + ", + None, + Some(serde_json::json!({ + "settings" : { "jsdoc": { }}, + })), + ), + ( + " /** * @overload * @satisfies (pass: valid names) */ - ", None, Some(serde_json::json!({ - "settings" : { "jsdoc": { }}, - }))), + ", + None, + Some(serde_json::json!({ + "settings" : { "jsdoc": { }}, + })), + ), ( " /** @@ -477,30 +530,42 @@ fn test() { None, ), // Typed - (" + ( + " /** @default 0 */ let a; - ", Some(serde_json::json!([ - { - "typed": true, - }, - ])), None), -(" + ", + Some(serde_json::json!([ + { + "typed": true, + }, + ])), + None, + ), + ( + " /** @template name */ let a; - ", Some(serde_json::json!([ - { - "typed": true, - }, - ])), None), -(" + ", + Some(serde_json::json!([ + { + "typed": true, + }, + ])), + None, + ), + ( + " /** @param param - takes information */ function takesOne(param) {} - ", Some(serde_json::json!([ - { - "typed": true, - }, - ])), None), + ", + Some(serde_json::json!([ + { + "typed": true, + }, + ])), + None, + ), ]; let fail = vec![ @@ -518,7 +583,7 @@ fn test() { * @Param (fail: invalid name) */ function quux () { - + } ", None, @@ -530,7 +595,7 @@ fn test() { * @foo (fail: invalid name) */ function quux () { - + } ", None, @@ -542,7 +607,7 @@ fn test() { * @arg foo (fail: invalid name, default aliased) */ function quux (foo) { - + } ", None, diff --git a/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs b/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs index d7d9d564ff1a..11d5fd5ab0db 100644 --- a/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs +++ b/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::phf_set; @@ -9,10 +7,13 @@ use serde::Deserialize; use crate::{context::LintContext, rule::Rule, utils::should_ignore_as_private}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content.")] -#[diagnostic(severity(warning), help("`@{1}` tag should not have body."))] -struct EmptyTagsDiagnostic(#[label] Span, String); +fn empty_tags_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content.", + ) + .with_help(format!("`@{x1}` tag should not have body.")) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct EmptyTags(Box); @@ -125,10 +126,7 @@ impl Rule for EmptyTags { continue; } - ctx.diagnostic(EmptyTagsDiagnostic( - comment.span_trimmed_first_line(), - tag_name.to_string(), - )); + ctx.diagnostic(empty_tags_diagnostic(comment.span_trimmed_first_line(), tag_name)); } } } @@ -145,7 +143,7 @@ fn test() { * @abstract */ function quux () { - + } ", None, @@ -157,7 +155,7 @@ fn test() { * */ function quux () { - + } ", None, @@ -169,7 +167,7 @@ fn test() { * @param aName */ function quux () { - + } ", None, @@ -183,7 +181,7 @@ fn test() { * @async */ function quux () { - + } ", None, @@ -211,7 +209,7 @@ fn test() { * @private */ function quux () { - + } ", None, @@ -223,7 +221,7 @@ fn test() { * @internal */ function quux () { - + } ", None, @@ -254,7 +252,7 @@ fn test() { * @abstract extra text */ function quux () { - + } ", None, @@ -280,7 +278,7 @@ fn test() { * @abstract extra text */ quux () { - + } } ", @@ -295,7 +293,7 @@ fn test() { * @async out of place */ function quux () { - + } ", None, @@ -307,7 +305,7 @@ fn test() { * @event anEvent */ function quux () { - + } ", Some(serde_json::json!([ @@ -326,7 +324,7 @@ fn test() { * bar */ function quux () { - + } ", None, @@ -339,7 +337,7 @@ fn test() { * foo */ function quux () { - + } ", None, @@ -351,7 +349,7 @@ fn test() { * @private {someType} */ function quux () { - + } ", None, diff --git a/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs b/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs index 288287dba4b7..3fe116837e9a 100644 --- a/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs +++ b/crates/oxc_linter/src/rules/jsdoc/implements_on_classes.rs @@ -6,19 +6,16 @@ use crate::{ AstNode, }; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jsdoc(implements-on-classes): `@implements` used on a non-constructor function" -)] -#[diagnostic(severity(warning), help("Add `@class` tag or use ES6 class syntax."))] -struct ImplementsOnClassesDiagnostic(#[label] pub Span); +fn implements_on_classes_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(implements-on-classes): `@implements` used on a non-constructor function") + .with_help("Add `@class` tag or use ES6 class syntax.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct ImplementsOnClasses; @@ -116,7 +113,7 @@ impl Rule for ImplementsOnClasses { if let Some(span) = implements_found { if !class_or_ctor_found { - ctx.diagnostic(ImplementsOnClassesDiagnostic(span)); + ctx.diagnostic(implements_on_classes_diagnostic(span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/no_defaults.rs b/crates/oxc_linter/src/rules/jsdoc/no_defaults.rs index 20bc206a54c7..2fd0cf552bc5 100644 --- a/crates/oxc_linter/src/rules/jsdoc/no_defaults.rs +++ b/crates/oxc_linter/src/rules/jsdoc/no_defaults.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use serde::Deserialize; @@ -14,10 +12,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(no-defaults): Defaults are not permitted.")] -#[diagnostic(severity(warning), help("{1}"))] -struct NoDefaultsDiagnostic(#[label] pub Span, String); +fn no_defaults_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(no-defaults): Defaults are not permitted.") + .with_help(x1.to_string()) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct NoDefaults(Box); @@ -97,13 +96,13 @@ impl Rule for NoDefaults { } match (config.no_optional_param_names, name_part.default) { - (true, _) => ctx.diagnostic(NoDefaultsDiagnostic( + (true, _) => ctx.diagnostic(no_defaults_diagnostic( name_part.span, - format!("Optional param names are not permitted on `@{tag_name}` tag."), + &format!("Optional param names are not permitted on `@{tag_name}` tag."), )), - (false, true) => ctx.diagnostic(NoDefaultsDiagnostic( + (false, true) => ctx.diagnostic(no_defaults_diagnostic( name_part.span, - format!("Defaults are not permitted on `@{tag_name}` tag."), + &format!("Defaults are not permitted on `@{tag_name}` tag."), )), (false, false) => {} } @@ -123,7 +122,7 @@ fn test() { * @param foo */ function quux (foo) { - + } ", None, @@ -135,7 +134,7 @@ fn test() { * @param {number} foo */ function quux (foo) { - + } ", None, @@ -148,7 +147,7 @@ fn test() { * @param {number} [bar] */ function quux (foo) { - + } ", None, @@ -195,7 +194,7 @@ fn test() { * @param {number} foo */ function quux (foo) { - + } ", Some(serde_json::json!([ @@ -230,7 +229,7 @@ fn test() { * @param {number} [foo="7"] */ function quux (foo) { - + } "#, None, @@ -243,7 +242,7 @@ fn test() { * @param {number} [bar = 1] */ const quux = (foo, bar = 1) => { - + } ", None, @@ -256,7 +255,7 @@ fn test() { * @param {number} [foo="7"] */ quux (foo) { - + } } "#, @@ -269,7 +268,7 @@ fn test() { * @param {number} [foo="7"] */ function quux (foo) { - + } "#, Some(serde_json::json!([ @@ -285,7 +284,7 @@ fn test() { * @param {number} [foo] */ function quux (foo) { - + } ", Some(serde_json::json!([ @@ -301,7 +300,7 @@ fn test() { * @arg {number} [foo="7"] */ function quux (foo) { - + } "#, None, diff --git a/crates/oxc_linter/src/rules/jsdoc/require_property.rs b/crates/oxc_linter/src/rules/jsdoc/require_property.rs index 97bef7450e2e..27f8db98320a 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_property.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_property.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -11,14 +9,11 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(require-property): The `@typedef` and `@namespace` tags must include a `@property` tag with the type Object.")] -#[diagnostic( - severity(warning), - help("Consider adding a `@property` tag or replacing it with a more specific type.") -)] - -struct RequirePropertyDiagnostic(#[label] pub Span); +fn require_property_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(require-property): The `@typedef` and `@namespace` tags must include a `@property` tag with the type Object.") + .with_help("Consider adding a `@property` tag or replacing it with a more specific type.") + .and_label(span) +} #[derive(Debug, Default, Clone)] pub struct RequireProperty; @@ -79,7 +74,7 @@ impl Rule for RequireProperty { // - This JSDoc has multiple `@typedef` or `@namespace` tags // - And previous `@typedef` or `@namespace` tag did not have `@property` tag if let Some(span) = should_report { - ctx.diagnostic(RequirePropertyDiagnostic(span)); + ctx.diagnostic(require_property_diagnostic(span)); } let (Some(type_part), _, _) = tag.type_name_comment() else { @@ -99,7 +94,7 @@ impl Rule for RequireProperty { } if let Some(span) = should_report { - ctx.diagnostic(RequirePropertyDiagnostic(span)); + ctx.diagnostic(require_property_diagnostic(span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/require_property_description.rs b/crates/oxc_linter/src/rules/jsdoc/require_property_description.rs index 3c09cccb1aa0..8d8ded15b2ed 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_property_description.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_property_description.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -11,10 +9,13 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(require-property-description): Missing description in @property tag.")] -#[diagnostic(severity(warning), help("Add a description to this @property tag."))] -struct RequirePropertyDescriptionDiagnostic(#[label] pub Span); +fn require_property_description_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(require-property-description): Missing description in @property tag.", + ) + .with_help("Add a description to this @property tag.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequirePropertyDescription; @@ -67,7 +68,7 @@ impl Rule for RequirePropertyDescription { continue; }; - ctx.diagnostic(RequirePropertyDescriptionDiagnostic(tag_name.span)); + ctx.diagnostic(require_property_description_diagnostic(tag_name.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/require_property_name.rs b/crates/oxc_linter/src/rules/jsdoc/require_property_name.rs index 853a4b14385e..2ac229a9ac14 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_property_name.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_property_name.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -11,10 +9,13 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(require-property-name): Missing name in @property tag.")] -#[diagnostic(severity(warning), help("Add a type name to this @property tag."))] -struct RequirePropertyNameDiagnostic(#[label] pub Span); +fn require_property_name_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(require-property-name): Missing name in @property tag.", + ) + .with_help("Add a type name to this @property tag.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequirePropertyName; @@ -67,7 +68,7 @@ impl Rule for RequirePropertyName { continue; }; - ctx.diagnostic(RequirePropertyNameDiagnostic(tag_name.span)); + ctx.diagnostic(require_property_name_diagnostic(tag_name.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/require_property_type.rs b/crates/oxc_linter/src/rules/jsdoc/require_property_type.rs index 5412a79a335f..522e0cedea54 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_property_type.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_property_type.rs @@ -1,7 +1,5 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -11,10 +9,13 @@ use crate::{ utils::{should_ignore_as_internal, should_ignore_as_private}, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsdoc(require-property-type): Missing type in @property tag.")] -#[diagnostic(severity(warning), help("Add a {{type}} to this @property tag."))] -struct RequirePropertyTypeDiagnostic(#[label] pub Span); +fn require_property_type_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsdoc(require-property-type): Missing type in @property tag.", + ) + .with_help("Add a {type} to this @property tag.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct RequirePropertyType; @@ -67,7 +68,7 @@ impl Rule for RequirePropertyType { continue; }; - ctx.diagnostic(RequirePropertyTypeDiagnostic(tag_name.span)); + ctx.diagnostic(require_property_type_diagnostic(tag_name.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs index a0473661360f..7a8c95e1e857 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_semantic::{JSDoc, JSDocTag}; use oxc_span::Span; @@ -19,19 +17,22 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum RequireYieldsDiagnostic { - #[error("eslint-plugin-jsdoc(require-yields): Missing JSDoc `@yields` declaration for generator function.")] - #[diagnostic(severity(warning), help("Add `@yields` tag to the JSDoc comment."))] - MissingYields(#[label] Span), - #[error("eslint-plugin-jsdoc(require-yields): Duplicate `@yields` tags.")] - #[diagnostic(severity(warning), help("Remove redundunt `@yields` tag."))] - DuplicateYields(#[label] Span), - #[error( - "eslint-plugin-jsdoc(require-yields): `@yields` tag is required when using `@generator` tag." - )] - #[diagnostic(severity(warning), help("Add `@yields` tag to the JSDoc comment."))] - MissingYieldsWithGenerator(#[label] Span), +fn missing_yields(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(require-yields): Missing JSDoc `@yields` declaration for generator function.") + .with_help("Add `@yields` tag to the JSDoc comment.") + .with_labels([span0.into()]) +} + +fn duplicate_yields(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(require-yields): Duplicate `@yields` tags.") + .with_help("Remove redundunt `@yields` tag.") + .with_labels([span0.into()]) +} + +fn missing_yields_with_generator(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsdoc(require-yields): `@yields` tag is required when using `@generator` tag.") + .with_help("Add `@yields` tag to the JSDoc comment.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -144,7 +145,7 @@ impl Rule for RequireYields { if config.force_require_yields && is_missing_yields_tag(&jsdoc_tags, &resolved_yields_tag_name) { - ctx.diagnostic(RequireYieldsDiagnostic::MissingYields(func.span)); + ctx.diagnostic(missing_yields(func.span)); return; } @@ -152,7 +153,7 @@ impl Rule for RequireYields { if let Some(span) = is_duplicated_yields_tag(&jsdoc_tags, &resolved_yields_tag_name) { - ctx.diagnostic(RequireYieldsDiagnostic::DuplicateYields(span)); + ctx.diagnostic(duplicate_yields(span)); return; } @@ -164,7 +165,7 @@ impl Rule for RequireYields { &resolved_yields_tag_name, &resolved_generator_tag_name, ) { - ctx.diagnostic(RequireYieldsDiagnostic::MissingYieldsWithGenerator(span)); + ctx.diagnostic(missing_yields_with_generator(span)); } } } @@ -230,7 +231,7 @@ impl Rule for RequireYields { let resolved_yields_tag_name = settings.resolve_tag_name("yields"); if is_missing_yields_tag(&jsdoc_tags, &resolved_yields_tag_name) { - ctx.diagnostic(RequireYieldsDiagnostic::MissingYields(generator_func.span)); + ctx.diagnostic(missing_yields(generator_func.span)); } } _ => {} @@ -302,7 +303,7 @@ fn test() { * @yields Foo. */ function * quux () { - + yield foo; } ", 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 c956dc447d1f..2c155c19bcc7 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs @@ -2,10 +2,7 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElement, JSXOpeningElement}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -15,49 +12,56 @@ use crate::utils::{ }; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -enum AltTextDiagnostic { - // - #[error("eslint-plugin-jsx-a11y(alt-text): Missing `alt` attribute.")] - #[diagnostic(severity(warning), help("Must have `alt` prop, either with meaningful text, or an empty string for decorative images."))] - MissingAltProp(#[label] Span), - - #[error("eslint-plugin-jsx-a11y(alt-text): Invalid `alt` value.")] - #[diagnostic( - severity(warning), - help("Must have meaningful value for `alt` prop. Use alt=\"\" for presentational images.") - )] - MissingAltValue(#[label] Span), - - #[error("eslint-plugin-jsx-a11y(alt-text): Missing value for aria-label attribute.")] - #[diagnostic(severity(warning), help("The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images."))] - AriaLabelValue(#[label] Span), - - #[error("eslint-plugin-jsx-a11y(alt-text): Missing value for aria-labelledby attribute.")] - #[diagnostic( - severity(warning), - help("The alt attribute is preferred over aria-labelledby for images.") - )] - AriaLabelledByValue(#[label] Span), - - #[error("eslint-plugin-jsx-a11y(alt-text): ARIA used where native HTML could suffice.")] - #[diagnostic(severity(warning), help("Prefer alt=\"\" over presentational role. Native HTML attributes should be preferred for accessibility before resorting to ARIA attributes."))] - PreferAlt(#[label] Span), - - // - #[error("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.")] - #[diagnostic(severity(warning), help("Embedded elements must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop."))] - Object(#[label] Span), - - // - #[error("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.")] - #[diagnostic(severity(warning), help("Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop."))] - Area(#[label] Span), - - // - #[error("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.")] - #[diagnostic(severity(warning), help(" elements with type=\"image\" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop."))] - InputTypeImage(#[label] Span), +fn missing_alt_prop(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Missing `alt` attribute.") + .with_help("Must have `alt` prop, either with meaningful text, or an empty string for decorative images.") + .with_labels([span0.into()]) +} + +fn missing_alt_value(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Invalid `alt` value.") + .with_help( + "Must have meaningful value for `alt` prop. Use alt=\"\" for presentational images.", + ) + .with_labels([span0.into()]) +} + +fn aria_label_value(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Missing value for aria-label attribute.") + .with_help("The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images.") + .with_labels([span0.into()]) +} + +fn aria_labelled_by_value(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsx-a11y(alt-text): Missing value for aria-labelledby attribute.", + ) + .with_help("The alt attribute is preferred over aria-labelledby for images.") + .with_labels([span0.into()]) +} + +fn prefer_alt(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): ARIA used where native HTML could suffice.") + .with_help("Prefer alt=\"\" over presentational role. Native HTML attributes should be preferred for accessibility before resorting to ARIA attributes.") + .with_labels([span0.into()]) +} + +fn object(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.") + .with_help("Embedded elements must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.") + .with_labels([span0.into()]) +} + +fn area(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.") + .with_help("Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.") + .with_labels([span0.into()]) +} + +fn input_type_image(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(alt-text): Missing alternative text.") + .with_help(" elements with type=\"image\" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -162,8 +166,12 @@ impl Rule for AltText { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { return }; - let Some(name) = &get_element_type(ctx, jsx_el) else { return }; + let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { + return; + }; + let Some(name) = &get_element_type(ctx, jsx_el) else { + return; + }; // if let Some(custom_tags) = &self.img { @@ -239,31 +247,31 @@ fn aria_label_has_value<'a>(item: &'a JSXAttributeItem<'a>) -> bool { fn img_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) { if let Some(alt_prop) = has_jsx_prop_lowercase(node, "alt") { if !is_valid_alt_prop(alt_prop) { - ctx.diagnostic(AltTextDiagnostic::MissingAltValue(node.span)); + ctx.diagnostic(missing_alt_value(node.span)); } return; } if has_jsx_prop_lowercase(node, "role").map_or(false, is_presentation_role) { - ctx.diagnostic(AltTextDiagnostic::PreferAlt(node.span)); + ctx.diagnostic(prefer_alt(node.span)); return; } if let Some(aria_label_prop) = has_jsx_prop_lowercase(node, "aria-label") { if !aria_label_has_value(aria_label_prop) { - ctx.diagnostic(AltTextDiagnostic::AriaLabelValue(node.span)); + ctx.diagnostic(aria_label_value(node.span)); } return; } if let Some(aria_labelledby_prop) = has_jsx_prop_lowercase(node, "aria-labelledby") { if !aria_label_has_value(aria_labelledby_prop) { - ctx.diagnostic(AltTextDiagnostic::AriaLabelledByValue(node.span)); + ctx.diagnostic(aria_labelled_by_value(node.span)); } return; } - ctx.diagnostic(AltTextDiagnostic::MissingAltProp(node.span)); + ctx.diagnostic(missing_alt_prop(node.span)); } fn object_rule<'a>( @@ -283,7 +291,7 @@ fn object_rule<'a>( if has_label || has_title_attr || object_has_accessible_child(ctx, parent) { return; } - ctx.diagnostic(AltTextDiagnostic::Object(node.span)); + ctx.diagnostic(object(node.span)); } fn area_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) { @@ -297,11 +305,11 @@ fn area_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) { } has_jsx_prop_lowercase(node, "alt").map_or_else( || { - ctx.diagnostic(AltTextDiagnostic::Area(node.span)); + ctx.diagnostic(area(node.span)); }, |alt_prop| { if !is_valid_alt_prop(alt_prop) { - ctx.diagnostic(AltTextDiagnostic::Area(node.span)); + ctx.diagnostic(area(node.span)); } }, ); @@ -318,11 +326,11 @@ fn input_type_image_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext< } has_jsx_prop_lowercase(node, "alt").map_or_else( || { - ctx.diagnostic(AltTextDiagnostic::InputTypeImage(node.span)); + ctx.diagnostic(input_type_image(node.span)); }, |alt_prop| { if !is_valid_alt_prop(alt_prop) { - ctx.diagnostic(AltTextDiagnostic::InputTypeImage(node.span)); + ctx.diagnostic(input_type_image(node.span)); } }, ); diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs index 153a1790c01b..0f9e87025efa 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs @@ -1,8 +1,5 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,18 +13,16 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum AnchorHasContentDiagnostic { - #[error("eslint-plugin-jsx-a11y(anchor-has-content): Missing accessible content when using `a` elements.")] - #[diagnostic( - severity(warning), - help("Provide screen reader accessible content when using `a` elements.") - )] - MissingContent(#[label] Span), +fn missing_content(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(anchor-has-content): Missing accessible content when using `a` elements.") + .with_help("Provide screen reader accessible content when using `a` elements.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jsx-a11y(anchor-has-content): Missing accessible content when using `a` elements.")] - #[diagnostic(severity(warning), help("Remove the `aria-hidden` attribute to allow the anchor element and its content visible to assistive technologies."))] - RemoveAriaHidden(#[label] Span), +fn remove_aria_hidden(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(anchor-has-content): Missing accessible content when using `a` elements.") + .with_help("Remove the `aria-hidden` attribute to allow the anchor element and its content visible to assistive technologies.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -70,10 +65,12 @@ declare_oxc_lint!( impl Rule for AnchorHasContent { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXElement(jsx_el) = node.kind() { - let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { return }; + let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { + return; + }; if name == "a" { if is_hidden_from_screen_reader(ctx, &jsx_el.opening_element) { - ctx.diagnostic(AnchorHasContentDiagnostic::RemoveAriaHidden(jsx_el.span)); + ctx.diagnostic(remove_aria_hidden(jsx_el.span)); return; } @@ -87,7 +84,7 @@ impl Rule for AnchorHasContent { }; } - ctx.diagnostic(AnchorHasContentDiagnostic::MissingContent(jsx_el.span)); + ctx.diagnostic(missing_content(jsx_el.span)); } } } 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 9d41f2d80555..231dfebf1737 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 @@ -2,10 +2,7 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,21 +13,28 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum AnchorIsValidDiagnostic { - #[error( - "eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element." - )] - #[diagnostic(severity(warning), help("Provide an href for the `a` element."))] - MissingHrefAttribute(#[label] Span), +fn missing_href_attribute(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element.", + ) + .with_help("Provide an href for the `a` element.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.")] - #[diagnostic(severity(warning), help("Provide a correct href for the `a` element."))] - IncorrectHref(#[label] Span), +fn incorrect_href(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.", + ) + .with_help("Provide a correct href for the `a` element.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.")] - #[diagnostic(severity(warning), help("Use a `button` element instead of an `a` element."))] - CantBeAnchor(#[label] Span), +fn cant_be_anchor(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.", + ) + .with_help("Use a `button` element instead of an `a` element.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -128,8 +132,12 @@ impl Rule for AnchorIsValid { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXElement(jsx_el) = node.kind() { - let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else { return }; - let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { return }; + let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else { + return; + }; + let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { + return; + }; if name == "a" { if let Option::Some(herf_attr) = has_jsx_prop_lowercase(&jsx_el.opening_element, "href") @@ -143,19 +151,15 @@ impl Rule for AnchorIsValid { if has_jsx_prop_lowercase(&jsx_el.opening_element, "onclick") .is_some() { - ctx.diagnostic(AnchorIsValidDiagnostic::CantBeAnchor( - ident.span, - )); + ctx.diagnostic(cant_be_anchor(ident.span)); return; } - ctx.diagnostic(AnchorIsValidDiagnostic::IncorrectHref( - ident.span, - )); + ctx.diagnostic(incorrect_href(ident.span)); return; } } None => { - ctx.diagnostic(AnchorIsValidDiagnostic::IncorrectHref(ident.span)); + ctx.diagnostic(incorrect_href(ident.span)); return; } }, @@ -175,7 +179,7 @@ impl Rule for AnchorIsValid { if has_spreed_attr { return; } - ctx.diagnostic(AnchorIsValidDiagnostic::MissingHrefAttribute(ident.span)); + ctx.diagnostic(missing_href_attribute(ident.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs index 98ad800e7e47..19c533b8cb1d 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{JSXAttribute, JSXAttributeItem, JSXElementName}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -17,13 +15,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(aria-activedescendant-has-tabindex): Enforce elements with aria-activedescendant are tabbable.")] -#[diagnostic( - severity(warning), - help("An element that manages focus with `aria-activedescendant` must have a tabindex.") -)] -struct AriaActivedescendantHasTabindexDiagnostic(#[label] pub Span); +fn aria_activedescendant_has_tabindex_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(aria-activedescendant-has-tabindex): Enforce elements with aria-activedescendant are tabbable.") + .with_help("An element that manages focus with `aria-activedescendant` must have a tabindex.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct AriaActivedescendantHasTabindex; @@ -91,7 +87,7 @@ impl Rule for AriaActivedescendantHasTabindex { return; }; - ctx.diagnostic(AriaActivedescendantHasTabindexDiagnostic(identifier.span)); + ctx.diagnostic(aria_activedescendant_has_tabindex_diagnostic(identifier.span)); } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs index bae7fba659c8..5a26a45b42b5 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::JSXAttributeItem, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -11,10 +9,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(aria-props): Invalid ARIA prop.")] -#[diagnostic(severity(warning), help("`{1}` is an invalid ARIA attribute."))] -struct AriaPropsDiagnostic(#[label] pub Span, String); +fn aria_props_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(aria-props): Invalid ARIA prop.") + .with_help(format!("`{x1}` is an invalid ARIA attribute.")) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct AriaProps; @@ -44,7 +43,7 @@ impl Rule for AriaProps { if let AstKind::JSXAttributeItem(JSXAttributeItem::Attribute(attr)) = node.kind() { let name = get_jsx_attribute_name(&attr.name).to_lowercase(); if name.starts_with("aria-") && !VALID_ARIA_PROPS.contains(&name) { - ctx.diagnostic(AriaPropsDiagnostic(attr.span, name)); + ctx.diagnostic(aria_props_diagnostic(attr.span, &name)); } } } 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 85234aa5f681..d8bd21e75e98 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{JSXAttributeValue, JSXExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -17,13 +15,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(aria-role): Elements with ARIA roles must use a valid, non-abstract ARIA role.")] -#[diagnostic( - severity(warning), - help("Set a valid, non-abstract ARIA role for element with ARIA{1}") -)] -struct AriaRoleDiagnostic(#[label] pub Span, String); +fn aria_role_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(aria-role): Elements with ARIA roles must use a valid, non-abstract ARIA role.") + .with_help(format!("Set a valid, non-abstract ARIA role for element with ARIA{x1}")) + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct AriaRole(Box); @@ -96,7 +92,9 @@ declare_oxc_lint!( impl Rule for AriaRole { fn from_configuration(value: serde_json::Value) -> Self { - let Some(value) = value.as_array() else { return Self::default() }; + let Some(value) = value.as_array() else { + return Self::default(); + }; let mut ignore_non_dom = false; let mut allowed_invalid_roles: Vec = vec![]; @@ -138,7 +136,7 @@ impl Rule for AriaRole { Some(JSXAttributeValue::ExpressionContainer(container)) => { let jsexp = &container.expression; if matches!(jsexp, JSXExpression::NullLiteral(_)) || jsexp.is_undefined() { - ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new())); + ctx.diagnostic(aria_role_diagnostic(attr.span, "")); } } Some(JSXAttributeValue::StringLiteral(str)) => { @@ -148,14 +146,14 @@ impl Rule for AriaRole { !VALID_ARIA_ROLES.contains(word) && !self.allowed_invalid_roles.contains(&(*word).to_string()) }) { - ctx.diagnostic(AriaRoleDiagnostic( + ctx.diagnostic(aria_role_diagnostic( str.span, - format!(", `{error_prop}` is an invalid aria role"), + &format!(", `{error_prop}` is an invalid aria role"), )); } } _ => { - ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new())); + ctx.diagnostic(aria_role_diagnostic(attr.span, "")); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs index 3d34b8830692..c05c3b29f9d9 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs @@ -1,8 +1,6 @@ use oxc_ast::{ast::JSXAttributeItem, AstKind}; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::phf_set; @@ -36,10 +34,11 @@ declare_oxc_lint! { #[derive(Debug, Default, Clone)] pub struct AriaUnsupportedElements; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(aria-unsupported-elements): This element does not support ARIA roles, states and properties.")] -#[diagnostic(severity(warning), help("Try removing the prop `{1}`."))] -struct AriaUnsupportedElementsDiagnostic(#[label] pub Span, String); +fn aria_unsupported_elements_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(aria-unsupported-elements): This element does not support ARIA roles, states and properties.") + .with_help(format!("Try removing the prop `{x1}`.")) + .with_labels([span0.into()]) +} impl Rule for AriaUnsupportedElements { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { @@ -55,7 +54,7 @@ impl Rule for AriaUnsupportedElements { }; let attr_name = get_jsx_attribute_name(&attr.name).to_lowercase(); if INVALID_ATTRIBUTES.contains(&attr_name) { - ctx.diagnostic(AriaUnsupportedElementsDiagnostic(attr.span, attr_name)); + ctx.diagnostic(aria_unsupported_elements_diagnostic(attr.span, &attr_name)); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs index c2d39752d9cf..c91a15dcbe1b 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs @@ -8,22 +8,16 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeValue}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::{phf_map, phf_set}; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jsx-a11y(autocomplete-valid): `{autocomplete}` is not a valid value for autocomplete." -)] -#[diagnostic(severity(warning), help("Change `{autocomplete}` to a valid value for autocomplete."))] -struct AutocompleteValidDiagnostic { - #[label] - pub span: Span, - pub autocomplete: String, + +fn autocomplete_valid_diagnostic(span: Span, autocomplete: &str) -> OxcDiagnostic { + OxcDiagnostic::warning(format!("eslint-plugin-jsx-a11y(autocomplete-valid): `{autocomplete}` is not a valid value for autocomplete.")) + .with_help(format!("Change `{autocomplete}` to a valid value for autocomplete.")) + .with_label(span) } #[derive(Debug, Default, Clone)] @@ -182,7 +176,9 @@ impl Rule for AutocompleteValid { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { - let Some(name) = &get_element_type(ctx, jsx_el) else { return }; + let Some(name) = &get_element_type(ctx, jsx_el) else { + return; + }; if !self.input_components.contains(name) { return; } @@ -197,12 +193,9 @@ impl Rule for AutocompleteValid { let Some(JSXAttributeValue::StringLiteral(autocomplete_values)) = &attr.value else { return; }; - let value = autocomplete_values.value.to_string(); - if !is_valid_autocomplete_value(&value) { - ctx.diagnostic(AutocompleteValidDiagnostic { - span: attr.span, - autocomplete: value, - }); + let value = &autocomplete_values.value; + if !is_valid_autocomplete_value(value) { + ctx.diagnostic(autocomplete_valid_diagnostic(attr.span, value)); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs b/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs index 6a7402d3b2ef..574000a66ba8 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -17,10 +15,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(click-events-have-key-events): Enforce a clickable non-interactive element has at least one keyboard event listener.")] -#[diagnostic(severity(warning), help("Visible, non-interactive elements with click handlers must have one of keyup, keydown, or keypress listener."))] -struct ClickEventsHaveKeyEventsDiagnostic(#[label] pub Span); +fn click_events_have_key_events_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(click-events-have-key-events): Enforce a clickable non-interactive element has at least one keyboard event listener.") + .with_help("Visible, non-interactive elements with click handlers must have one of keyup, keydown, or keypress listener.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct ClickEventsHaveKeyEvents; @@ -82,7 +81,7 @@ impl Rule for ClickEventsHaveKeyEvents { return; } - ctx.diagnostic(ClickEventsHaveKeyEventsDiagnostic(jsx_opening_el.span)); + ctx.diagnostic(click_events_have_key_events_diagnostic(jsx_opening_el.span)); } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs index e3b757440db9..c76aa4ae4301 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs @@ -1,8 +1,6 @@ use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -13,13 +11,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint(heading-has-content): Headings must have content and the content must be accessible by a screen reader.")] -#[diagnostic( - severity(warning), - help("Provide screen reader accessible content when using heading elements.") -)] -struct HeadingHasContentDiagnostic(#[label] pub Span); +fn heading_has_content_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint(heading-has-content): Headings must have content and the content must be accessible by a screen reader.") + .with_help("Provide screen reader accessible content when using heading elements.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct HeadingHasContent(Box); @@ -116,7 +112,7 @@ impl Rule for HeadingHasContent { return; } - ctx.diagnostic(HeadingHasContentDiagnostic(jsx_el.span)); + ctx.diagnostic(heading_has_content_diagnostic(jsx_el.span)); } } 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 52adea4466fb..b783ad9b3029 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 @@ -2,10 +2,7 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,15 +13,18 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -enum HtmlHasLangDiagnostic { - #[error("eslint-plugin-jsx-a11y(html-has-lang): Missing lang attribute.")] - #[diagnostic(severity(warning), help("Add a lang attribute to the html element whose value represents the primary language of document."))] - MissingLangProp(#[label] Span), +fn missing_lang_prop(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(html-has-lang): Missing lang attribute.") + .with_help("Add a lang attribute to the html element whose value represents the primary language of document.") + .with_labels([span0.into()]) +} - #[error("eslint-plugin-jsx-a11y(html-has-lang): Missing value for lang attribute")] - #[diagnostic(severity(warning), help("Must have meaningful value for `lang` prop."))] - MissingLangValue(#[label] Span), +fn missing_lang_value(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning( + "eslint-plugin-jsx-a11y(html-has-lang): Missing value for lang attribute", + ) + .with_help("Must have meaningful value for `lang` prop.") + .with_labels([span0.into()]) } #[derive(Debug, Default, Clone)] @@ -73,10 +73,10 @@ impl Rule for HtmlHasLang { }; has_jsx_prop_lowercase(jsx_el, "lang").map_or_else( - || ctx.diagnostic(HtmlHasLangDiagnostic::MissingLangProp(identifier.span)), + || ctx.diagnostic(missing_lang_prop(identifier.span)), |lang_prop| { if !is_valid_lang_prop(lang_prop) { - ctx.diagnostic(HtmlHasLangDiagnostic::MissingLangValue(jsx_el.span)); + ctx.diagnostic(missing_lang_value(jsx_el.span)); } }, ); 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 9a8cb56af516..b0691bd90ad8 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 @@ -2,10 +2,8 @@ use oxc_ast::{ ast::{JSXAttributeValue, JSXElementName, JSXExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,12 +14,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error( - "eslint-plugin-jsx-a11y(iframe-has-title): Missing `title` attribute for the `iframe` element." -)] -#[diagnostic(severity(warning), help("Provide title property for iframe element."))] -struct IframeHasTitleDiagnostic(#[label] pub Span); +fn iframe_has_title_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(iframe-has-title): Missing `title` attribute for the `iframe` element.") + .with_help("Provide title property for iframe element.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct IframeHasTitle; @@ -71,14 +68,16 @@ impl Rule for IframeHasTitle { return; }; - let Some(name) = get_element_type(ctx, jsx_el) else { return }; + let Some(name) = get_element_type(ctx, jsx_el) else { + return; + }; if name != "iframe" { return; } let Some(alt_prop) = has_jsx_prop_lowercase(jsx_el, "title") else { - ctx.diagnostic(IframeHasTitleDiagnostic(iden.span)); + ctx.diagnostic(iframe_has_title_diagnostic(iden.span)); return; }; @@ -114,7 +113,7 @@ impl Rule for IframeHasTitle { _ => {} } - ctx.diagnostic(IframeHasTitleDiagnostic(iden.span)); + ctx.diagnostic(iframe_has_title_diagnostic(iden.span)); } } 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 86ce0db6c9bd..bc0b90a53b23 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,13 +1,11 @@ +use oxc_diagnostics::OxcDiagnostic; + use regex::Regex; use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -16,10 +14,9 @@ use crate::utils::{ }; use crate::{context::LintContext, rule::Rule, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(img-redundant-alt): Redundant alt attribute.")] -#[diagnostic(severity(warning), help("Provide no redundant alt text for image. Screen-readers already announce `img` tags as an image. You don’t need to use the words `image`, `photo,` or `picture` (or any specified custom words) in the alt prop."))] -struct ImgRedundantAltDiagnostic(#[label] pub Span); +fn img_redundant_alt_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(img-redundant-alt): Redundant alt attribute.").with_help("Provide no redundant alt text for image. Screen-readers already announce `img` tags as an image. You don’t need to use the words `image`, `photo,` or `picture` (or any specified custom words) in the alt prop.").with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct ImgRedundantAlt(Box); @@ -105,7 +102,9 @@ impl Rule for ImgRedundantAlt { Self(Box::new(img_redundant_alt)) } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { return }; + let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { + return; + }; let Some(element_type) = get_element_type(ctx, jsx_el) else { return; }; @@ -143,7 +142,7 @@ impl Rule for ImgRedundantAlt { let alt_text = lit.value.as_str(); if is_redundant_alt_text(alt_text, &self.redundant_words) { - ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); + ctx.diagnostic(img_redundant_alt_diagnostic(alt_attribute_name_span)); } } JSXAttributeValue::ExpressionContainer(container) => match &container.expression { @@ -151,7 +150,7 @@ impl Rule for ImgRedundantAlt { let alt_text = lit.value.as_str(); if is_redundant_alt_text(alt_text, &self.redundant_words) { - ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); + ctx.diagnostic(img_redundant_alt_diagnostic(alt_attribute_name_span)); } } JSXExpression::TemplateLiteral(lit) => { @@ -159,7 +158,7 @@ impl Rule for ImgRedundantAlt { let alt_text = quasi.value.raw.as_str(); if is_redundant_alt_text(alt_text, &self.redundant_words) { - ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); + ctx.diagnostic(img_redundant_alt_diagnostic(alt_attribute_name_span)); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs index bb1271b34743..6f9ffad7e2f3 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs @@ -3,10 +3,8 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -17,10 +15,11 @@ use crate::{ AstNode, }; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(lang): Lang attribute must have a valid value.")] -#[diagnostic(severity(warning), help("Set a valid value for lang attribute."))] -struct LangDiagnostic(#[label] pub Span); +fn lang_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warning("eslint-plugin-jsx-a11y(lang): Lang attribute must have a valid value.") + .with_help("Set a valid value for lang attribute.") + .with_labels([span0.into()]) +} #[derive(Debug, Default, Clone)] pub struct Lang; @@ -78,11 +77,11 @@ impl Rule for Lang { }; has_jsx_prop_lowercase(jsx_el, "lang").map_or_else( - || ctx.diagnostic(LangDiagnostic(identifier.span)), + || ctx.diagnostic(lang_diagnostic(identifier.span)), |lang_prop| { if !is_valid_lang_prop(lang_prop) { if let JSXAttributeItem::Attribute(attr) = lang_prop { - ctx.diagnostic(LangDiagnostic(attr.span)); + ctx.diagnostic(lang_diagnostic(attr.span)); } } }, 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 0af2afa97300..902e95b7d99a 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 @@ -2,22 +2,18 @@ use oxc_ast::{ ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXChild, JSXExpression}, AstKind, }; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::OxcDiagnostic; + use oxc_macros::declare_oxc_lint; use oxc_span::Span; use crate::{context::LintContext, rule::Rule, utils::get_element_type, AstNode}; -#[derive(Debug, Error, Diagnostic)] -#[error("eslint-plugin-jsx-a11y(media-has-caption): Missing element with captions inside