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
data/src/rules/style/sample.rs
CHANGED
|
@@ -79,7 +79,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
79
79
|
/// `(call $_ ${:first :last :[] :at :slice} $...)`: the receiver, the selector's name and the
|
|
80
80
|
/// arguments of a call that takes one element out of a collection.
|
|
81
81
|
fn indexing<'tree>(
|
|
82
|
-
context: &RuleContext<'_>,
|
|
82
|
+
context: &'tree RuleContext<'_>,
|
|
83
83
|
node: Node<'tree>,
|
|
84
84
|
) -> Option<(Node<'tree>, &'static str, Vec<Argument<'tree>>)> {
|
|
85
85
|
if node.kind_str() == "element_reference" {
|
|
@@ -101,7 +101,7 @@ fn indexing<'tree>(
|
|
|
101
101
|
};
|
|
102
102
|
let written = node
|
|
103
103
|
.field("arguments")
|
|
104
|
-
.map(|list| super::nodes::
|
|
104
|
+
.map(|list| super::nodes::children_in(list, context))
|
|
105
105
|
.unwrap_or_default();
|
|
106
106
|
Some((receiver, method, group(&written)))
|
|
107
107
|
}
|
|
@@ -128,7 +128,7 @@ fn shuffle_call(
|
|
|
128
128
|
}
|
|
129
129
|
let written = node
|
|
130
130
|
.field("arguments")
|
|
131
|
-
.map(|list| super::nodes::
|
|
131
|
+
.map(|list| super::nodes::children_in(list, context))
|
|
132
132
|
.unwrap_or_default();
|
|
133
133
|
let first = group(&written)
|
|
134
134
|
.first()
|
|
@@ -107,6 +107,10 @@ fn receiver_allowed(receiver: Option<Node<'_>>, context: &RuleContext<'_>) -> bo
|
|
|
107
107
|
};
|
|
108
108
|
match receiver.kind_str() {
|
|
109
109
|
"hash" => true,
|
|
110
|
+
// `(call _ {:to_h :to_hash} ...)` matches a receiverless call too -- `to_h.reject { … }`
|
|
111
|
+
// is `(send (send nil :to_h) :reject)`. The grammar writes a bare name as an `identifier`,
|
|
112
|
+
// which fell through to "not a hash".
|
|
113
|
+
"identifier" => matches!(context.source.node_text(receiver), "to_h" | "to_hash"),
|
|
110
114
|
// `env_const?`: `(const {nil? cbase} :ENV)`.
|
|
111
115
|
"constant" => context.source.node_text(receiver) == "ENV",
|
|
112
116
|
"scope_resolution" => {
|
|
@@ -150,7 +154,7 @@ fn is_hash_constant(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
150
154
|
/// an `itblock` reading `it`.
|
|
151
155
|
fn block_argument(context: &RuleContext<'_>, block: Node<'_>) -> Option<String> {
|
|
152
156
|
if let Some(parameters) = block.field("parameters") {
|
|
153
|
-
let declared = super::nodes::
|
|
157
|
+
let declared = super::nodes::children_in(parameters, context);
|
|
154
158
|
let [only] = declared.as_slice() else {
|
|
155
159
|
return None;
|
|
156
160
|
};
|
|
@@ -170,7 +174,7 @@ fn block_argument(context: &RuleContext<'_>, block: Node<'_>) -> Option<String>
|
|
|
170
174
|
/// How many numbered parameters the body reads, and whether it reads `it`. A nested block's names
|
|
171
175
|
/// belong to that block, not to this one.
|
|
172
176
|
fn scan_implicit(context: &RuleContext<'_>, node: Node<'_>, numbered: &mut usize, it: &mut bool) {
|
|
173
|
-
for child in super::nodes::
|
|
177
|
+
for child in super::nodes::children_in(node, context) {
|
|
174
178
|
if matches!(child.kind_str(), "block" | "do_block" | "lambda") {
|
|
175
179
|
continue;
|
|
176
180
|
}
|
|
@@ -140,7 +140,7 @@ fn find_range(statement: Node<'_>, context: &RuleContext<'_>) -> Option<String>
|
|
|
140
140
|
}
|
|
141
141
|
let receiver = inner.field("receiver")?;
|
|
142
142
|
let receiver = match receiver.kind_str() {
|
|
143
|
-
"parenthesized_statements" => super::nodes::
|
|
143
|
+
"parenthesized_statements" => super::nodes::children_in(receiver, context).first().copied()?,
|
|
144
144
|
_ => receiver,
|
|
145
145
|
};
|
|
146
146
|
Some(context.source.node_text(receiver).to_owned())
|
|
@@ -2,9 +2,10 @@ use std::collections::HashSet;
|
|
|
2
2
|
|
|
3
3
|
use crate::diagnostic::{Edit, Offense};
|
|
4
4
|
use crate::rules::RuleContext;
|
|
5
|
+
use crate::rules::node_ext::NodeExt;
|
|
5
6
|
use crate::rules::support::Interpolations;
|
|
6
7
|
use crate::source::is_protected;
|
|
7
|
-
use crate::rules::
|
|
8
|
+
use crate::rules::send_node::named_children_iter;
|
|
8
9
|
|
|
9
10
|
/// Node kinds whose named children are a sequence of statements.
|
|
10
11
|
///
|
|
@@ -39,7 +40,6 @@ const NON_STATEMENT_KINDS: &[&str] = &["comment", "empty_statement", "rescue", "
|
|
|
39
40
|
/// walks a token stream rather than the text, so none of these is a semicolon to it.
|
|
40
41
|
const SEMICOLON_BEARING_TOKENS: &[&str] = &["global_variable", "character", "delimited_symbol"];
|
|
41
42
|
|
|
42
|
-
|
|
43
43
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
44
44
|
let text = context.source.text();
|
|
45
45
|
if !text.contains(';') {
|
|
@@ -113,6 +113,23 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
113
113
|
// `corrector.wrap(node, '(', ')') if node`: an endless range whose `;` goes away would
|
|
114
114
|
// reach on to the next line and swallow it (`42..;\n42...;` becomes one range). Upstream
|
|
115
115
|
// puts parentheses around the range first.
|
|
116
|
+
// `token_before_semicolon&.type == :tLABEL`: `m key:;` has to keep reading as an argument
|
|
117
|
+
// list once the semicolon goes, so the hash gains parentheses and the space in front of it
|
|
118
|
+
// is removed. Without it `m key:` is a label standing on its own.
|
|
119
|
+
if let Some((space, arguments)) = value_omission_before(context, offset) {
|
|
120
|
+
offenses.push(offense.corrected_by_all([
|
|
121
|
+
Edit {
|
|
122
|
+
start: space.start,
|
|
123
|
+
end: space.end,
|
|
124
|
+
replacement: String::new(),
|
|
125
|
+
safe: true,
|
|
126
|
+
},
|
|
127
|
+
insert(arguments.start, "("),
|
|
128
|
+
insert(arguments.end, ")"),
|
|
129
|
+
removal,
|
|
130
|
+
]));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
116
133
|
if let Some(range) = endless_range_before(context, offset) {
|
|
117
134
|
offenses.push(offense.corrected_by_all([
|
|
118
135
|
insert(range.start, "("),
|
|
@@ -207,6 +224,12 @@ fn line_terminator_or_opener(
|
|
|
207
224
|
if after_last == "}" {
|
|
208
225
|
return Some(last);
|
|
209
226
|
}
|
|
227
|
+
// `exist_semicolon_before_right_string_interpolation_brace?`: the `}` closing an interpolation
|
|
228
|
+
// is a `tSTRING_DEND`, and the string it sits in goes on past it -- so the line does not end
|
|
229
|
+
// there, but the semicolon still terminates the last expression of the interpolation.
|
|
230
|
+
if closes_an_interpolation(context, last) {
|
|
231
|
+
return Some(last);
|
|
232
|
+
}
|
|
210
233
|
let before_first = text[code.start..first].trim_end();
|
|
211
234
|
if before_first.ends_with('{') {
|
|
212
235
|
return Some(first);
|
|
@@ -214,6 +237,17 @@ fn line_terminator_or_opener(
|
|
|
214
237
|
None
|
|
215
238
|
}
|
|
216
239
|
|
|
240
|
+
/// Whether the first thing after the semicolon is the brace that closes an interpolation.
|
|
241
|
+
fn closes_an_interpolation(context: &RuleContext<'_>, semicolon: usize) -> bool {
|
|
242
|
+
let text = context.source.text();
|
|
243
|
+
let rest = &text[semicolon + 1..];
|
|
244
|
+
let brace = semicolon + 1 + (rest.len() - rest.trim_start().len());
|
|
245
|
+
text[brace..].starts_with('}')
|
|
246
|
+
&& context
|
|
247
|
+
.nodes_of("interpolation")
|
|
248
|
+
.any(|node| node.end_byte() == brace + 1)
|
|
249
|
+
}
|
|
250
|
+
|
|
217
251
|
/// The byte range of `line_number` with comments and surrounding whitespace removed.
|
|
218
252
|
fn code_range(context: &RuleContext<'_>, line_number: usize) -> Option<std::ops::Range<usize>> {
|
|
219
253
|
let text = context.source.text();
|
|
@@ -235,8 +269,8 @@ fn code_range(context: &RuleContext<'_>, line_number: usize) -> Option<std::ops:
|
|
|
235
269
|
fn expression_separator_lines(context: &RuleContext<'_>) -> HashSet<usize> {
|
|
236
270
|
let mut lines = HashSet::new();
|
|
237
271
|
for node in context.nodes_of_any(STATEMENT_SEQUENCE_KINDS) {
|
|
238
|
-
let
|
|
239
|
-
let children: Vec<_> = node
|
|
272
|
+
let _cursor = node.walk();
|
|
273
|
+
let children: Vec<_> = named_children_iter(node, context).collect();
|
|
240
274
|
// `begin ... end` holds its statements directly upstream rather than wrapping them in the
|
|
241
275
|
// `begin` node the cop looks for, so `begin a; b end` is not an offense. Add a `rescue` and
|
|
242
276
|
// the protected body becomes such a node after all.
|
|
@@ -295,3 +329,42 @@ fn heredoc_opened_before(
|
|
|
295
329
|
.iter()
|
|
296
330
|
.any(|(line, end)| *line == line_number && *end <= offset)
|
|
297
331
|
}
|
|
332
|
+
|
|
333
|
+
/// The space to drop and the span to bracket when the semicolon follows a value-omitted label.
|
|
334
|
+
///
|
|
335
|
+
/// `value_omission_pair_nodes` upstream; the pair's grandparent is the call whose selector the
|
|
336
|
+
/// space belongs to.
|
|
337
|
+
fn value_omission_before(
|
|
338
|
+
context: &RuleContext<'_>,
|
|
339
|
+
offset: usize,
|
|
340
|
+
) -> Option<(std::ops::Range<usize>, std::ops::Range<usize>)> {
|
|
341
|
+
let before = context.source.text()[..offset].trim_end().len();
|
|
342
|
+
for pair in context.nodes_of("pair") {
|
|
343
|
+
if pair.field("value").is_some() || pair.end_byte() != before {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
let list = pair.parent()?;
|
|
347
|
+
if list.kind_str() != "argument_list" {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
let call = list.parent()?;
|
|
351
|
+
if call.kind_str() != "call" {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
let selector = call.field("method")?;
|
|
355
|
+
// A call already written with brackets needs neither edit.
|
|
356
|
+
if list
|
|
357
|
+
.child(0)
|
|
358
|
+
.is_some_and(|first| context.source.node_text(first) == "(")
|
|
359
|
+
{
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
let _cursor = list.walk();
|
|
363
|
+
let first = named_children_iter(list, context).next()?;
|
|
364
|
+
return Some((
|
|
365
|
+
selector.end_byte()..first.start_byte(),
|
|
366
|
+
first.start_byte()..list.end_byte(),
|
|
367
|
+
));
|
|
368
|
+
}
|
|
369
|
+
None
|
|
370
|
+
}
|
|
@@ -3,18 +3,20 @@ use tree_sitter::Node;
|
|
|
3
3
|
use crate::diagnostic::{Edit, Offense};
|
|
4
4
|
use crate::rules::RuleContext;
|
|
5
5
|
use crate::rules::lint::locals::LocalVariables;
|
|
6
|
-
use crate::rules::send_node::{is_plain_send, top_level_constant};
|
|
7
6
|
use crate::rules::node_ext::NodeExt;
|
|
7
|
+
use crate::rules::send_node::{is_plain_send, top_level_constant};
|
|
8
|
+
|
|
9
|
+
const FAIL_MSG: &str = "Use `fail` instead of `raise` to signal exceptions.";
|
|
10
|
+
const RAISE_MSG: &str = "Use `raise` instead of `fail` to rethrow exceptions.";
|
|
8
11
|
|
|
9
12
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
10
13
|
let style = context
|
|
11
14
|
.setting::<String>("EnforcedStyle")
|
|
12
15
|
.unwrap_or_else(|| "only_raise".to_owned());
|
|
13
|
-
// `:semantic` decides per rescue clause which of the two words belongs where; it is not the
|
|
14
|
-
// default and is left to the pass that ports `on_rescue`.
|
|
15
16
|
let (looked_for, preferred) = match style.as_str() {
|
|
16
17
|
"only_raise" => ("fail", "raise"),
|
|
17
18
|
"only_fail" => ("raise", "fail"),
|
|
19
|
+
"semantic" => return check_semantic(context, offenses),
|
|
18
20
|
_ => return,
|
|
19
21
|
};
|
|
20
22
|
// `custom_fail_defined?`: a file that defines its own `fail` is not talking about `Kernel#fail`.
|
|
@@ -45,6 +47,61 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
/// `:semantic`: `raise` belongs inside a rescue clause and `fail` everywhere else.
|
|
51
|
+
///
|
|
52
|
+
/// Upstream reaches the same split from two directions -- `on_rescue` walks the body for `raise`
|
|
53
|
+
/// and each `resbody` for `fail`, while `on_send` catches every remaining `raise` -- with
|
|
54
|
+
/// `ignore_node` keeping the two from reporting the same call twice. Asking each call where it
|
|
55
|
+
/// sits gives the same set without the bookkeeping: a call under a `rescue` clause's body may say
|
|
56
|
+
/// `raise`, and one anywhere else may say `fail`.
|
|
57
|
+
fn check_semantic(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
58
|
+
let locals = LocalVariables::new(context);
|
|
59
|
+
for node in context.nodes_of_any(&["call", "identifier"]) {
|
|
60
|
+
if !is_plain_send(node, context) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
let rescuing = inside_rescue_body(node);
|
|
64
|
+
let looked_for = match rescuing {
|
|
65
|
+
true => "fail",
|
|
66
|
+
false => "raise",
|
|
67
|
+
};
|
|
68
|
+
let Some(selector) = signal_selector(node, looked_for, context, &locals) else {
|
|
69
|
+
continue;
|
|
70
|
+
};
|
|
71
|
+
let (message, preferred) = match rescuing {
|
|
72
|
+
true => (RAISE_MSG, "raise"),
|
|
73
|
+
false => (FAIL_MSG, "fail"),
|
|
74
|
+
};
|
|
75
|
+
offenses.push(
|
|
76
|
+
context
|
|
77
|
+
.offense(message, selector.byte_range())
|
|
78
|
+
.corrected_by(Edit {
|
|
79
|
+
start: selector.start_byte(),
|
|
80
|
+
end: selector.end_byte(),
|
|
81
|
+
replacement: preferred.to_owned(),
|
|
82
|
+
safe: true,
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Whether the call sits in the body of a `rescue` clause -- not in the exception list or the
|
|
89
|
+
/// `=> e` binding, which are part of the same node but not part of the clause's body.
|
|
90
|
+
fn inside_rescue_body(node: Node<'_>) -> bool {
|
|
91
|
+
let mut current = node;
|
|
92
|
+
while let Some(parent) = current.parent() {
|
|
93
|
+
if parent.kind_str() == "rescue"
|
|
94
|
+
&& parent.field("body").is_some_and(|body| {
|
|
95
|
+
body.start_byte() <= node.start_byte() && node.end_byte() <= body.end_byte()
|
|
96
|
+
})
|
|
97
|
+
{
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
current = parent;
|
|
101
|
+
}
|
|
102
|
+
false
|
|
103
|
+
}
|
|
104
|
+
|
|
48
105
|
/// `command_or_kernel_call?`: the `raise` / `fail` token of a receiverless call or of one made
|
|
49
106
|
/// through `Kernel`.
|
|
50
107
|
fn signal_selector<'tree>(
|
|
@@ -107,8 +107,16 @@ fn is_dig(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
107
107
|
let Some(arguments) = node.field("arguments") else {
|
|
108
108
|
return false;
|
|
109
109
|
};
|
|
110
|
-
let arguments = super::nodes::
|
|
111
|
-
!
|
|
110
|
+
let arguments = super::nodes::children_in(arguments, context);
|
|
111
|
+
// `(call _ :dig !{hash block_pass}+)`: at least one argument that is neither a hash nor a
|
|
112
|
+
// block pass. The grammar spells a brace-less hash as its own pairs and splats, and an
|
|
113
|
+
// anonymous `**` as a lone `hash_splat_argument` -- all of them one `hash` upstream.
|
|
114
|
+
arguments.iter().any(|argument| {
|
|
115
|
+
!matches!(
|
|
116
|
+
argument.kind_str(),
|
|
117
|
+
"hash" | "pair" | "hash_splat_argument" | "block_argument"
|
|
118
|
+
)
|
|
119
|
+
})
|
|
112
120
|
&& arguments
|
|
113
121
|
.iter()
|
|
114
122
|
.all(|argument| !matches!(argument.kind_str(), "hash" | "pair" | "block_argument"))
|
|
@@ -6,6 +6,7 @@ use crate::diagnostic::{Edit, Offense};
|
|
|
6
6
|
use crate::rules::RuleContext;
|
|
7
7
|
use crate::rules::lint::locals::LocalVariables;
|
|
8
8
|
use crate::rules::node_ext::NodeExt;
|
|
9
|
+
use crate::rules::send_node::all_children_of;
|
|
9
10
|
|
|
10
11
|
/// One entry of `Methods`, which upstream writes as a list of single-key hashes.
|
|
11
12
|
type Methods = Vec<HashMap<String, Vec<String>>>;
|
|
@@ -43,11 +44,20 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
43
44
|
let Some(list) = block.field("parameters") else {
|
|
44
45
|
continue;
|
|
45
46
|
};
|
|
46
|
-
// `eligible_arguments?`: every parameter has to be a plain one.
|
|
47
|
-
|
|
47
|
+
// `eligible_arguments?`: every parameter has to be a plain one. A block-local written
|
|
48
|
+
// after a `;` is a `shadowarg` upstream, which fails `arg_type?` -- the grammar spells it
|
|
49
|
+
// as another identifier in the same list and marks it only by the separator.
|
|
50
|
+
let written = super::nodes::children_in(list, context);
|
|
48
51
|
if written.is_empty() || written.iter().any(|arg| arg.kind_str() != "identifier") {
|
|
49
52
|
continue;
|
|
50
53
|
}
|
|
54
|
+
let _cursor = list.walk();
|
|
55
|
+
if all_children_of(list, context)
|
|
56
|
+
.into_iter()
|
|
57
|
+
.any(|child| !child.is_named() && child.kind_str() == ";")
|
|
58
|
+
{
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
51
61
|
let names: Vec<&str> = written
|
|
52
62
|
.iter()
|
|
53
63
|
.map(|arg| context.source.node_text(*arg))
|
|
@@ -116,12 +126,12 @@ fn rename_reads(
|
|
|
116
126
|
locals: &LocalVariables<'_, '_>,
|
|
117
127
|
edits: &mut Vec<Edit>,
|
|
118
128
|
) {
|
|
119
|
-
let mut stack: Vec<Node<'_>> = super::nodes::
|
|
129
|
+
let mut stack: Vec<Node<'_>> = super::nodes::children_in(node, context)
|
|
120
130
|
.into_iter()
|
|
121
131
|
.filter(|child| child.kind_str() != "block_parameters")
|
|
122
132
|
.collect();
|
|
123
133
|
while let Some(current) = stack.pop() {
|
|
124
|
-
stack.extend(super::nodes::
|
|
134
|
+
stack.extend(super::nodes::children_in(current, context));
|
|
125
135
|
if current.kind_str() != "identifier" || !locals.is_lvar(current) {
|
|
126
136
|
continue;
|
|
127
137
|
}
|
|
@@ -6,6 +6,8 @@ use crate::diagnostic::{Edit, Offense};
|
|
|
6
6
|
use crate::rules::RuleContext;
|
|
7
7
|
use crate::rules::node_ext::NodeExt;
|
|
8
8
|
use crate::rules::send_node::heredoc_body;
|
|
9
|
+
use crate::rules::send_node::named_children_of;
|
|
10
|
+
use crate::rules::send_node::all_children_of;
|
|
9
11
|
|
|
10
12
|
const MSG: &str = "Prefer multiline `do`...`end` block.";
|
|
11
13
|
|
|
@@ -82,14 +84,14 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
82
84
|
/// any and after the `do` when it does not.
|
|
83
85
|
fn do_line(context: &RuleContext<'_>, block: Node<'_>) -> Option<usize> {
|
|
84
86
|
if let Some(parameters) = block.field("parameters")
|
|
85
|
-
&& !super::nodes::
|
|
87
|
+
&& !super::nodes::children_in(parameters, context).is_empty()
|
|
86
88
|
{
|
|
87
89
|
return Some(parameters.end_byte());
|
|
88
90
|
}
|
|
89
91
|
let _ = context;
|
|
90
|
-
let
|
|
91
|
-
block
|
|
92
|
-
.
|
|
92
|
+
let _cursor = block.walk();
|
|
93
|
+
all_children_of(block, context)
|
|
94
|
+
.into_iter()
|
|
93
95
|
.find(|child| child.kind_str() == "do")
|
|
94
96
|
.map(|token| token.end_byte())
|
|
95
97
|
}
|
|
@@ -97,8 +99,7 @@ fn do_line(context: &RuleContext<'_>, block: Node<'_>) -> Option<usize> {
|
|
|
97
99
|
/// `node.loc.end`.
|
|
98
100
|
fn end_keyword<'tree>(block: Node<'tree>) -> Option<Node<'tree>> {
|
|
99
101
|
let mut cursor = block.walk();
|
|
100
|
-
block
|
|
101
|
-
.children(&mut cursor)
|
|
102
|
+
block.children(&mut cursor)
|
|
102
103
|
.find(|child| child.kind_str() == "end")
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -110,9 +111,9 @@ fn trailing_heredoc(context: &RuleContext<'_>, block: Node<'_>) -> Option<usize>
|
|
|
110
111
|
if node.kind_str() == "heredoc_beginning"
|
|
111
112
|
&& let Some(body) = heredoc_body(node, context)
|
|
112
113
|
{
|
|
113
|
-
let
|
|
114
|
-
if let Some(terminator) = body
|
|
115
|
-
.
|
|
114
|
+
let _cursor = body.walk();
|
|
115
|
+
if let Some(terminator) = named_children_of(body, context)
|
|
116
|
+
.into_iter()
|
|
116
117
|
.find(|child| child.kind_str() == "heredoc_end")
|
|
117
118
|
{
|
|
118
119
|
last = Some(last.map_or(terminator.end_byte(), |end: usize| {
|
|
@@ -120,7 +121,7 @@ fn trailing_heredoc(context: &RuleContext<'_>, block: Node<'_>) -> Option<usize>
|
|
|
120
121
|
}));
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
|
-
crate::rules::
|
|
124
|
+
crate::rules::push_named_children_in(node, context, &mut stack);
|
|
124
125
|
}
|
|
125
126
|
last
|
|
126
127
|
}
|
|
@@ -200,7 +201,7 @@ fn comment_within(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
200
201
|
/// `safe_to_split?`.
|
|
201
202
|
fn safe_to_split(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
202
203
|
let mut stack: Vec<Node<'_>> = Vec::new();
|
|
203
|
-
crate::rules::
|
|
204
|
+
crate::rules::push_named_children_in(node, context, &mut stack);
|
|
204
205
|
while let Some(child) = stack.pop() {
|
|
205
206
|
if UNSAFE_TO_SPLIT.contains(&child.kind_str()) {
|
|
206
207
|
return false;
|
|
@@ -220,7 +221,7 @@ fn safe_to_split(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
220
221
|
{
|
|
221
222
|
return false;
|
|
222
223
|
}
|
|
223
|
-
crate::rules::
|
|
224
|
+
crate::rules::push_named_children_in(child, context, &mut stack);
|
|
224
225
|
}
|
|
225
226
|
true
|
|
226
227
|
}
|
|
@@ -164,7 +164,7 @@ fn endless_correction(context: &RuleContext<'_>, node: Node<'_>, body: &Body) ->
|
|
|
164
164
|
.map_or("", |name| context.source.node_text(name));
|
|
165
165
|
let arguments = node.field("parameters").map_or_else(
|
|
166
166
|
|| "()".to_owned(),
|
|
167
|
-
|list| match super::nodes::
|
|
167
|
+
|list| match super::nodes::children_in(list, context).is_empty() {
|
|
168
168
|
true => "()".to_owned(),
|
|
169
169
|
false => context.source.node_text(list).to_owned(),
|
|
170
170
|
},
|
|
@@ -198,7 +198,7 @@ fn method_body_source(context: &RuleContext<'_>, node: Node<'_>, body: &Body) ->
|
|
|
198
198
|
let (receiver, name, arguments) = match expression.kind_str() {
|
|
199
199
|
"call" => {
|
|
200
200
|
let list = expression.field("arguments")?;
|
|
201
|
-
let arguments = super::nodes::
|
|
201
|
+
let arguments = super::nodes::children_in(list, context);
|
|
202
202
|
if arguments.is_empty() {
|
|
203
203
|
return None;
|
|
204
204
|
}
|
|
@@ -62,7 +62,7 @@ struct Slice<'tree> {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
impl<'tree> Slice<'tree> {
|
|
65
|
-
fn read(node: Node<'tree>, context: &RuleContext<'_>) -> Option<Self> {
|
|
65
|
+
fn read(node: Node<'tree>, context: &'tree RuleContext<'_>) -> Option<Self> {
|
|
66
66
|
match node.kind_str() {
|
|
67
67
|
"element_reference" => {
|
|
68
68
|
// `ary[0..-1] = x` is a call to `:[]=`, which the cop does not restrict itself to.
|
|
@@ -70,7 +70,7 @@ impl<'tree> Slice<'tree> {
|
|
|
70
70
|
if is_assignment_target(node) {
|
|
71
71
|
return None;
|
|
72
72
|
}
|
|
73
|
-
let children = super::nodes::
|
|
73
|
+
let children = super::nodes::children_in(node, context);
|
|
74
74
|
let [_, argument] = children.as_slice() else {
|
|
75
75
|
return None;
|
|
76
76
|
};
|
|
@@ -118,7 +118,7 @@ fn use_variable_assignment_in_condition(
|
|
|
118
118
|
return false;
|
|
119
119
|
};
|
|
120
120
|
let inner_source = context.source.node_text(inner);
|
|
121
|
-
super::conditional::descendants(condition)
|
|
121
|
+
super::conditional::descendants(condition, context)
|
|
122
122
|
.into_iter()
|
|
123
123
|
.filter(|descendant| descendant.kind_str() == "assignment")
|
|
124
124
|
.filter_map(|assignment| assignment.field("left"))
|
|
@@ -399,7 +399,7 @@ fn add_parentheses(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
399
399
|
/// `assignment_in_and?`.
|
|
400
400
|
fn assignment_in_and(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
401
401
|
is_and(context, node)
|
|
402
|
-
&& super::conditional::descendants(node)
|
|
402
|
+
&& super::conditional::descendants(node, context)
|
|
403
403
|
.into_iter()
|
|
404
404
|
.skip(1)
|
|
405
405
|
.any(|descendant| matches!(descendant.kind_str(), "assignment" | "operator_assignment"))
|
|
@@ -413,7 +413,7 @@ fn call_shape(context: &RuleContext<'_>, node: Node<'_>) -> Option<(bool, bool,
|
|
|
413
413
|
let arguments = node.field("arguments");
|
|
414
414
|
let parenthesized =
|
|
415
415
|
arguments.is_some_and(|list| context.source.node_text(list).starts_with('('));
|
|
416
|
-
let any = arguments.is_some_and(|list| !super::nodes::
|
|
416
|
+
let any = arguments.is_some_and(|list| !super::nodes::children_in(list, context).is_empty());
|
|
417
417
|
Some((any, parenthesized, false))
|
|
418
418
|
}
|
|
419
419
|
// `a[0]` is `(send a :[] 0)`: an argument, and no parentheses around it.
|
|
@@ -469,7 +469,7 @@ fn parenthesized_method_arguments(context: &RuleContext<'_>, node: Node<'_>) ->
|
|
|
469
469
|
return context.source.node_text(node).to_owned();
|
|
470
470
|
};
|
|
471
471
|
let call = context.source.slice(node.start_byte()..selector.end_byte());
|
|
472
|
-
let first = super::nodes::
|
|
472
|
+
let first = super::nodes::children_in(arguments, context)
|
|
473
473
|
.first()
|
|
474
474
|
.map_or(arguments.start_byte(), Node::start_byte);
|
|
475
475
|
let rest = context.source.slice(first..node.end_byte());
|
|
@@ -204,9 +204,9 @@ fn is_lone_begin(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
204
204
|
// `"#$!"` interpolates without a `begin`: upstream hangs the global off the `dstr` itself.
|
|
205
205
|
"interpolation" => {
|
|
206
206
|
context.source.node_text(node).starts_with("#{")
|
|
207
|
-
&& super::nodes::
|
|
207
|
+
&& super::nodes::children_in(node, context).len() == 1
|
|
208
208
|
}
|
|
209
|
-
"parenthesized_statements" => super::nodes::
|
|
209
|
+
"parenthesized_statements" => super::nodes::children_in(node, context).len() == 1,
|
|
210
210
|
_ => false,
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -17,7 +17,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
17
17
|
let Some(parameters) = node.field("parameters") else {
|
|
18
18
|
continue;
|
|
19
19
|
};
|
|
20
|
-
if super::nodes::
|
|
20
|
+
if super::nodes::children_in(parameters, context).is_empty() {
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
23
23
|
let text = context.source.node_text(parameters);
|
|
@@ -7,12 +7,13 @@ use tree_sitter::Node;
|
|
|
7
7
|
|
|
8
8
|
use super::conditional::descendants;
|
|
9
9
|
use super::nodes;
|
|
10
|
+
use crate::rules::RuleContext;
|
|
10
11
|
use crate::rules::node_ext::NodeExt;
|
|
11
12
|
|
|
12
13
|
/// `condition.each_node.any?(&:lvasgn_type?)`: a condition that binds a local cannot move behind
|
|
13
14
|
/// the body that reads it.
|
|
14
|
-
pub(super) fn non_eligible_condition(condition: Node<'_>) -> bool {
|
|
15
|
-
descendants(condition).into_iter().any(|node| {
|
|
15
|
+
pub(super) fn non_eligible_condition(condition: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
16
|
+
descendants(condition, context).into_iter().any(|node| {
|
|
16
17
|
matches!(node.kind_str(), "assignment" | "operator_assignment")
|
|
17
18
|
&& node.field("left").is_some_and(binds_local)
|
|
18
19
|
})
|
|
@@ -111,6 +111,12 @@ fn convertible(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
111
111
|
"call" => node
|
|
112
112
|
.field("method")
|
|
113
113
|
.is_some_and(|selector| context.source.node_text(selector) == "extend"),
|
|
114
|
+
// A receiverless call with no arguments is a bare name to the grammar and a `send`
|
|
115
|
+
// upstream, which `extend_call?` answers to just the same.
|
|
116
|
+
"identifier" => {
|
|
117
|
+
context.source.node_text(node) == "extend"
|
|
118
|
+
&& !context.variable_analysis().is_variable_reference(node)
|
|
119
|
+
}
|
|
114
120
|
_ => false,
|
|
115
121
|
}
|
|
116
122
|
}
|
|
@@ -135,7 +141,7 @@ fn is_public(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
135
141
|
let Some(parent) = node.parent() else {
|
|
136
142
|
return true;
|
|
137
143
|
};
|
|
138
|
-
let siblings = super::nodes::
|
|
144
|
+
let siblings = super::nodes::children_in(parent, context);
|
|
139
145
|
let Some(position) = siblings.iter().position(|child| child.id() == node.id()) else {
|
|
140
146
|
return true;
|
|
141
147
|
};
|
|
@@ -10,6 +10,8 @@ use crate::rules::send_node::is_plain_send;
|
|
|
10
10
|
|
|
11
11
|
use super::literal::{self, Quoting};
|
|
12
12
|
use crate::rules::node_ext::NodeExt;
|
|
13
|
+
use crate::rules::send_node::all_children_of;
|
|
14
|
+
use crate::rules::send_node::all_children_iter;
|
|
13
15
|
|
|
14
16
|
const MSG: &str = "Prefer string interpolation to string concatenation.";
|
|
15
17
|
|
|
@@ -240,7 +242,7 @@ fn adjust_str(context: &RuleContext<'_>, node: Node<'_>, encoding: LiteralEncodi
|
|
|
240
242
|
},
|
|
241
243
|
// `'a' 'b'`, `(...)` and `#{...}` are all a list of children upstream.
|
|
242
244
|
"chained_string" | "parenthesized_statements" | "interpolation" => {
|
|
243
|
-
super::nodes::
|
|
245
|
+
super::nodes::children_in(node, context)
|
|
244
246
|
.into_iter()
|
|
245
247
|
.map(|child| adjust_str(context, child, encoding))
|
|
246
248
|
.collect()
|
|
@@ -322,11 +324,11 @@ struct Interpolated<'tree> {
|
|
|
322
324
|
/// The children upstream's `dstr` holds, or `None` when the literal has no interpolation and is a
|
|
323
325
|
/// plain `str`.
|
|
324
326
|
fn interpolated<'tree>(
|
|
325
|
-
context: &RuleContext<'_>,
|
|
327
|
+
context: &'tree RuleContext<'_>,
|
|
326
328
|
node: Node<'tree>,
|
|
327
329
|
) -> Option<Interpolated<'tree>> {
|
|
328
|
-
let
|
|
329
|
-
let children: Vec<Node<'tree>> = node
|
|
330
|
+
let _cursor = node.walk();
|
|
331
|
+
let children: Vec<Node<'tree>> = all_children_iter(node, context).collect();
|
|
330
332
|
if !children.iter().any(|child| child.kind_str() == "interpolation") {
|
|
331
333
|
return None;
|
|
332
334
|
}
|
|
@@ -424,6 +426,9 @@ impl Heredocs {
|
|
|
424
426
|
"string" => interpolated(context, node).is_none() && written_on_one_line(context, node),
|
|
425
427
|
"character" => true,
|
|
426
428
|
"heredoc_beginning" => self.plain.get(&node.id()).copied().unwrap_or(false),
|
|
429
|
+
// The parser resolves `__FILE__` to the path it stands for, which is a `str`. The
|
|
430
|
+
// grammar leaves it an identifier.
|
|
431
|
+
"identifier" => context.source.node_text(node) == "__FILE__",
|
|
427
432
|
_ => false,
|
|
428
433
|
}
|
|
429
434
|
}
|
|
@@ -472,9 +477,9 @@ fn body_is_str(context: &RuleContext<'_>, body: Node<'_>) -> bool {
|
|
|
472
477
|
if has_interpolation_descendant(body) {
|
|
473
478
|
return false;
|
|
474
479
|
}
|
|
475
|
-
let
|
|
476
|
-
let end = body
|
|
477
|
-
.
|
|
480
|
+
let _cursor = body.walk();
|
|
481
|
+
let end = all_children_of(body, context)
|
|
482
|
+
.into_iter()
|
|
478
483
|
.find(|child| child.kind_str() == "heredoc_end")
|
|
479
484
|
.map_or(body.end_byte(), |terminator| terminator.start_byte());
|
|
480
485
|
let text = &context.source.text()[body.start_byte()..end];
|
|
@@ -5,6 +5,7 @@ use crate::rules::RuleContext;
|
|
|
5
5
|
use crate::rules::node_ext::NodeExt;
|
|
6
6
|
use crate::rules::ruby_literal;
|
|
7
7
|
use crate::rules::send_node;
|
|
8
|
+
use crate::rules::send_node::named_children_iter;
|
|
8
9
|
|
|
9
10
|
const MSG: &str = "Prefer symbols instead of strings as hash keys.";
|
|
10
11
|
|
|
@@ -62,8 +63,8 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
62
63
|
/// for the replacement resolves such an escape to a character, which loses the distinction.
|
|
63
64
|
fn is_valid_encoding(key: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
64
65
|
let mut bytes: Vec<u8> = Vec::new();
|
|
65
|
-
let
|
|
66
|
-
for child in key
|
|
66
|
+
let _cursor = key.walk();
|
|
67
|
+
for child in named_children_iter(key, context) {
|
|
67
68
|
let text = context.source.node_text(child);
|
|
68
69
|
match raw_byte(text).filter(|_| child.kind_str() == "escape_sequence") {
|
|
69
70
|
Some(byte) => bytes.push(byte),
|