sonicop 26.8.111 → 26.8.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONFORMANCE.md +64 -31
- data/Cargo.lock +94 -19
- data/Cargo.toml +13 -8
- data/NOTICE +15 -3
- data/README.ja.md +145 -57
- data/README.md +140 -48
- data/build.rs +155 -0
- data/lib/sonicop/runner.rb +7 -1
- data/lib/sonicop/version.rb +1 -1
- data/src/cli.rs +216 -45
- data/src/config/inheritance.rs +637 -64
- data/src/config/mod.rs +130 -1
- data/src/config/paths.rs +550 -48
- data/src/cop_name.rs +18 -1
- data/src/diagnostic.rs +168 -3
- data/src/directives.rs +398 -77
- data/src/display_width.rs +82 -42
- data/src/display_width_table.rs +506 -0
- data/src/engine.rs +1532 -51
- data/src/formatter.rs +10 -1
- data/src/lib.rs +2 -0
- data/src/main.rs +112 -1
- data/src/nul_bytes.rs +245 -47
- data/src/parser.rs +37 -0
- data/src/profile.rs +174 -24
- data/src/ruby_version.rs +167 -21
- data/src/rules/bundler/duplicated_group.rs +3 -2
- data/src/rules/bundler/gem_comment.rs +5 -4
- data/src/rules/gemspec/attribute_assignment.rs +4 -3
- data/src/rules/gemspec/deprecated_attribute_assignment.rs +7 -6
- data/src/rules/gemspec/duplicated_assignment.rs +4 -3
- data/src/rules/gemspec/require_mfa.rs +10 -9
- data/src/rules/gemspec/required_ruby_version.rs +3 -2
- data/src/rules/gemspec/support.rs +28 -8
- data/src/rules/layout/access_modifier_indentation.rs +13 -6
- data/src/rules/layout/argument_alignment.rs +19 -7
- data/src/rules/layout/array_alignment.rs +1 -2
- data/src/rules/layout/block_alignment.rs +74 -30
- data/src/rules/layout/block_end_newline.rs +5 -5
- data/src/rules/layout/class_structure.rs +13 -4
- data/src/rules/layout/closing_heredoc_indentation.rs +24 -5
- data/src/rules/layout/closing_parenthesis_indentation.rs +3 -3
- data/src/rules/layout/comment_indentation.rs +2 -1
- data/src/rules/layout/condition_position.rs +3 -2
- data/src/rules/layout/def_end_alignment.rs +10 -6
- data/src/rules/layout/dot_position.rs +1 -2
- data/src/rules/layout/else_alignment.rs +24 -14
- data/src/rules/layout/empty_line_after_guard_clause.rs +8 -7
- data/src/rules/layout/empty_line_after_multiline_condition.rs +5 -5
- data/src/rules/layout/empty_line_between_defs.rs +8 -6
- data/src/rules/layout/empty_lines_around_access_modifier.rs +22 -10
- data/src/rules/layout/empty_lines_around_arguments.rs +4 -4
- data/src/rules/layout/empty_lines_around_body.rs +8 -2
- data/src/rules/layout/empty_lines_around_exception_handling_keywords.rs +1 -2
- data/src/rules/layout/empty_lines_around_method_body.rs +3 -2
- data/src/rules/layout/end_alignment.rs +32 -7
- data/src/rules/layout/end_of_line.rs +14 -1
- data/src/rules/layout/extra_spacing.rs +9 -11
- data/src/rules/layout/first_array_element_indentation.rs +1 -2
- data/src/rules/layout/first_hash_element_indentation.rs +8 -6
- data/src/rules/layout/hash_alignment.rs +113 -19
- data/src/rules/layout/heredoc_argument_closing_parenthesis.rs +6 -5
- data/src/rules/layout/heredoc_indentation.rs +4 -3
- data/src/rules/layout/indentation_style.rs +6 -4
- data/src/rules/layout/indentation_width.rs +76 -16
- data/src/rules/layout/initial_indentation.rs +18 -5
- data/src/rules/layout/line_continuation_leading_space.rs +4 -3
- data/src/rules/layout/line_continuation_spacing.rs +4 -3
- data/src/rules/layout/line_length.rs +417 -43
- data/src/rules/layout/mod.rs +1 -1
- data/src/rules/layout/multiline_assignment_layout.rs +20 -3
- data/src/rules/layout/multiline_block_layout.rs +1 -2
- data/src/rules/layout/multiline_expression.rs +60 -31
- data/src/rules/layout/multiline_hash_brace_layout.rs +4 -3
- data/src/rules/layout/multiline_method_call_indentation.rs +1 -1
- data/src/rules/layout/multiline_operation_indentation.rs +3 -3
- data/src/rules/layout/redundant_line_break.rs +16 -5
- data/src/rules/layout/rescue_ensure_alignment.rs +20 -12
- data/src/rules/layout/single_line_block_chain.rs +13 -4
- data/src/rules/layout/space_around_block_parameters.rs +16 -2
- data/src/rules/layout/space_around_equals_in_parameter_default.rs +1 -2
- data/src/rules/layout/space_around_keyword.rs +22 -4
- data/src/rules/layout/space_around_operators.rs +34 -2
- data/src/rules/layout/space_before_brackets.rs +13 -6
- data/src/rules/layout/space_before_comma.rs +3 -2
- data/src/rules/layout/space_inside_array_literal_brackets.rs +25 -10
- data/src/rules/layout/space_inside_array_percent_literal.rs +7 -1
- data/src/rules/layout/space_inside_parens.rs +170 -87
- data/src/rules/layout/space_inside_range_literal.rs +53 -19
- data/src/rules/layout/space_inside_reference_brackets.rs +65 -3
- data/src/rules/layout/space_inside_string_interpolation.rs +147 -48
- data/src/rules/layout/support.rs +172 -26
- data/src/rules/layout/tokens.rs +47 -7
- data/src/rules/layout/trailing_whitespace.rs +5 -2
- data/src/rules/lint/access_modifier.rs +50 -5
- data/src/rules/lint/ambiguity.rs +97 -7
- data/src/rules/lint/ambiguous_block_association.rs +29 -12
- data/src/rules/lint/ambiguous_operator.rs +8 -0
- data/src/rules/lint/ambiguous_operator_precedence.rs +18 -1
- data/src/rules/lint/ambiguous_range.rs +15 -1
- data/src/rules/lint/ambiguous_regexp_literal.rs +40 -1
- data/src/rules/lint/array_literal_in_regexp.rs +13 -5
- data/src/rules/lint/assignment_in_condition.rs +3 -2
- data/src/rules/lint/blocks.rs +2 -1
- data/src/rules/lint/boolean_symbol.rs +4 -4
- data/src/rules/lint/constant_definition_in_block.rs +1 -2
- data/src/rules/lint/constant_reassignment.rs +9 -13
- data/src/rules/lint/constant_resolution.rs +8 -0
- data/src/rules/lint/cop_directives.rs +28 -4
- data/src/rules/lint/debugger.rs +59 -9
- data/src/rules/lint/deprecated_open_ssl_constant.rs +1 -2
- data/src/rules/lint/disjunctive_assignment_in_constructor.rs +2 -2
- data/src/rules/lint/duplicate_branch.rs +16 -6
- data/src/rules/lint/duplicate_case_condition.rs +5 -5
- data/src/rules/lint/duplicate_hash_key.rs +8 -4
- data/src/rules/lint/duplicate_match_pattern.rs +7 -5
- data/src/rules/lint/duplicate_methods.rs +133 -54
- data/src/rules/lint/duplicate_require.rs +1 -2
- data/src/rules/lint/duplicate_set_element.rs +19 -5
- data/src/rules/lint/empty_conditional_body.rs +19 -3
- data/src/rules/lint/empty_expression.rs +16 -1
- data/src/rules/lint/empty_in_pattern.rs +3 -2
- data/src/rules/lint/empty_when.rs +4 -4
- data/src/rules/lint/ensure_return.rs +3 -2
- data/src/rules/lint/float_comparison.rs +8 -6
- data/src/rules/lint/flow.rs +8 -5
- data/src/rules/lint/format_parameter_mismatch.rs +4 -3
- data/src/rules/lint/hash_compare_by_identity.rs +5 -4
- data/src/rules/lint/heredoc_method_call_position.rs +3 -2
- data/src/rules/lint/implicit_string_concatenation.rs +33 -3
- data/src/rules/lint/ineffective_access_modifier.rs +5 -4
- data/src/rules/lint/inherit_exception.rs +4 -3
- data/src/rules/lint/literal_as_condition.rs +51 -31
- data/src/rules/lint/literal_assignment_in_condition.rs +4 -3
- data/src/rules/lint/literal_in_interpolation.rs +66 -5
- data/src/rules/lint/literals.rs +10 -8
- data/src/rules/lint/missing_cop_enable_directive.rs +95 -18
- data/src/rules/lint/mod.rs +1 -0
- data/src/rules/lint/nested_method_definition.rs +36 -11
- data/src/rules/lint/nil_receiver.rs +420 -0
- data/src/rules/lint/no_return_in_begin_end_blocks.rs +2 -2
- data/src/rules/lint/node_equality.rs +8 -5
- data/src/rules/lint/non_atomic_file_operation.rs +9 -3
- data/src/rules/lint/non_deterministic_require_order.rs +5 -3
- data/src/rules/lint/non_local_exit_from_iterator.rs +22 -9
- data/src/rules/lint/number_conversion.rs +1 -1
- data/src/rules/lint/out_of_range_regexp_ref.rs +6 -4
- data/src/rules/lint/parentheses_as_grouped_expression.rs +5 -0
- data/src/rules/lint/redundant_cop_enable_directive.rs +3 -1
- data/src/rules/lint/redundant_dir_glob_sort.rs +1 -2
- data/src/rules/lint/redundant_safe_navigation.rs +47 -22
- data/src/rules/lint/redundant_splat_expansion.rs +4 -2
- data/src/rules/lint/redundant_string_coercion.rs +3 -2
- data/src/rules/lint/redundant_type_conversion.rs +16 -4
- data/src/rules/lint/regexp.rs +1 -3
- data/src/rules/lint/regexp_source.rs +2 -2
- data/src/rules/lint/regexp_tree.rs +38 -5
- data/src/rules/lint/require_range_parentheses.rs +5 -5
- data/src/rules/lint/rescue_exception.rs +4 -3
- data/src/rules/lint/rescue_type.rs +3 -2
- data/src/rules/lint/return_in_void_context.rs +8 -3
- data/src/rules/lint/safe_navigation_chain.rs +47 -11
- data/src/rules/lint/self_assignment.rs +20 -2
- data/src/rules/lint/shadowed_argument.rs +2 -2
- data/src/rules/lint/shadowed_exception.rs +2 -2
- data/src/rules/lint/suppressed_exception.rs +21 -3
- data/src/rules/lint/suppressed_exception_in_number_conversion.rs +4 -3
- data/src/rules/lint/symbol_conversion.rs +56 -8
- data/src/rules/lint/syntax.rs +195 -5
- data/src/rules/lint/top_level_return_with_argument.rs +11 -1
- data/src/rules/lint/triple_quotes.rs +3 -2
- data/src/rules/lint/unescaped_bracket_in_regexp.rs +1 -1
- data/src/rules/lint/unexpected_block_arity.rs +23 -5
- data/src/rules/lint/unified_integer.rs +18 -12
- data/src/rules/lint/unmodified_reduce_accumulator.rs +36 -7
- data/src/rules/lint/unreachable_code.rs +4 -4
- data/src/rules/lint/unreachable_loop.rs +26 -10
- data/src/rules/lint/unreachable_pattern_branch.rs +7 -5
- data/src/rules/lint/unused_block_argument.rs +2 -2
- data/src/rules/lint/unused_method_argument.rs +1 -2
- data/src/rules/lint/useless_assignment.rs +3 -2
- data/src/rules/lint/useless_constant_scoping.rs +3 -2
- data/src/rules/lint/useless_else_without_rescue.rs +4 -3
- data/src/rules/lint/useless_rescue.rs +5 -4
- data/src/rules/lint/useless_ruby2_keywords.rs +2 -1
- data/src/rules/lint/useless_setter_call.rs +5 -4
- data/src/rules/lint/useless_times.rs +4 -3
- data/src/rules/lint/variable_force.rs +307 -79
- data/src/rules/lint/void.rs +32 -20
- data/src/rules/metrics/abc_size.rs +197 -7
- data/src/rules/metrics/block_length.rs +19 -10
- data/src/rules/metrics/block_nesting.rs +1 -2
- data/src/rules/metrics/class_length.rs +16 -4
- data/src/rules/metrics/collection_literal_length.rs +2 -1
- data/src/rules/metrics/complexity.rs +52 -14
- data/src/rules/metrics/fragments.rs +10 -14
- data/src/rules/metrics/locals.rs +35 -14
- data/src/rules/metrics/method_length.rs +14 -6
- data/src/rules/metrics/parameter_lists.rs +7 -6
- data/src/rules/metrics/perceived_complexity.rs +1 -3
- data/src/rules/metrics/support.rs +94 -4
- data/src/rules/mod.rs +525 -80
- data/src/rules/naming/ascii_identifiers.rs +3 -3
- data/src/rules/naming/binary_operator_parameter_name.rs +5 -4
- data/src/rules/naming/block_forwarding.rs +9 -8
- data/src/rules/naming/constant_name.rs +56 -18
- data/src/rules/naming/file_name.rs +174 -43
- data/src/rules/naming/method_name.rs +8 -1
- data/src/rules/naming/predicate_method.rs +42 -14
- data/src/rules/naming/predicate_prefix.rs +70 -4
- data/src/rules/naming/rescued_exceptions_variable_name.rs +14 -11
- data/src/rules/naming/support.rs +54 -295
- data/src/rules/naming/variable_name.rs +17 -4
- data/src/rules/naming/variable_number.rs +9 -10
- data/src/rules/node_ext.rs +1 -1
- data/src/rules/ruby_literal.rs +8 -3
- data/src/rules/security/compound_hash.rs +3 -2
- data/src/rules/security/open.rs +5 -4
- data/src/rules/send_node.rs +134 -0
- data/src/rules/single_line.rs +4 -4
- data/src/rules/style/access_modifier_declarations.rs +76 -22
- data/src/rules/style/accessor_grouping.rs +44 -6
- data/src/rules/style/alias.rs +4 -3
- data/src/rules/style/and_or.rs +2 -2
- data/src/rules/style/arguments_forwarding.rs +5 -5
- data/src/rules/style/array_coercion.rs +5 -5
- data/src/rules/style/array_first_last.rs +19 -4
- data/src/rules/style/array_intersect.rs +26 -8
- data/src/rules/style/array_intersect_with_single_element.rs +1 -1
- data/src/rules/style/ascii_comments.rs +2 -0
- data/src/rules/style/attr.rs +1 -1
- data/src/rules/style/auto_resource_cleanup.rs +1 -1
- data/src/rules/style/bitwise_predicate.rs +2 -2
- data/src/rules/style/block_args.rs +1 -1
- data/src/rules/style/block_comments.rs +11 -2
- data/src/rules/style/block_delimiters.rs +154 -10
- data/src/rules/style/case_equality.rs +7 -2
- data/src/rules/style/case_like_if.rs +26 -2
- data/src/rules/style/class_and_module_children.rs +162 -10
- data/src/rules/style/class_methods.rs +1 -1
- data/src/rules/style/class_methods_definitions.rs +9 -0
- data/src/rules/style/collection_compact.rs +4 -4
- data/src/rules/style/combinable_defined.rs +2 -2
- data/src/rules/style/combinable_loops.rs +9 -11
- data/src/rules/style/comments.rs +6 -3
- data/src/rules/style/comparable_clamp.rs +2 -2
- data/src/rules/style/concat_array_literals.rs +3 -3
- data/src/rules/style/conditional.rs +12 -1
- data/src/rules/style/conditional_assignment.rs +133 -4
- data/src/rules/style/constant_visibility.rs +3 -3
- data/src/rules/style/copyright.rs +64 -3
- data/src/rules/style/data_inheritance.rs +1 -1
- data/src/rules/style/def_with_parentheses.rs +1 -1
- data/src/rules/style/document_dynamic_eval_definition.rs +50 -12
- data/src/rules/style/documentation.rs +1 -1
- data/src/rules/style/each_for_simple_loop.rs +1 -1
- data/src/rules/style/each_with_object.rs +4 -4
- data/src/rules/style/empty_block_parameter.rs +1 -1
- data/src/rules/style/empty_case_condition.rs +6 -6
- data/src/rules/style/empty_class_definition.rs +1 -1
- data/src/rules/style/empty_else.rs +1 -1
- data/src/rules/style/empty_heredoc.rs +14 -1
- data/src/rules/style/empty_lambda_parameter.rs +1 -1
- data/src/rules/style/empty_literal.rs +34 -2
- data/src/rules/style/empty_method.rs +1 -1
- data/src/rules/style/empty_string_inside_interpolation.rs +1 -1
- data/src/rules/style/endless.rs +11 -3
- data/src/rules/style/env_home.rs +1 -1
- data/src/rules/style/eval_with_location.rs +6 -0
- data/src/rules/style/even_odd.rs +1 -1
- data/src/rules/style/exact_regexp_match.rs +1 -1
- data/src/rules/style/expand_path_arguments.rs +3 -3
- data/src/rules/style/explicit_block_argument.rs +3 -3
- data/src/rules/style/file_open.rs +5 -0
- data/src/rules/style/file_open_call.rs +3 -3
- data/src/rules/style/file_write.rs +1 -1
- data/src/rules/style/for.rs +1 -1
- data/src/rules/style/format_sequences.rs +6 -0
- data/src/rules/style/format_string.rs +75 -11
- data/src/rules/style/format_string_token.rs +61 -12
- data/src/rules/style/frozen_string_literal_comment.rs +62 -4
- data/src/rules/style/guard_clause.rs +8 -7
- data/src/rules/style/hash_as_last_array_item.rs +4 -4
- data/src/rules/style/hash_each_methods.rs +1 -1
- data/src/rules/style/hash_like_case.rs +8 -8
- data/src/rules/style/hash_lookup_method.rs +43 -3
- data/src/rules/style/hash_subset.rs +16 -10
- data/src/rules/style/hash_syntax.rs +609 -71
- data/src/rules/style/hash_transform.rs +20 -18
- data/src/rules/style/hash_transform_keys.rs +7 -0
- data/src/rules/style/hash_transform_values.rs +7 -0
- data/src/rules/style/identical_conditional_branches.rs +54 -5
- data/src/rules/style/if_inside_else.rs +19 -3
- data/src/rules/style/if_unless_modifier.rs +13 -14
- data/src/rules/style/if_with_boolean_literal_branches.rs +1 -1
- data/src/rules/style/if_with_semicolon.rs +7 -1
- data/src/rules/style/implicit_runtime_error.rs +7 -0
- data/src/rules/style/in_pattern_then.rs +2 -2
- data/src/rules/style/infinite_loop.rs +6 -0
- data/src/rules/style/inline_comment.rs +4 -1
- data/src/rules/style/inverse_methods.rs +12 -2
- data/src/rules/style/invertible_unless_condition.rs +12 -12
- data/src/rules/style/it_block_parameter.rs +5 -2
- data/src/rules/style/keyword_arguments_merging.rs +8 -2
- data/src/rules/style/keyword_parameters_order.rs +1 -1
- data/src/rules/style/line_end_concatenation.rs +1 -1
- data/src/rules/style/magic_comment_format.rs +14 -6
- data/src/rules/style/map_chain.rs +1 -1
- data/src/rules/style/map_compact_with_conditional_block.rs +3 -3
- data/src/rules/style/map_into_array.rs +7 -7
- data/src/rules/style/map_join.rs +3 -3
- data/src/rules/style/method_call_with_args_parentheses.rs +7 -7
- data/src/rules/style/method_call_without_args_parentheses.rs +2 -2
- data/src/rules/style/method_def_parentheses.rs +2 -3
- data/src/rules/style/min_max.rs +3 -3
- data/src/rules/style/min_max_comparison.rs +2 -2
- data/src/rules/style/missing_else.rs +1 -1
- data/src/rules/style/missing_respond_to_missing.rs +1 -1
- data/src/rules/style/mixin_grouping.rs +6 -7
- data/src/rules/style/multiline_block_chain.rs +6 -8
- data/src/rules/style/multiline_if_then.rs +1 -1
- data/src/rules/style/multiline_in_pattern_then.rs +16 -3
- data/src/rules/style/multiline_method_signature.rs +1 -1
- data/src/rules/style/multiline_when_then.rs +2 -2
- data/src/rules/style/mutable_constant.rs +31 -35
- data/src/rules/style/negated_if.rs +2 -2
- data/src/rules/style/negated_if_else_condition.rs +5 -3
- data/src/rules/style/negated_while.rs +1 -1
- data/src/rules/style/negative_array_index.rs +25 -3
- data/src/rules/style/nested_modifier.rs +1 -1
- data/src/rules/style/nested_parenthesized_calls.rs +2 -2
- data/src/rules/style/nested_ternary_operator.rs +2 -2
- data/src/rules/style/next.rs +12 -12
- data/src/rules/style/nil_lambda.rs +2 -2
- data/src/rules/style/nodes.rs +21 -0
- data/src/rules/style/non_nil_check.rs +82 -2
- data/src/rules/style/numbered_parameters.rs +4 -1
- data/src/rules/style/numbered_parameters_limit.rs +2 -2
- data/src/rules/style/numeric_literals.rs +17 -1
- data/src/rules/style/numeric_predicate.rs +25 -6
- data/src/rules/style/one_line_conditional.rs +24 -7
- data/src/rules/style/operator_method_call.rs +5 -0
- data/src/rules/style/optional_arguments.rs +1 -1
- data/src/rules/style/or_assignment.rs +1 -1
- data/src/rules/style/parallel_assignment.rs +20 -9
- data/src/rules/style/parens.rs +1 -1
- data/src/rules/style/parentheses_around_condition.rs +3 -3
- data/src/rules/style/partition_instead_of_double_select.rs +3 -3
- data/src/rules/style/percent.rs +18 -9
- data/src/rules/style/percent_q_literals.rs +40 -7
- data/src/rules/style/preferred_hash_methods.rs +1 -1
- data/src/rules/style/quoted_symbols.rs +6 -0
- data/src/rules/style/raise_args.rs +26 -20
- data/src/rules/style/random_with_offset.rs +1 -1
- data/src/rules/style/reduce_to_hash.rs +6 -6
- data/src/rules/style/redundant_argument.rs +1 -1
- data/src/rules/style/redundant_assignment.rs +5 -5
- data/src/rules/style/redundant_begin.rs +5 -3
- data/src/rules/style/redundant_capital_w.rs +3 -2
- data/src/rules/style/redundant_condition.rs +17 -8
- data/src/rules/style/redundant_current_directory_in_path.rs +2 -2
- data/src/rules/style/redundant_double_splat_hash_braces.rs +15 -5
- data/src/rules/style/redundant_exception.rs +3 -1
- data/src/rules/style/redundant_file_extension_in_require.rs +2 -2
- data/src/rules/style/redundant_format.rs +98 -20
- data/src/rules/style/redundant_freeze.rs +1 -1
- data/src/rules/style/redundant_heredoc_delimiter_quotes.rs +1 -1
- data/src/rules/style/redundant_initialize.rs +6 -3
- data/src/rules/style/redundant_interpolation.rs +8 -8
- data/src/rules/style/redundant_interpolation_unfreeze.rs +10 -3
- data/src/rules/style/redundant_line_continuation.rs +10 -7
- data/src/rules/style/redundant_parentheses.rs +96 -19
- data/src/rules/style/redundant_regexp_argument.rs +1 -1
- data/src/rules/style/redundant_regexp_character_class.rs +4 -3
- data/src/rules/style/redundant_regexp_escape.rs +3 -2
- data/src/rules/style/redundant_return.rs +1 -2
- data/src/rules/style/redundant_self.rs +28 -16
- data/src/rules/style/redundant_sort.rs +1 -1
- data/src/rules/style/redundant_sort_by.rs +1 -1
- data/src/rules/style/redundant_string_escape.rs +6 -5
- data/src/rules/style/regexp_literal.rs +3 -2
- data/src/rules/style/require_order.rs +2 -2
- data/src/rules/style/rescue_modifier.rs +9 -4
- data/src/rules/style/rescue_standard_error.rs +1 -1
- data/src/rules/style/return_nil.rs +2 -2
- data/src/rules/style/return_nil_in_predicate_method_definition.rs +2 -2
- data/src/rules/style/safe_navigation.rs +2 -2
- data/src/rules/style/safe_navigation_chain_length.rs +8 -1
- data/src/rules/style/sample.rs +3 -3
- data/src/rules/style/select_by.rs +6 -2
- data/src/rules/style/select_by_range.rs +1 -1
- data/src/rules/style/semicolon.rs +77 -4
- data/src/rules/style/signal_exception.rs +60 -3
- data/src/rules/style/single_argument_dig.rs +10 -2
- data/src/rules/style/single_line_block_params.rs +14 -4
- data/src/rules/style/single_line_do_end_block.rs +13 -12
- data/src/rules/style/single_line_methods.rs +2 -2
- data/src/rules/style/slicing_with_range.rs +2 -2
- data/src/rules/style/sole_nested_conditional.rs +4 -4
- data/src/rules/style/special_global_vars.rs +2 -2
- data/src/rules/style/stabby_lambda_parentheses.rs +1 -1
- data/src/rules/style/statement_modifier.rs +3 -2
- data/src/rules/style/static_class.rs +7 -1
- data/src/rules/style/string_concatenation.rs +12 -7
- data/src/rules/style/string_hash_keys.rs +3 -2
- data/src/rules/style/string_literals.rs +3 -3
- data/src/rules/style/string_literals_in_interpolation.rs +4 -1
- data/src/rules/style/struct_inheritance.rs +3 -3
- data/src/rules/style/super_arguments.rs +15 -7
- data/src/rules/style/super_with_args_parentheses.rs +49 -12
- data/src/rules/style/swap_values.rs +1 -1
- data/src/rules/style/symbol_array.rs +9 -2
- data/src/rules/style/symbol_proc.rs +3 -3
- data/src/rules/style/tally_method.rs +4 -4
- data/src/rules/style/ternary_parentheses.rs +30 -15
- data/src/rules/style/top_level_method_definition.rs +10 -5
- data/src/rules/style/trailing_body.rs +1 -1
- data/src/rules/style/trailing_comma.rs +206 -10
- data/src/rules/style/trailing_comma_in_arguments.rs +9 -10
- data/src/rules/style/trailing_comma_in_array_literal.rs +2 -1
- data/src/rules/style/trailing_comma_in_hash_literal.rs +2 -1
- data/src/rules/style/trailing_method_end_statement.rs +6 -1
- data/src/rules/style/trailing_underscore_variable.rs +4 -5
- data/src/rules/style/trivial_accessors.rs +2 -2
- data/src/rules/style/unless_logical_operators.rs +1 -1
- data/src/rules/style/unpack_first.rs +11 -2
- data/src/rules/style/when_then.rs +4 -4
- data/src/rules/style/while_until_do.rs +1 -1
- data/src/rules/style/while_until_modifier.rs +3 -3
- data/src/rules/style/word_array.rs +9 -7
- data/src/rules/style/yoda_condition.rs +2 -2
- data/src/rules/style/zero_length_predicate.rs +5 -0
- data/src/rules/support.rs +128 -11
- data/src/source.rs +18 -15
- metadata +5 -1
|
@@ -97,7 +97,7 @@ impl Cop<'_, '_> {
|
|
|
97
97
|
};
|
|
98
98
|
let lhs_elements = assignments(left);
|
|
99
99
|
let rhs_elements = match RHS_LISTS.contains(&rhs.kind_str()) {
|
|
100
|
-
true => super::nodes::
|
|
100
|
+
true => super::nodes::children_in(rhs, self.context),
|
|
101
101
|
false => Vec::new(),
|
|
102
102
|
};
|
|
103
103
|
|
|
@@ -122,7 +122,7 @@ impl Cop<'_, '_> {
|
|
|
122
122
|
};
|
|
123
123
|
// `contains_heredoc?`: splitting the assignment would drop the following lines into the
|
|
124
124
|
// heredoc's body.
|
|
125
|
-
if descendants(rhs)
|
|
125
|
+
if descendants(rhs, self.context)
|
|
126
126
|
.into_iter()
|
|
127
127
|
.any(|inner| inner.kind_str() == "heredoc_beginning")
|
|
128
128
|
{
|
|
@@ -177,7 +177,7 @@ impl Cop<'_, '_> {
|
|
|
177
177
|
"identifier" | "instance_variable" | "global_variable" | "class_variable"
|
|
178
178
|
| "constant" => {
|
|
179
179
|
let name = self.source(lhs);
|
|
180
|
-
descendants(rhs)
|
|
180
|
+
descendants(rhs, self.context)
|
|
181
181
|
.into_iter()
|
|
182
182
|
.any(|inner| self.reads_name(inner, name))
|
|
183
183
|
}
|
|
@@ -206,7 +206,19 @@ impl Cop<'_, '_> {
|
|
|
206
206
|
};
|
|
207
207
|
let receiver = self.source(receiver);
|
|
208
208
|
let method = self.source(method);
|
|
209
|
-
|
|
209
|
+
// `add_self_to_getters`: a bare name on the right is read as `self.name` before the
|
|
210
|
+
// dependencies are worked out, so `self.a, self.b = b, a` is the swap it looks like.
|
|
211
|
+
if receiver == "self"
|
|
212
|
+
&& descendants(rhs, self.context).into_iter().any(|inner| {
|
|
213
|
+
inner.kind_str() == "identifier"
|
|
214
|
+
&& self.source(inner) == method
|
|
215
|
+
// `(send nil? $_)`: a name the file never assigned is a call, not a variable.
|
|
216
|
+
&& !self.context.variable_analysis().is_variable_reference(inner)
|
|
217
|
+
})
|
|
218
|
+
{
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
descendants(rhs, self.context).into_iter().any(|inner| {
|
|
210
222
|
matches!(inner.kind_str(), "call" | "method_call")
|
|
211
223
|
&& inner
|
|
212
224
|
.field("receiver")
|
|
@@ -224,7 +236,7 @@ impl Cop<'_, '_> {
|
|
|
224
236
|
};
|
|
225
237
|
let object = self.source(object);
|
|
226
238
|
let arguments = index_arguments(self, lhs);
|
|
227
|
-
descendants(rhs).into_iter().any(|inner| {
|
|
239
|
+
descendants(rhs, self.context).into_iter().any(|inner| {
|
|
228
240
|
inner.kind_str() == "element_reference"
|
|
229
241
|
&& inner
|
|
230
242
|
.field("object")
|
|
@@ -329,7 +341,7 @@ impl Cop<'_, '_> {
|
|
|
329
341
|
}
|
|
330
342
|
|
|
331
343
|
fn index_arguments(cop: &Cop<'_, '_>, node: Node<'_>) -> Vec<String> {
|
|
332
|
-
let mut arguments = super::nodes::
|
|
344
|
+
let mut arguments = super::nodes::children_in(node, cop.context);
|
|
333
345
|
if node.field("object").is_some() && !arguments.is_empty() {
|
|
334
346
|
arguments.remove(0);
|
|
335
347
|
}
|
|
@@ -342,8 +354,7 @@ fn index_arguments(cop: &Cop<'_, '_>, node: Node<'_>) -> Vec<String> {
|
|
|
342
354
|
/// `modifier_range`: the keyword and the condition behind it, which becomes the head of the block.
|
|
343
355
|
fn modifier_range(modifier: Node<'_>) -> Range<usize> {
|
|
344
356
|
let mut cursor = modifier.walk();
|
|
345
|
-
let keyword = modifier
|
|
346
|
-
.children(&mut cursor)
|
|
357
|
+
let keyword = modifier.children(&mut cursor)
|
|
347
358
|
.find(|child| !child.is_named())
|
|
348
359
|
.map_or(modifier.start_byte(), |child| child.start_byte());
|
|
349
360
|
keyword..modifier.end_byte()
|
|
@@ -410,7 +421,7 @@ fn sole_statement_of_a_method(context: &RuleContext<'_>, node: Node<'_>) -> bool
|
|
|
410
421
|
}
|
|
411
422
|
rescue.parent_of(context).is_some_and(|body| {
|
|
412
423
|
body.kind_str() == "body_statement"
|
|
413
|
-
&& super::nodes::
|
|
424
|
+
&& super::nodes::children_in(body, context).len() == 1
|
|
414
425
|
&& body
|
|
415
426
|
.parent_of(context)
|
|
416
427
|
.is_some_and(|owner| matches!(owner.kind_str(), "method" | "singleton_method"))
|
data/src/rules/style/parens.rs
CHANGED
|
@@ -191,7 +191,7 @@ fn is_ternary_condition_before_question_mark(context: &RuleContext<'_>, node: No
|
|
|
191
191
|
|
|
192
192
|
/// `comment_above_close_paren_swallows_chain?`.
|
|
193
193
|
fn comment_above_close_paren(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
194
|
-
let Some(last) = super::nodes::
|
|
194
|
+
let Some(last) = super::nodes::children_in(node, context).last().copied() else {
|
|
195
195
|
return false;
|
|
196
196
|
};
|
|
197
197
|
let close = node.end_byte() - 1;
|
|
@@ -40,7 +40,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
40
40
|
if condition.kind_str() != "parenthesized_statements" {
|
|
41
41
|
continue;
|
|
42
42
|
}
|
|
43
|
-
let children = super::nodes::
|
|
43
|
+
let children = super::nodes::children_in(condition, context);
|
|
44
44
|
let Some(first) = children.first().copied() else {
|
|
45
45
|
continue;
|
|
46
46
|
};
|
|
@@ -139,7 +139,7 @@ fn parens_required(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
139
139
|
/// `safe_assignment?`: `(begin {equals_asgn? #setter_method?})`, the parenthesized assignment that
|
|
140
140
|
/// says the assignment was meant.
|
|
141
141
|
fn is_safe_assignment(context: &RuleContext<'_>, condition: Node<'_>) -> bool {
|
|
142
|
-
let children = super::nodes::
|
|
142
|
+
let children = super::nodes::children_in(condition, context);
|
|
143
143
|
let [only] = children.as_slice() else {
|
|
144
144
|
return false;
|
|
145
145
|
};
|
|
@@ -187,7 +187,7 @@ fn unwrap(context: &RuleContext<'_>, condition: Node<'_>) -> Vec<Edit> {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
fn comment_above_close_paren(context: &RuleContext<'_>, condition: Node<'_>) -> bool {
|
|
190
|
-
let Some(last) = super::nodes::
|
|
190
|
+
let Some(last) = super::nodes::children_in(condition, context).last().copied() else {
|
|
191
191
|
return false;
|
|
192
192
|
};
|
|
193
193
|
let close = condition.end_byte() - 1;
|
|
@@ -39,7 +39,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
39
39
|
if !holds_a_begin(parent) {
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
|
-
let statements: Vec<Node<'_>> = super::nodes::
|
|
42
|
+
let statements: Vec<Node<'_>> = super::nodes::children_in(parent, context);
|
|
43
43
|
for window in statements.windows(2) {
|
|
44
44
|
let [sibling_container, container] = window else {
|
|
45
45
|
continue;
|
|
@@ -278,7 +278,7 @@ fn block_matches_block_pass(block: Node<'_>, send: Node<'_>, context: &RuleConte
|
|
|
278
278
|
let Some(argument) = last_argument(send) else {
|
|
279
279
|
return false;
|
|
280
280
|
};
|
|
281
|
-
let Some(symbol) = super::nodes::
|
|
281
|
+
let Some(symbol) = super::nodes::children_in(argument, context).into_iter().next() else {
|
|
282
282
|
return false;
|
|
283
283
|
};
|
|
284
284
|
send_node::symbol_name(symbol, context) == Some(method)
|
|
@@ -287,7 +287,7 @@ fn block_matches_block_pass(block: Node<'_>, send: Node<'_>, context: &RuleConte
|
|
|
287
287
|
/// `symbol_proc_method?`: `(block _ (args (arg _name)) (send (lvar _name) $_method_name))`.
|
|
288
288
|
fn symbol_proc_method<'a>(node: Node<'_>, context: &'a RuleContext<'_>) -> Option<&'a str> {
|
|
289
289
|
let block = block_of(node)?;
|
|
290
|
-
let parameters = super::nodes::
|
|
290
|
+
let parameters = super::nodes::children_in(block.field("parameters")?, context);
|
|
291
291
|
let [only] = parameters.as_slice() else {
|
|
292
292
|
return None;
|
|
293
293
|
};
|
data/src/rules/style/percent.rs
CHANGED
|
@@ -10,6 +10,7 @@ use tree_sitter::Node;
|
|
|
10
10
|
|
|
11
11
|
use crate::rules::RuleContext;
|
|
12
12
|
use crate::rules::node_ext::NodeExt;
|
|
13
|
+
use crate::rules::send_node::named_children_iter;
|
|
13
14
|
|
|
14
15
|
/// Node kinds that can be written as a `%`-literal. A node of one of these kinds is only a percent
|
|
15
16
|
/// literal when its opening delimiter actually starts with `%`; `"a"` and `%q(a)` share a kind.
|
|
@@ -49,7 +50,7 @@ pub(super) struct PercentLiteral {
|
|
|
49
50
|
|
|
50
51
|
impl PercentLiteral {
|
|
51
52
|
pub(super) fn new(node: Node<'_>, context: &RuleContext<'_>) -> Option<Self> {
|
|
52
|
-
if node.child_count() < 2 || is_modulo_operand(node) {
|
|
53
|
+
if node.child_count() < 2 || is_modulo_operand(node, context) {
|
|
53
54
|
return None;
|
|
54
55
|
}
|
|
55
56
|
let begin = node.child(0)?;
|
|
@@ -67,13 +68,20 @@ impl PercentLiteral {
|
|
|
67
68
|
if !types_for(node.kind_str()).contains(&percent_type.as_str()) {
|
|
68
69
|
return None;
|
|
69
70
|
}
|
|
71
|
+
// **A literal closed by a newline leaves the grammar with an empty closing node.** `%\n\n`
|
|
72
|
+
// is a string whose delimiter is the line break: the break that ends it is counted into the
|
|
73
|
+
// content and the closing node sits after it with no text of its own. The delimiter is
|
|
74
|
+
// still there in the source, one character back from where the literal ends.
|
|
70
75
|
let close_text = context.source.node_text(close);
|
|
71
|
-
let
|
|
76
|
+
let close = match close_text.chars().next() {
|
|
77
|
+
Some(character) => close.start_byte()..close.start_byte() + character.len_utf8(),
|
|
78
|
+
None => node.end_byte().saturating_sub(opening.len_utf8())..node.end_byte(),
|
|
79
|
+
};
|
|
72
80
|
Some(Self {
|
|
73
81
|
percent_type,
|
|
74
82
|
opening,
|
|
75
83
|
begin: begin.byte_range(),
|
|
76
|
-
close
|
|
84
|
+
close,
|
|
77
85
|
})
|
|
78
86
|
}
|
|
79
87
|
}
|
|
@@ -91,8 +99,8 @@ pub(super) fn literal_segments<'a>(
|
|
|
91
99
|
let text = context.source.text();
|
|
92
100
|
let mut segments = Vec::new();
|
|
93
101
|
if matches!(node.kind_str(), "string_array" | "symbol_array") {
|
|
94
|
-
let
|
|
95
|
-
for element in node
|
|
102
|
+
let _cursor = node.walk();
|
|
103
|
+
for element in named_children_iter(node, context) {
|
|
96
104
|
if !holds_interpolation(element) {
|
|
97
105
|
segments.push(&text[element.byte_range()]);
|
|
98
106
|
}
|
|
@@ -102,8 +110,8 @@ pub(super) fn literal_segments<'a>(
|
|
|
102
110
|
|
|
103
111
|
// Everything else is one run of text, broken only where an interpolation replaces it with code.
|
|
104
112
|
let mut start = literal.begin.end;
|
|
105
|
-
let
|
|
106
|
-
for child in node
|
|
113
|
+
let _cursor = node.walk();
|
|
114
|
+
for child in named_children_iter(node, context) {
|
|
107
115
|
if child.kind_str() != "interpolation" {
|
|
108
116
|
continue;
|
|
109
117
|
}
|
|
@@ -120,8 +128,9 @@ pub(super) fn literal_segments<'a>(
|
|
|
120
128
|
/// is modulo and `"%s" %[x]` is a `send` whose argument is an array. tree-sitter chains the two into
|
|
121
129
|
/// one literal anyway, which would otherwise hand every such call a percent literal that upstream
|
|
122
130
|
/// never saw.
|
|
123
|
-
pub(super) fn is_modulo_operand(node: Node<'_>) -> bool {
|
|
124
|
-
|
|
131
|
+
pub(super) fn is_modulo_operand(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
132
|
+
context
|
|
133
|
+
.parent(node)
|
|
125
134
|
.is_some_and(|parent| parent.kind_str() == "chained_string")
|
|
126
135
|
&& node.prev_named_sibling().is_some()
|
|
127
136
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
use crate::diagnostic::{Edit, Offense};
|
|
4
4
|
use crate::rules::RuleContext;
|
|
5
5
|
use crate::rules::node_ext::NodeExt;
|
|
6
|
+
use crate::rules::send_node::named_children_of;
|
|
6
7
|
|
|
7
8
|
const LOWER_CASE_Q_MSG: &str = "Do not use `%Q` unless interpolation is needed. Use `%q`.";
|
|
8
9
|
const UPPER_CASE_Q_MSG: &str = "Use `%Q` instead of `%q`.";
|
|
@@ -21,7 +22,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
21
22
|
// `process(node, '%Q', '%q')` then `correct_literal_style?`.
|
|
22
23
|
if !matches!(literal.percent_type.as_str(), "%q" | "%Q")
|
|
23
24
|
|| literal.percent_type == wanted
|
|
24
|
-
|| !is_str(node)
|
|
25
|
+
|| !is_str(node, context, &literal)
|
|
25
26
|
{
|
|
26
27
|
continue;
|
|
27
28
|
}
|
|
@@ -31,7 +32,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
31
32
|
let delimiters = [literal.opening, closing_of(context, &literal)];
|
|
32
33
|
let single = super::literal::decode(body, super::literal::Quoting::Single, &delimiters);
|
|
33
34
|
let double = super::literal::decode(body, super::literal::Quoting::Double, &delimiters);
|
|
34
|
-
if single.value != double.value {
|
|
35
|
+
if single.value != double.value || (!lower_case && has_active_interpolation(body)) {
|
|
35
36
|
continue;
|
|
36
37
|
}
|
|
37
38
|
let text = context.source.node_text(node);
|
|
@@ -60,6 +61,26 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/// Whether changing a single-quoted percent literal to `%Q` would turn text into interpolation.
|
|
65
|
+
/// An even run of backslashes leaves `#` active; an odd run escapes it (and the value comparison
|
|
66
|
+
/// above separately catches the backslash whose meaning changes in that case).
|
|
67
|
+
fn has_active_interpolation(body: &str) -> bool {
|
|
68
|
+
let bytes = body.as_bytes();
|
|
69
|
+
for index in 0..bytes.len().saturating_sub(1) {
|
|
70
|
+
if bytes[index] != b'#' || !matches!(bytes[index + 1], b'{' | b'$' | b'@') {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
let mut slash = index;
|
|
74
|
+
while slash > 0 && bytes[slash - 1] == b'\\' {
|
|
75
|
+
slash -= 1;
|
|
76
|
+
}
|
|
77
|
+
if (index - slash) % 2 == 0 {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
false
|
|
82
|
+
}
|
|
83
|
+
|
|
63
84
|
fn closing_of(context: &RuleContext<'_>, literal: &super::percent::PercentLiteral) -> char {
|
|
64
85
|
context
|
|
65
86
|
.source
|
|
@@ -71,10 +92,22 @@ fn closing_of(context: &RuleContext<'_>, literal: &super::percent::PercentLitera
|
|
|
71
92
|
|
|
72
93
|
/// `node.str_type?`: a literal is a `dstr` upstream both when it interpolates and when its text
|
|
73
94
|
/// does not fit on one line.
|
|
74
|
-
fn is_str(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
fn is_str(
|
|
96
|
+
node: tree_sitter::Node<'_>,
|
|
97
|
+
context: &RuleContext<'_>,
|
|
98
|
+
literal: &super::percent::PercentLiteral,
|
|
99
|
+
) -> bool {
|
|
100
|
+
let _cursor = node.walk();
|
|
101
|
+
if named_children_of(node, context)
|
|
102
|
+
.into_iter()
|
|
78
103
|
.any(|child| child.kind_str() == "interpolation")
|
|
79
|
-
|
|
104
|
+
{
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
// **The parser cuts the contents after every line break**, so a literal written over lines is
|
|
108
|
+
// a `dstr` of one `str` per line and `on_str` never sees it. A break that closes the contents
|
|
109
|
+
// leaves a single `str`, which is why `%Q{\n}` still reaches this cop.
|
|
110
|
+
let body = context.source.slice(literal.begin.end..literal.close.start);
|
|
111
|
+
!body.strip_suffix('\n').unwrap_or(body).contains('\n')
|
|
80
112
|
}
|
|
113
|
+
|
|
@@ -14,7 +14,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
14
14
|
// `node.arguments.one?`: the predicate takes exactly the key it asks about.
|
|
15
15
|
let one_argument = node
|
|
16
16
|
.field("arguments")
|
|
17
|
-
.is_some_and(|arguments| super::nodes::
|
|
17
|
+
.is_some_and(|arguments| super::nodes::children_in(arguments, context).len() == 1);
|
|
18
18
|
if !one_argument {
|
|
19
19
|
continue;
|
|
20
20
|
}
|
|
@@ -63,6 +63,12 @@ fn symbol_kind(node: Node<'_>, context: &RuleContext<'_>) -> Option<bool> {
|
|
|
63
63
|
return None;
|
|
64
64
|
}
|
|
65
65
|
if node.kind_str() == "delimited_symbol" {
|
|
66
|
+
// A literal running over more than one line is a `dsym` too: the parser gives each line a
|
|
67
|
+
// `str` of its own and hangs them under one, so `on_sym` never sees it. A break that closes
|
|
68
|
+
// the contents leaves one `str`, and `:"two\n"` stays a `sym`.
|
|
69
|
+
if crate::rules::support::quoted_spans_lines(context.source.node_text(node)) {
|
|
70
|
+
return None;
|
|
71
|
+
}
|
|
66
72
|
// An empty `:""` is a `dsym` upstream as well -- but an empty hash key is a plain `sym`.
|
|
67
73
|
return (node.named_child_count() > 0).then_some(false);
|
|
68
74
|
}
|
|
@@ -37,15 +37,19 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
37
37
|
let written = arguments(node);
|
|
38
38
|
let range = send_range(node, context);
|
|
39
39
|
let offense = if compact {
|
|
40
|
-
|
|
40
|
+
if !reports_compact(&written) {
|
|
41
41
|
continue;
|
|
42
|
-
}
|
|
43
|
-
context
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
}
|
|
43
|
+
let offense = context.offense(
|
|
44
|
+
format!("Provide an exception object as an argument to `{method}`."),
|
|
45
|
+
range.clone(),
|
|
46
|
+
);
|
|
47
|
+
// `correction_exploded_to_compact` hands back the original source when there is more
|
|
48
|
+
// than one message argument, so the offense stands without a rewrite.
|
|
49
|
+
match exploded_to_compact(context, node, &written, &range) {
|
|
50
|
+
Some(edit) => offense.corrected_by(edit),
|
|
51
|
+
None => offense,
|
|
52
|
+
}
|
|
49
53
|
} else {
|
|
50
54
|
let Some(edit) = compact_to_exploded(context, node, &written, &range, &allowed) else {
|
|
51
55
|
continue;
|
|
@@ -115,27 +119,29 @@ fn compact_to_exploded(
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
/// `check_compact` plus `correction_exploded_to_compact`.
|
|
122
|
+
/// `check_compact`: more than one argument, unless the exception itself was handed a hash.
|
|
123
|
+
fn reports_compact(written: &[crate::rules::send_node::Argument<'_>]) -> bool {
|
|
124
|
+
if written.len() <= 1 {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
let exception = written[0].first();
|
|
128
|
+
// A call whose own first argument is a hash is `raise Klass, key: value`, which stays.
|
|
129
|
+
!(exception.kind_str() == "call"
|
|
130
|
+
&& arguments(exception)
|
|
131
|
+
.first()
|
|
132
|
+
.is_some_and(|argument| matches!(argument.first().kind_str(), "pair" | "hash")))
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
fn exploded_to_compact(
|
|
119
136
|
context: &RuleContext<'_>,
|
|
120
137
|
node: Node<'_>,
|
|
121
138
|
written: &[crate::rules::send_node::Argument<'_>],
|
|
122
139
|
range: &std::ops::Range<usize>,
|
|
123
140
|
) -> Option<Edit> {
|
|
124
|
-
if written.len()
|
|
141
|
+
if !reports_compact(written) || written.len() > 2 {
|
|
125
142
|
return None;
|
|
126
143
|
}
|
|
127
144
|
let exception = written[0].first();
|
|
128
|
-
// A call whose own first argument is a hash is `raise Klass, key: value`, which stays.
|
|
129
|
-
if exception.kind_str() == "call"
|
|
130
|
-
&& arguments(exception)
|
|
131
|
-
.first()
|
|
132
|
-
.is_some_and(|argument| matches!(argument.first().kind_str(), "pair" | "hash"))
|
|
133
|
-
{
|
|
134
|
-
return None;
|
|
135
|
-
}
|
|
136
|
-
if written.len() > 2 {
|
|
137
|
-
return None;
|
|
138
|
-
}
|
|
139
145
|
let argument = context.source.slice(written[1].range());
|
|
140
146
|
let exception_class = exception.field("receiver").map_or_else(
|
|
141
147
|
|| context.source.node_text(exception),
|
|
@@ -117,7 +117,7 @@ fn random_call(context: &RuleContext<'_>, node: Node<'_>) -> Option<Random> {
|
|
|
117
117
|
format!("{source}.rand")
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
-
let arguments = super::nodes::
|
|
120
|
+
let arguments = super::nodes::children_in(node.field("arguments")?, context);
|
|
121
121
|
let [argument] = arguments.as_slice() else {
|
|
122
122
|
return None;
|
|
123
123
|
};
|
|
@@ -95,12 +95,12 @@ struct Built<'tree> {
|
|
|
95
95
|
fn hash_builder<'tree>(
|
|
96
96
|
node: Node<'tree>,
|
|
97
97
|
block: Node<'tree>,
|
|
98
|
-
context: &RuleContext<'_>,
|
|
98
|
+
context: &'tree RuleContext<'_>,
|
|
99
99
|
locals: &LocalVariables<'_, '_>,
|
|
100
100
|
) -> Option<Built<'tree>> {
|
|
101
101
|
let folding = context.source.node_text(node.field("method")?) != "each_with_object";
|
|
102
102
|
// `(hash)`: the seed has to be an empty literal.
|
|
103
|
-
let arguments = super::nodes::
|
|
103
|
+
let arguments = super::nodes::children_in(node.field("arguments")?, context);
|
|
104
104
|
match arguments.as_slice() {
|
|
105
105
|
[only] if only.kind_str() == "hash" && only.named_child_count() == 0 => {}
|
|
106
106
|
_ => return None,
|
|
@@ -153,7 +153,7 @@ fn hash_builder<'tree>(
|
|
|
153
153
|
fn subscript_assignment<'tree>(
|
|
154
154
|
node: Node<'tree>,
|
|
155
155
|
accumulator: &str,
|
|
156
|
-
context: &RuleContext<'_>,
|
|
156
|
+
context: &'tree RuleContext<'_>,
|
|
157
157
|
) -> Option<(Node<'tree>, Node<'tree>)> {
|
|
158
158
|
if node.kind_str() != "assignment" {
|
|
159
159
|
return None;
|
|
@@ -166,7 +166,7 @@ fn subscript_assignment<'tree>(
|
|
|
166
166
|
if context.source.node_text(object) != accumulator {
|
|
167
167
|
return None;
|
|
168
168
|
}
|
|
169
|
-
let indices = super::nodes::
|
|
169
|
+
let indices = super::nodes::children_in(target, context);
|
|
170
170
|
let [_, key] = indices.as_slice() else {
|
|
171
171
|
return None;
|
|
172
172
|
};
|
|
@@ -178,7 +178,7 @@ fn reads(node: Node<'_>, name: &str, context: &RuleContext<'_>) -> bool {
|
|
|
178
178
|
if node.kind_str() == "identifier" && context.source.node_text(node) == name {
|
|
179
179
|
return true;
|
|
180
180
|
}
|
|
181
|
-
super::nodes::
|
|
181
|
+
super::nodes::children_in(node, context)
|
|
182
182
|
.into_iter()
|
|
183
183
|
.any(|child| reads(child, name, context))
|
|
184
184
|
}
|
|
@@ -200,7 +200,7 @@ fn nested_match(
|
|
|
200
200
|
{
|
|
201
201
|
return true;
|
|
202
202
|
}
|
|
203
|
-
super::nodes::
|
|
203
|
+
super::nodes::children_in(node, context)
|
|
204
204
|
.into_iter()
|
|
205
205
|
.any(|child| nested_match(child, context, locals))
|
|
206
206
|
}
|
|
@@ -53,7 +53,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
53
53
|
let Some(arguments) = node.field("arguments") else {
|
|
54
54
|
continue;
|
|
55
55
|
};
|
|
56
|
-
let written = super::nodes::
|
|
56
|
+
let written = super::nodes::children_in(arguments, context);
|
|
57
57
|
let [only] = written.as_slice() else {
|
|
58
58
|
continue;
|
|
59
59
|
};
|
|
@@ -21,9 +21,9 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
21
21
|
/// `EnsureNode#branch` -- the *ensure* clause -- so the code it guards is never looked at. A body
|
|
22
22
|
/// split by a `rescue` alone is a `rescue` node, every branch of which is followed.
|
|
23
23
|
fn check_container(context: &RuleContext<'_>, container: Node<'_>, offenses: &mut Vec<Offense>) {
|
|
24
|
-
let children = super::nodes::
|
|
24
|
+
let children = super::nodes::children_in(container, context);
|
|
25
25
|
if let Some(ensure) = children.iter().find(|child| child.kind_str() == "ensure") {
|
|
26
|
-
let statements = super::nodes::
|
|
26
|
+
let statements = super::nodes::children_in(*ensure, context);
|
|
27
27
|
check_statements(context, &statements, offenses);
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
@@ -44,7 +44,7 @@ fn check_container(context: &RuleContext<'_>, container: Node<'_>, offenses: &mu
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
"else" => {
|
|
47
|
-
let statements = super::nodes::
|
|
47
|
+
let statements = super::nodes::children_in(*clause, context);
|
|
48
48
|
check_statements(context, &statements, offenses);
|
|
49
49
|
}
|
|
50
50
|
_ => {}
|
|
@@ -131,7 +131,7 @@ fn comments_between(context: &RuleContext<'_>, assignment: Node<'_>, reference:
|
|
|
131
131
|
fn check_branch(context: &RuleContext<'_>, node: Node<'_>, offenses: &mut Vec<Offense>) {
|
|
132
132
|
match node.kind_str() {
|
|
133
133
|
"case" | "case_match" => {
|
|
134
|
-
for child in super::nodes::
|
|
134
|
+
for child in super::nodes::children_in(node, context) {
|
|
135
135
|
match child.kind_str() {
|
|
136
136
|
"when" | "in_clause" => {
|
|
137
137
|
if let Some(body) = child.field("body") {
|
|
@@ -139,7 +139,7 @@ fn check_branch(context: &RuleContext<'_>, node: Node<'_>, offenses: &mut Vec<Of
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
"else" => {
|
|
142
|
-
let statements = super::nodes::
|
|
142
|
+
let statements = super::nodes::children_in(child, context);
|
|
143
143
|
check_statements(context, &statements, offenses);
|
|
144
144
|
}
|
|
145
145
|
_ => {}
|
|
@@ -19,7 +19,9 @@ const IMPLICIT_BEGIN_VERSION: RubyVersion = RubyVersion::new(2, 5);
|
|
|
19
19
|
|
|
20
20
|
/// The clauses that make the parser put a `rescue` or an `ensure` node between the `kwbegin` and
|
|
21
21
|
/// its statements, which is what leaves it a single child.
|
|
22
|
-
|
|
22
|
+
/// `contain_rescue_or_ensure?` asks for a `rescue` or an `ensure` and **not** for an `else`: a
|
|
23
|
+
/// `begin ... else ... end` written without a `rescue` is the redundant `begin` this cop reports.
|
|
24
|
+
const CLAUSES: &[&str] = &["rescue", "ensure"];
|
|
23
25
|
|
|
24
26
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
25
27
|
// `add_offense` refuses a range it has already reported, and every handler here reports the
|
|
@@ -193,7 +195,7 @@ fn replace_begin_with_statement(
|
|
|
193
195
|
edits: &mut Vec<Edit>,
|
|
194
196
|
anchor: &mut Option<Range<usize>>,
|
|
195
197
|
) {
|
|
196
|
-
let Some(first) = super::nodes::
|
|
198
|
+
let Some(first) = super::nodes::children_in(node, context).first().copied() else {
|
|
197
199
|
return;
|
|
198
200
|
};
|
|
199
201
|
let source = context.source.node_text(first);
|
|
@@ -236,7 +238,7 @@ fn correct_modifier_form(
|
|
|
236
238
|
anchor: &mut Option<Range<usize>>,
|
|
237
239
|
) {
|
|
238
240
|
let (Some(first), Some(keyword), Some(condition)) = (
|
|
239
|
-
super::nodes::
|
|
241
|
+
super::nodes::children_in(node, context).first().copied(),
|
|
240
242
|
modifier_keyword(parent),
|
|
241
243
|
parent.field("condition"),
|
|
242
244
|
) else {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
use crate::diagnostic::{Edit, Offense};
|
|
4
4
|
use crate::rules::RuleContext;
|
|
5
5
|
use crate::rules::node_ext::NodeExt;
|
|
6
|
+
use crate::rules::send_node::named_children_iter;
|
|
6
7
|
|
|
7
8
|
const MSG: &str = "Do not use `%W` unless interpolation is needed. If not, use `%w`.";
|
|
8
9
|
|
|
@@ -16,8 +17,8 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
16
17
|
}
|
|
17
18
|
// `requires_interpolation?`: an element that interpolates, or one whose text could not be
|
|
18
19
|
// written between single quotes.
|
|
19
|
-
let
|
|
20
|
-
let interpolates = node
|
|
20
|
+
let _cursor = node.walk();
|
|
21
|
+
let interpolates = named_children_iter(node, context).any(|element| {
|
|
21
22
|
holds_interpolation(element)
|
|
22
23
|
|| super::literal::double_quotes_required(context.source.node_text(element))
|
|
23
24
|
});
|
|
@@ -5,6 +5,8 @@ use tree_sitter::Node;
|
|
|
5
5
|
use crate::diagnostic::{Edit, Offense};
|
|
6
6
|
use crate::rules::RuleContext;
|
|
7
7
|
use crate::rules::node_ext::NodeExt;
|
|
8
|
+
use crate::rules::send_node::all_children_of;
|
|
9
|
+
use crate::rules::send_node::all_children_iter;
|
|
8
10
|
|
|
9
11
|
const MSG: &str = "Use double pipes `||` instead.";
|
|
10
12
|
const REDUNDANT_CONDITION: &str = "This condition is not needed.";
|
|
@@ -63,8 +65,12 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
63
65
|
"unless" => (normalized_else.clone(), normalized_if.clone()),
|
|
64
66
|
_ => (normalized_if.clone(), normalized_else.clone()),
|
|
65
67
|
};
|
|
68
|
+
// `if_branch_is_true_type_and_else_is_not?` returns early unless the conditional was
|
|
69
|
+
// written as an `if` or a ternary: `unless x then true else false end` is outside it.
|
|
70
|
+
let is_if = node.kind_str() != "unless";
|
|
66
71
|
if !is_offense(
|
|
67
72
|
context,
|
|
73
|
+
is_if,
|
|
68
74
|
ternary,
|
|
69
75
|
condition,
|
|
70
76
|
&raw_if,
|
|
@@ -111,6 +117,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
111
117
|
#[allow(clippy::too_many_arguments)]
|
|
112
118
|
fn is_offense(
|
|
113
119
|
context: &RuleContext<'_>,
|
|
120
|
+
is_if: bool,
|
|
114
121
|
ternary: bool,
|
|
115
122
|
condition: Node<'_>,
|
|
116
123
|
raw_if: &Option<Branch<'_>>,
|
|
@@ -137,6 +144,7 @@ fn is_offense(
|
|
|
137
144
|
}
|
|
138
145
|
if !synonymous(
|
|
139
146
|
context,
|
|
147
|
+
is_if,
|
|
140
148
|
condition,
|
|
141
149
|
raw_if,
|
|
142
150
|
normalized_if,
|
|
@@ -179,6 +187,7 @@ fn is_plain_node(branch: &Branch<'_>) -> bool {
|
|
|
179
187
|
|
|
180
188
|
fn synonymous(
|
|
181
189
|
context: &RuleContext<'_>,
|
|
190
|
+
is_if: bool,
|
|
182
191
|
condition: Node<'_>,
|
|
183
192
|
raw_if: &Option<Branch<'_>>,
|
|
184
193
|
normalized_if: &Option<Branch<'_>>,
|
|
@@ -192,7 +201,7 @@ fn synonymous(
|
|
|
192
201
|
{
|
|
193
202
|
return true;
|
|
194
203
|
}
|
|
195
|
-
if if_branch_is_true(context, condition, normalized_if, normalized_else, allowed) {
|
|
204
|
+
if is_if && if_branch_is_true(context, condition, normalized_if, normalized_else, allowed) {
|
|
196
205
|
return true;
|
|
197
206
|
}
|
|
198
207
|
if assignment_pair(context, normalized_if, normalized_else).is_some()
|
|
@@ -446,8 +455,8 @@ fn branch<'tree>(node: Node<'tree>, want_consequence: bool) -> Option<Branch<'tr
|
|
|
446
455
|
|
|
447
456
|
/// `range_between(node.loc.question.begin_pos, node.loc.colon.end_pos)`.
|
|
448
457
|
fn question_to_colon(context: &RuleContext<'_>, node: Node<'_>) -> Option<Range<usize>> {
|
|
449
|
-
let
|
|
450
|
-
let children: Vec<Node<'_>> = node
|
|
458
|
+
let _cursor = node.walk();
|
|
459
|
+
let children: Vec<Node<'_>> = all_children_iter(node, context).collect();
|
|
451
460
|
let question = children
|
|
452
461
|
.iter()
|
|
453
462
|
.find(|child| context.source.node_text(**child) == "?")?;
|
|
@@ -591,16 +600,16 @@ fn stands_in_a_send(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
591
600
|
|
|
592
601
|
/// The operator token a binary expression is written with, which the grammar keeps unnamed.
|
|
593
602
|
fn binary_operator<'a>(context: &'a RuleContext<'_>, node: Node<'_>) -> Option<&'a str> {
|
|
594
|
-
let
|
|
595
|
-
let operator = node
|
|
596
|
-
.
|
|
603
|
+
let _cursor = node.walk();
|
|
604
|
+
let operator = all_children_of(node, context)
|
|
605
|
+
.into_iter()
|
|
597
606
|
.find(|child| !child.is_named() && !child.is_extra())?;
|
|
598
607
|
Some(context.source.node_text(operator))
|
|
599
608
|
}
|
|
600
609
|
|
|
601
610
|
fn writes_through_safe_navigation(context: &RuleContext<'_>, call: Node<'_>) -> bool {
|
|
602
|
-
let
|
|
603
|
-
call
|
|
611
|
+
let _cursor = call.walk();
|
|
612
|
+
all_children_iter(call, context)
|
|
604
613
|
.any(|child| !child.is_named() && context.source.node_text(child) == "&.")
|
|
605
614
|
}
|
|
606
615
|
|