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
|
@@ -17,7 +17,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
17
17
|
}
|
|
18
18
|
let Some(argument) = node
|
|
19
19
|
.field("arguments")
|
|
20
|
-
.and_then(|arguments| super::nodes::
|
|
20
|
+
.and_then(|arguments| super::nodes::children_in(arguments, context).into_iter().next())
|
|
21
21
|
else {
|
|
22
22
|
continue;
|
|
23
23
|
};
|
|
@@ -56,7 +56,7 @@ fn leading_path_content<'a>(node: Node<'_>, context: &'a RuleContext<'_>) -> Opt
|
|
|
56
56
|
if node.kind_str() != "string" {
|
|
57
57
|
return None;
|
|
58
58
|
}
|
|
59
|
-
let first = super::nodes::
|
|
59
|
+
let first = super::nodes::children_in(node, context).into_iter().next()?;
|
|
60
60
|
if first.kind_str() != "string_content" {
|
|
61
61
|
return None;
|
|
62
62
|
}
|
|
@@ -14,7 +14,15 @@ const MERGE_METHODS: &[&str] = &["merge", "merge!"];
|
|
|
14
14
|
|
|
15
15
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
16
16
|
for node in context.nodes_of("hash") {
|
|
17
|
-
|
|
17
|
+
// `node.pairs` lists the `pair` children alone -- a nested `**{…}` is a `kwsplat` and is
|
|
18
|
+
// not one of them. Counting it as a pair made every hash holding one look rocket-written
|
|
19
|
+
// and dropped the **outer** hash of `**{a: 1, **{b: 2}}` on the floor.
|
|
20
|
+
let written = super::nodes::children_in(node, context);
|
|
21
|
+
let pairs: Vec<Node<'_>> = written
|
|
22
|
+
.iter()
|
|
23
|
+
.copied()
|
|
24
|
+
.filter(|child| child.kind_str() == "pair")
|
|
25
|
+
.collect();
|
|
18
26
|
if pairs.is_empty() || pairs.iter().any(|pair| is_hash_rocket(*pair, context)) {
|
|
19
27
|
continue;
|
|
20
28
|
}
|
|
@@ -35,7 +43,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
35
43
|
offenses.push(
|
|
36
44
|
context
|
|
37
45
|
.offense(MSG, splat.byte_range())
|
|
38
|
-
.corrected_by_all(autocorrect(context, node, splat, &
|
|
46
|
+
.corrected_by_all(autocorrect(context, node, splat, &written)),
|
|
39
47
|
);
|
|
40
48
|
}
|
|
41
49
|
}
|
|
@@ -98,6 +106,8 @@ fn autocorrect(
|
|
|
98
106
|
context: &RuleContext<'_>,
|
|
99
107
|
node: Node<'_>,
|
|
100
108
|
splat: Node<'_>,
|
|
109
|
+
// Everything the braces hold, `kwsplat` children included: the braces come off around all of
|
|
110
|
+
// it, not just around the pairs.
|
|
101
111
|
pairs: &[Node<'_>],
|
|
102
112
|
) -> Vec<Edit> {
|
|
103
113
|
let mut edits = Vec::new();
|
|
@@ -157,15 +167,15 @@ fn autocorrect(
|
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
/// `select_merge_method_nodes`, in the order `each_descendant` walks them.
|
|
160
|
-
fn merge_calls<'tree>(splat: Node<'tree>, context: &RuleContext<'_>) -> Vec<Node<'tree>> {
|
|
170
|
+
fn merge_calls<'tree>(splat: Node<'tree>, context: &'tree RuleContext<'_>) -> Vec<Node<'tree>> {
|
|
161
171
|
let mut found = Vec::new();
|
|
162
172
|
let mut stack: Vec<Node<'tree>> = Vec::new();
|
|
163
|
-
crate::rules::
|
|
173
|
+
crate::rules::push_named_children_in(splat, context, &mut stack);
|
|
164
174
|
while let Some(node) = stack.pop() {
|
|
165
175
|
if node.kind_str() == "call" && mergeable(node, context) {
|
|
166
176
|
found.push(node);
|
|
167
177
|
}
|
|
168
|
-
crate::rules::
|
|
178
|
+
crate::rules::push_named_children_in(node, context, &mut stack);
|
|
169
179
|
}
|
|
170
180
|
found.sort_by_key(Node::start_byte);
|
|
171
181
|
found
|
|
@@ -117,7 +117,9 @@ fn argument_source(context: &RuleContext<'_>, message: &Argument<'_>) -> String
|
|
|
117
117
|
let literal = message.parts().len() == 1
|
|
118
118
|
&& matches!(
|
|
119
119
|
message.first().kind_str(),
|
|
120
|
-
|
|
120
|
+
// `any_str_type?` is `str`, `dstr` **and `xstr`** -- a backtick command is a string
|
|
121
|
+
// literal, so it needs no `.to_s`.
|
|
122
|
+
"string" | "chained_string" | "character" | "heredoc_beginning" | "subshell"
|
|
121
123
|
);
|
|
122
124
|
if literal {
|
|
123
125
|
text.to_owned()
|
|
@@ -21,13 +21,13 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
21
21
|
let Some(arguments) = node.field("arguments") else {
|
|
22
22
|
continue;
|
|
23
23
|
};
|
|
24
|
-
let [name] = super::nodes::
|
|
24
|
+
let [name] = super::nodes::children_in(arguments, context)[..] else {
|
|
25
25
|
continue;
|
|
26
26
|
};
|
|
27
27
|
// `$str_type?`: an interpolated or multi-line literal is a `dstr` upstream.
|
|
28
28
|
if name.kind_str() != "string"
|
|
29
29
|
|| name.start_position().row != name.end_position().row
|
|
30
|
-
|| super::nodes::
|
|
30
|
+
|| super::nodes::children_in(name, context)
|
|
31
31
|
.iter()
|
|
32
32
|
.any(|child| child.kind_str() == "interpolation")
|
|
33
33
|
{
|
|
@@ -10,6 +10,8 @@ use crate::rules::ruby_literal::{string_value, unescape};
|
|
|
10
10
|
use crate::rules::send_node::{
|
|
11
11
|
Argument, arguments, has_interpolation, heredoc_body, send_range, symbol_name,
|
|
12
12
|
};
|
|
13
|
+
use crate::rules::send_node::all_children_of;
|
|
14
|
+
use crate::rules::send_node::named_children_iter;
|
|
13
15
|
|
|
14
16
|
/// `RESTRICT_ON_SEND`.
|
|
15
17
|
const FORMAT_METHODS: &[&str] = &["format", "sprintf"];
|
|
@@ -97,8 +99,8 @@ fn static_string_value(node: Node<'_>, context: &RuleContext<'_>) -> Option<Stri
|
|
|
97
99
|
"string" if !has_interpolation(node) => Some(string_value(node, context)),
|
|
98
100
|
"string" => {
|
|
99
101
|
let mut text = String::new();
|
|
100
|
-
let
|
|
101
|
-
for child in node
|
|
102
|
+
let _cursor = node.walk();
|
|
103
|
+
for child in named_children_iter(node, context) {
|
|
102
104
|
if child.kind_str() != "interpolation" {
|
|
103
105
|
text.push_str(&string_value(node, context));
|
|
104
106
|
break;
|
|
@@ -108,8 +110,8 @@ fn static_string_value(node: Node<'_>, context: &RuleContext<'_>) -> Option<Stri
|
|
|
108
110
|
}
|
|
109
111
|
"chained_string" => {
|
|
110
112
|
let mut text = String::new();
|
|
111
|
-
let
|
|
112
|
-
for child in node
|
|
113
|
+
let _cursor = node.walk();
|
|
114
|
+
for child in named_children_iter(node, context) {
|
|
113
115
|
if child.kind_str() == "string" && !has_interpolation(child) {
|
|
114
116
|
text.push_str(&string_value(child, context));
|
|
115
117
|
}
|
|
@@ -120,8 +122,8 @@ fn static_string_value(node: Node<'_>, context: &RuleContext<'_>) -> Option<Stri
|
|
|
120
122
|
"heredoc_beginning" => {
|
|
121
123
|
let body = heredoc_body(node, context)?;
|
|
122
124
|
let mut text = String::new();
|
|
123
|
-
let
|
|
124
|
-
for child in body
|
|
125
|
+
let _cursor = body.walk();
|
|
126
|
+
for child in named_children_iter(body, context) {
|
|
125
127
|
if child.kind_str() == "heredoc_content" {
|
|
126
128
|
text.push_str(context.source.node_text(child));
|
|
127
129
|
}
|
|
@@ -148,6 +150,11 @@ fn detect_unnecessary_fields(
|
|
|
148
150
|
if template.kind_str() != "string" || has_interpolation(*template) || rest.is_empty() {
|
|
149
151
|
return;
|
|
150
152
|
}
|
|
153
|
+
// `$str`: a template written over lines is a `dstr` of one `str` per line, which the pattern
|
|
154
|
+
// does not match.
|
|
155
|
+
if crate::rules::support::quoted_spans_lines(context.source.node_text(*template)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
151
158
|
if splatted_arguments(rest) {
|
|
152
159
|
return;
|
|
153
160
|
}
|
|
@@ -204,6 +211,22 @@ fn all_fields_literal(
|
|
|
204
211
|
if unknown_variable_width(sequence, &pending, values) {
|
|
205
212
|
continue;
|
|
206
213
|
}
|
|
214
|
+
// `%999…$d`: upstream raises on an argument number no argument answers, and a raising
|
|
215
|
+
// `format` is not a redundant one. Reading the unparsable number as "no number given"
|
|
216
|
+
// instead made the cop fold the call to whatever the first argument printed as.
|
|
217
|
+
if unreadable_argument_number(sequence) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
// `format('%{y}', 2015)` raises `ArgumentError: one hash required`. A named sequence has
|
|
221
|
+
// nothing to read without a hash to read it from, so there is no value to fold to.
|
|
222
|
+
if hash.is_none()
|
|
223
|
+
&& matches!(
|
|
224
|
+
sequence.style,
|
|
225
|
+
SequenceStyle::Annotated | SequenceStyle::Template
|
|
226
|
+
)
|
|
227
|
+
{
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
207
230
|
let Some(index) = find_argument(sequence, &mut pending, hash, values) else {
|
|
208
231
|
continue;
|
|
209
232
|
};
|
|
@@ -348,11 +371,30 @@ fn as_integer(value: &Value) -> Option<i128> {
|
|
|
348
371
|
Value::Float(number) => Some(number.trunc() as i128),
|
|
349
372
|
// `Integer('0x10')` is 16: the prefixes count, which a plain parse would refuse.
|
|
350
373
|
Value::Text(text) => parse_integer(text.trim()),
|
|
374
|
+
// `Integer((5+0i))` is 5: a complex whose imaginary part is zero converts, and one whose
|
|
375
|
+
// is not raises. `Value::Complex` keeps what the literal prints as, which is where the
|
|
376
|
+
// two halves are read from.
|
|
377
|
+
Value::Complex(text) => real_part_of_complex(text),
|
|
351
378
|
Value::Computed(inner) => as_integer(inner),
|
|
352
379
|
_ => None,
|
|
353
380
|
}
|
|
354
381
|
}
|
|
355
382
|
|
|
383
|
+
/// The real part of a complex literal whose imaginary part is zero, as `Integer()` reads it.
|
|
384
|
+
fn real_part_of_complex(text: &str) -> Option<i128> {
|
|
385
|
+
let body = text.trim();
|
|
386
|
+
let imaginary = body.rfind(['+', '-']).filter(|index| *index > 0)?;
|
|
387
|
+
if !body.ends_with('i') {
|
|
388
|
+
return None;
|
|
389
|
+
}
|
|
390
|
+
let tail = &body[imaginary + 1..body.len() - 1];
|
|
391
|
+
// `(5+0i)` converts, `(5+1i)` does not.
|
|
392
|
+
if tail.parse::<f64>().ok()? != 0.0 {
|
|
393
|
+
return None;
|
|
394
|
+
}
|
|
395
|
+
parse_integer(body[..imaginary].trim())
|
|
396
|
+
}
|
|
397
|
+
|
|
356
398
|
/// `numeric?` together with `Float(value, exception: false)`.
|
|
357
399
|
fn as_float(value: &Value) -> Option<f64> {
|
|
358
400
|
match value {
|
|
@@ -360,6 +402,7 @@ fn as_float(value: &Value) -> Option<f64> {
|
|
|
360
402
|
Value::Rational(numerator, denominator) => Some(*numerator as f64 / *denominator as f64),
|
|
361
403
|
Value::Float(number) => Some(*number),
|
|
362
404
|
Value::Text(text) => text.trim().parse::<f64>().ok(),
|
|
405
|
+
Value::Complex(text) => real_part_of_complex(text).map(|number| number as f64),
|
|
363
406
|
Value::Computed(inner) => as_float(inner),
|
|
364
407
|
_ => None,
|
|
365
408
|
}
|
|
@@ -458,7 +501,7 @@ fn pair_value(context: &RuleContext<'_>, pair: Node<'_>) -> Option<(String, Valu
|
|
|
458
501
|
/// `argument_value`, for the literals this cop can reproduce.
|
|
459
502
|
fn argument_value(context: &RuleContext<'_>, node: Node<'_>) -> Option<Value> {
|
|
460
503
|
let node = match node.kind_str() {
|
|
461
|
-
"parenthesized_statements" => *super::nodes::
|
|
504
|
+
"parenthesized_statements" => *super::nodes::children_in(node, context).first()?,
|
|
462
505
|
_ => node,
|
|
463
506
|
};
|
|
464
507
|
let text = context.source.node_text(node);
|
|
@@ -490,6 +533,9 @@ fn argument_value(context: &RuleContext<'_>, node: Node<'_>) -> Option<Value> {
|
|
|
490
533
|
"delimited_symbol" if !has_interpolation(node) => {
|
|
491
534
|
symbol_name(node, context).map(|name| Value::Text(name.to_owned()))
|
|
492
535
|
}
|
|
536
|
+
// `dsym_value`: a symbol written with an interpolation formats to what its own text reads
|
|
537
|
+
// as, so `:"#{foo}"` carries the same value the string `"#{foo}"` does.
|
|
538
|
+
"delimited_symbol" => Some(Value::Dynamic(dstr_value(node, context))),
|
|
493
539
|
// `?a`, which the parser resolves into the one-character string it stands for.
|
|
494
540
|
"character" => Some(Value::Text(crate::rules::ruby_literal::character_value(
|
|
495
541
|
text,
|
|
@@ -501,7 +547,7 @@ fn argument_value(context: &RuleContext<'_>, node: Node<'_>) -> Option<Value> {
|
|
|
501
547
|
"complex" => complex_value(text).map(Value::Complex),
|
|
502
548
|
"hash" => {
|
|
503
549
|
let mut held = Vec::new();
|
|
504
|
-
for pair in super::nodes::
|
|
550
|
+
for pair in super::nodes::children_in(node, context) {
|
|
505
551
|
held.push(pair_value(context, pair)?);
|
|
506
552
|
}
|
|
507
553
|
Some(Value::Pairs(held))
|
|
@@ -541,7 +587,10 @@ fn computed_number(context: &RuleContext<'_>, node: Node<'_>) -> Option<Value> {
|
|
|
541
587
|
true => "",
|
|
542
588
|
false => "+",
|
|
543
589
|
};
|
|
544
|
-
|
|
590
|
+
// Only the `0+` real part that `complex_value` writes comes off. Trimming a further
|
|
591
|
+
// `0` turned `(5+0i)` into `5+i`, which is `5+1i` -- and `Integer()` then refused it,
|
|
592
|
+
// because a complex with a non-zero imaginary part does not convert.
|
|
593
|
+
let imaginary = imaginary.trim_start_matches("0+");
|
|
545
594
|
Some(Value::Computed(Box::new(Value::Complex(format!(
|
|
546
595
|
"{left_text}{sign}{imaginary}"
|
|
547
596
|
)))))
|
|
@@ -554,7 +603,7 @@ fn computed_number(context: &RuleContext<'_>, node: Node<'_>) -> Option<Value> {
|
|
|
554
603
|
/// interpolated contributes the source it was written as.
|
|
555
604
|
fn dstr_value(node: Node<'_>, context: &RuleContext<'_>) -> String {
|
|
556
605
|
if node.kind_str() == "chained_string" {
|
|
557
|
-
return super::nodes::
|
|
606
|
+
return super::nodes::children_in(node, context)
|
|
558
607
|
.into_iter()
|
|
559
608
|
.map(|child| dstr_value(child, context))
|
|
560
609
|
.collect();
|
|
@@ -563,8 +612,8 @@ fn dstr_value(node: Node<'_>, context: &RuleContext<'_>) -> String {
|
|
|
563
612
|
return string_value(node, context);
|
|
564
613
|
}
|
|
565
614
|
let mut value = String::new();
|
|
566
|
-
let
|
|
567
|
-
for child in node
|
|
615
|
+
let _cursor = node.walk();
|
|
616
|
+
for child in named_children_iter(node, context) {
|
|
568
617
|
match child.kind_str() {
|
|
569
618
|
"string_content" => value.push_str(context.source.node_text(child)),
|
|
570
619
|
"escape_sequence" => unescape(context.source.node_text(child), &mut value),
|
|
@@ -645,6 +694,15 @@ fn apply_format(text: &str, values: &[Value]) -> Option<String> {
|
|
|
645
694
|
}
|
|
646
695
|
false => sequence.width.parse::<isize>().ok(),
|
|
647
696
|
};
|
|
697
|
+
// `%.*f` takes its precision from an argument too, and consumes it before the value.
|
|
698
|
+
let precision = match sequence.precision.starts_with('*') {
|
|
699
|
+
true => {
|
|
700
|
+
let taken = *pending.first()?;
|
|
701
|
+
pending.remove(0);
|
|
702
|
+
Some(as_integer(values.get(taken)?)?.max(0) as usize)
|
|
703
|
+
}
|
|
704
|
+
false => None,
|
|
705
|
+
};
|
|
648
706
|
let index = match hash.is_some()
|
|
649
707
|
&& matches!(
|
|
650
708
|
sequence.style,
|
|
@@ -661,17 +719,27 @@ fn apply_format(text: &str, values: &[Value]) -> Option<String> {
|
|
|
661
719
|
},
|
|
662
720
|
};
|
|
663
721
|
let value = value_at(values, hash, sequence, index)?;
|
|
664
|
-
out.push_str(&render(sequence, value, width)?);
|
|
722
|
+
out.push_str(&render(sequence, value, width, precision)?);
|
|
665
723
|
}
|
|
666
724
|
out.push_str(text.get(cursor..)?);
|
|
667
725
|
Some(out)
|
|
668
726
|
}
|
|
669
727
|
|
|
670
728
|
/// One formatted field.
|
|
671
|
-
fn render(
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
729
|
+
fn render(
|
|
730
|
+
sequence: &Sequence,
|
|
731
|
+
value: &Value,
|
|
732
|
+
width: Option<isize>,
|
|
733
|
+
from_argument: Option<usize>,
|
|
734
|
+
) -> Option<String> {
|
|
735
|
+
let precision: Option<usize> = match from_argument {
|
|
736
|
+
Some(precision) => Some(precision),
|
|
737
|
+
None => match (sequence.has_precision, sequence.precision.is_empty()) {
|
|
738
|
+
(false, _) => None,
|
|
739
|
+
// `%.d`: the dot with no digits behind it asks for a precision of zero.
|
|
740
|
+
(true, true) => Some(0),
|
|
741
|
+
(true, false) => Some(sequence.precision.parse().ok()?),
|
|
742
|
+
},
|
|
675
743
|
};
|
|
676
744
|
let kind = match sequence.style {
|
|
677
745
|
SequenceStyle::Template => 's',
|
|
@@ -689,6 +757,11 @@ fn render(sequence: &Sequence, value: &Value, width: Option<isize>) -> Option<St
|
|
|
689
757
|
let number = as_integer(value)?;
|
|
690
758
|
let mut digits = number.unsigned_abs().to_string();
|
|
691
759
|
if let Some(precision) = precision {
|
|
760
|
+
// `format('%.d', 0)` is `''`: a precision of zero asks for no digits at all, and
|
|
761
|
+
// zero has none to keep. Padding up to the precision left the `0` in place.
|
|
762
|
+
if precision == 0 && number == 0 {
|
|
763
|
+
digits.clear();
|
|
764
|
+
}
|
|
692
765
|
while digits.len() < precision {
|
|
693
766
|
digits.insert(0, '0');
|
|
694
767
|
}
|
|
@@ -784,9 +857,9 @@ fn to_s(value: &Value) -> String {
|
|
|
784
857
|
|
|
785
858
|
/// `quote`: the result keeps the delimiters the template was written with.
|
|
786
859
|
fn quote(context: &RuleContext<'_>, template: Node<'_>, call: Node<'_>, formatted: &str) -> String {
|
|
787
|
-
let
|
|
788
|
-
let delimiters: Vec<Node<'_>> = template
|
|
789
|
-
.
|
|
860
|
+
let _cursor = template.walk();
|
|
861
|
+
let delimiters: Vec<Node<'_>> = all_children_of(template, context)
|
|
862
|
+
.into_iter()
|
|
790
863
|
.filter(|child| !child.is_named())
|
|
791
864
|
.collect();
|
|
792
865
|
let mut opening = delimiters.first().map_or("'".to_owned(), |token| {
|
|
@@ -863,3 +936,8 @@ fn report(context: &RuleContext<'_>, call: Node<'_>, replacement: String) -> Off
|
|
|
863
936
|
safe: true,
|
|
864
937
|
})
|
|
865
938
|
}
|
|
939
|
+
|
|
940
|
+
/// A `%N$` whose `N` no `usize` holds -- upstream raises rather than formatting.
|
|
941
|
+
fn unreadable_argument_number(sequence: &Sequence) -> bool {
|
|
942
|
+
sequence.flags.contains('$') && argument_number(sequence).is_none()
|
|
943
|
+
}
|
|
@@ -132,7 +132,7 @@ fn operation_produces_immutable_object(
|
|
|
132
132
|
if node.kind_str() != "parenthesized_statements" {
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
|
-
let children = super::nodes::
|
|
135
|
+
let children = super::nodes::children_in(node, context);
|
|
136
136
|
let [only] = children.as_slice() else {
|
|
137
137
|
return false;
|
|
138
138
|
};
|
|
@@ -67,7 +67,7 @@ fn needs_quotes(
|
|
|
67
67
|
let Some(body) = send_node::heredoc_body(node, context) else {
|
|
68
68
|
return true;
|
|
69
69
|
};
|
|
70
|
-
let children = super::nodes::
|
|
70
|
+
let children = super::nodes::children_in(body, context);
|
|
71
71
|
let Some(end) = children
|
|
72
72
|
.last()
|
|
73
73
|
.filter(|last| last.kind_str() == "heredoc_end")
|
|
@@ -42,7 +42,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
42
42
|
let statements = node
|
|
43
43
|
.field("body")
|
|
44
44
|
.map(|body| {
|
|
45
|
-
super::nodes::
|
|
45
|
+
super::nodes::children_in(body, context)
|
|
46
46
|
.into_iter()
|
|
47
47
|
.filter(|child| child.kind_str() != "comment")
|
|
48
48
|
.collect::<Vec<Node<'_>>>()
|
|
@@ -122,10 +122,13 @@ fn holds_comments(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
122
122
|
/// `find_end_line` for a definition.
|
|
123
123
|
fn find_end_line(node: Node<'_>, context: &RuleContext<'_>) -> usize {
|
|
124
124
|
let fallback = context.source.line_column(node.end_byte()).0;
|
|
125
|
-
|
|
125
|
+
// `node.parent` is nil at the top level upstream, and the `|| node.loc.end.line` fallback is
|
|
126
|
+
// what answers there. The grammar hands back `program` instead, which would otherwise put the
|
|
127
|
+
// end line at 1 and hide every comment inside the definition.
|
|
128
|
+
let Some(parent) = node.parent().filter(|parent| parent.kind_str() != "program") else {
|
|
126
129
|
return fallback;
|
|
127
130
|
};
|
|
128
|
-
let siblings: Vec<Node<'_>> = super::nodes::
|
|
131
|
+
let siblings: Vec<Node<'_>> = super::nodes::children_in(parent, context)
|
|
129
132
|
.into_iter()
|
|
130
133
|
.filter(|child| child.kind_str() != "comment")
|
|
131
134
|
.collect();
|
|
@@ -5,8 +5,8 @@ use tree_sitter::Node;
|
|
|
5
5
|
use crate::diagnostic::{Edit, Offense};
|
|
6
6
|
use crate::ruby_version::RubyVersion;
|
|
7
7
|
use crate::rules::RuleContext;
|
|
8
|
-
use crate::rules::send_node;
|
|
9
8
|
use crate::rules::node_ext::NodeExt;
|
|
9
|
+
use crate::rules::send_node;
|
|
10
10
|
|
|
11
11
|
const MSG: &str = "Prefer `to_s` over string interpolation.";
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ const NOT_ON_ITS_OWN: &[&str] = &["chained_string", "string_array", "symbol_arra
|
|
|
20
20
|
|
|
21
21
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
22
22
|
for node in context.nodes_of("string") {
|
|
23
|
-
let children = super::nodes::
|
|
23
|
+
let children = super::nodes::children_in(node, context);
|
|
24
24
|
let [interpolation] = children.as_slice() else {
|
|
25
25
|
continue;
|
|
26
26
|
};
|
|
@@ -39,12 +39,14 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
39
39
|
if is_symbol_key(context, node) {
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
|
-
let embedded = super::nodes::
|
|
43
|
-
// `use_match_pattern?`: `"#{x => y}"` binds a name rather than producing one.
|
|
42
|
+
let embedded = super::nodes::children_in(*interpolation, context);
|
|
43
|
+
// `use_match_pattern?`: `"#{x => y}"` binds a name rather than producing one. The test it
|
|
44
|
+
// runs is `match_pattern_type?`, which `x in y` fails -- that is a `match_pattern_p`, and
|
|
45
|
+
// `"#{42 in var}"` is reported like any other interpolation.
|
|
44
46
|
if context.target_ruby_version() > RubyVersion::new(2, 7)
|
|
45
47
|
&& embedded
|
|
46
48
|
.iter()
|
|
47
|
-
.any(|child|
|
|
49
|
+
.any(|child| child.kind_str() == "match_pattern")
|
|
48
50
|
{
|
|
49
51
|
continue;
|
|
50
52
|
}
|
|
@@ -162,9 +164,7 @@ fn is_symbol_key(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
162
164
|
return false;
|
|
163
165
|
};
|
|
164
166
|
parent.kind_str() == "pair"
|
|
165
|
-
&& parent
|
|
166
|
-
.field("key")
|
|
167
|
-
.is_some_and(|key| key.id() == node.id())
|
|
167
|
+
&& parent.field("key").is_some_and(|key| key.id() == node.id())
|
|
168
168
|
&& parent
|
|
169
169
|
.child(1)
|
|
170
170
|
.is_some_and(|separator| context.source.node_text(separator) == ":")
|
|
@@ -47,7 +47,7 @@ fn interpolates(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
47
47
|
} else {
|
|
48
48
|
node
|
|
49
49
|
};
|
|
50
|
-
super::nodes::
|
|
50
|
+
super::nodes::children_in(literal, context)
|
|
51
51
|
.iter()
|
|
52
52
|
.any(|child| match child.kind_str() {
|
|
53
53
|
"interpolation" => true,
|
|
@@ -83,7 +83,14 @@ fn unfreezing(
|
|
|
83
83
|
return None;
|
|
84
84
|
}
|
|
85
85
|
let selector = parent.field("method")?;
|
|
86
|
-
|
|
86
|
+
// `:+@` written as an ordinary call (`"...".+@`) reaches the same matcher as the
|
|
87
|
+
// unary form; the grammar only spells the operator one as `unary`.
|
|
88
|
+
if !matches!(context.source.node_text(selector), "dup" | "+@") {
|
|
89
|
+
return None;
|
|
90
|
+
}
|
|
91
|
+
// `(send dstr_type? {:+@ :dup})` takes no arguments: `"…".dup(baz)` is a different
|
|
92
|
+
// call and upstream leaves it alone.
|
|
93
|
+
if !crate::rules::send_node::arguments(parent).is_empty() {
|
|
87
94
|
return None;
|
|
88
95
|
}
|
|
89
96
|
Some((
|
|
@@ -106,7 +113,7 @@ fn unfreezing(
|
|
|
106
113
|
if context.source.node_text(selector) != "new" {
|
|
107
114
|
return None;
|
|
108
115
|
}
|
|
109
|
-
match super::nodes::
|
|
116
|
+
match super::nodes::children_in(parent, context).as_slice() {
|
|
110
117
|
[only] if only.id() == node.id() => {}
|
|
111
118
|
_ => return None,
|
|
112
119
|
}
|
|
@@ -6,8 +6,9 @@ use tree_sitter::Node;
|
|
|
6
6
|
|
|
7
7
|
use crate::diagnostic::{Edit, Offense};
|
|
8
8
|
use crate::rules::RuleContext;
|
|
9
|
-
use crate::rules::layout::tokens::{TokenKind, tokens};
|
|
9
|
+
use crate::rules::layout::tokens::{Token, TokenKind, tokens};
|
|
10
10
|
use crate::rules::node_ext::NodeExt;
|
|
11
|
+
use crate::rules::send_node::named_children_of;
|
|
11
12
|
|
|
12
13
|
const MSG: &str = "Redundant line continuation.";
|
|
13
14
|
|
|
@@ -66,10 +67,12 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
66
67
|
/// continuation written after the last of them.
|
|
67
68
|
fn ast_range(context: &RuleContext<'_>) -> Option<Range<usize>> {
|
|
68
69
|
let root = context.root_node();
|
|
69
|
-
let
|
|
70
|
-
let statements: Vec<Node<'_>> = root
|
|
71
|
-
.
|
|
72
|
-
|
|
70
|
+
let _cursor = root.walk();
|
|
71
|
+
let statements: Vec<Node<'_>> = named_children_of(root, context)
|
|
72
|
+
.into_iter()
|
|
73
|
+
// `__END__` and what follows it is `DATA`, not code: upstream's AST stops at the keyword,
|
|
74
|
+
// so a `\` down there is out of the range the candidates are searched in.
|
|
75
|
+
.filter(|child| !matches!(child.kind_str(), "comment" | "uninterpreted"))
|
|
73
76
|
.collect();
|
|
74
77
|
let (first, last) = (statements.first()?, statements.last()?);
|
|
75
78
|
Some(first.start_byte()..last.end_byte())
|
|
@@ -102,7 +105,7 @@ fn within_comment(context: &RuleContext<'_>, range: &Range<usize>) -> bool {
|
|
|
102
105
|
/// `implicit_string_concatenation?`: two string literals written one under the other, which the
|
|
103
106
|
/// continuation is what joins.
|
|
104
107
|
fn implicit_string_concatenation(context: &RuleContext<'_>, range: &Range<usize>) -> bool {
|
|
105
|
-
let stream = tokens(context);
|
|
108
|
+
let stream: &[Token] = tokens(context);
|
|
106
109
|
let line = context.source.line_column(range.start).0;
|
|
107
110
|
let before = stream.iter().rfind(|token| token.range.end <= range.start);
|
|
108
111
|
let ends = before.is_some_and(|token| {
|
|
@@ -131,7 +134,7 @@ fn string_content_ranges(context: &RuleContext<'_>) -> Vec<Range<usize>> {
|
|
|
131
134
|
let mut stack = vec![context.root_node()];
|
|
132
135
|
while let Some(node) = stack.pop() {
|
|
133
136
|
if !LITERALS.contains(&node.kind_str()) {
|
|
134
|
-
crate::rules::
|
|
137
|
+
crate::rules::push_named_children_in(node, context, &mut stack);
|
|
135
138
|
continue;
|
|
136
139
|
}
|
|
137
140
|
// What an interpolation holds is code again, so it is cut out of the literal and walked.
|