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
|
@@ -7,6 +7,7 @@ use crate::rules::lint::locals::LocalVariables;
|
|
|
7
7
|
|
|
8
8
|
use super::conditional::{UpstreamParent, upstream_parent};
|
|
9
9
|
use crate::rules::node_ext::NodeExt;
|
|
10
|
+
use crate::rules::send_node::all_children_iter;
|
|
10
11
|
|
|
11
12
|
/// `ALLOWED_NODE_TYPES`: the parents whose child keeps its parentheses around a logical operator.
|
|
12
13
|
/// `send` covers every shape the grammar spells as a call.
|
|
@@ -73,6 +74,31 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
73
74
|
}
|
|
74
75
|
check_group(context, &locals, node, &mut pending);
|
|
75
76
|
}
|
|
77
|
+
// **`^(var)` is a `pin` holding a `begin` upstream**, so the parentheses are a node the walk
|
|
78
|
+
// above would find. The grammar writes them as part of the pin itself, which leaves
|
|
79
|
+
// `allowed_pin_operator?` -- `^(pin (begin !{lvar ivar cvar gvar}))` -- as the only thing to
|
|
80
|
+
// say about them: a pinned variable is reported, anything else is left alone.
|
|
81
|
+
for node in context.nodes_of("expression_reference_pattern") {
|
|
82
|
+
let Some(value) = node.field("value") else {
|
|
83
|
+
continue;
|
|
84
|
+
};
|
|
85
|
+
if !is_variable(context, &locals, value) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
let Some(range) = pinned_parentheses(node, context) else {
|
|
89
|
+
continue;
|
|
90
|
+
};
|
|
91
|
+
offenses.push(
|
|
92
|
+
context
|
|
93
|
+
.offense("Don't use parentheses around a variable.", range.clone())
|
|
94
|
+
.corrected_by(crate::diagnostic::Edit {
|
|
95
|
+
start: range.start,
|
|
96
|
+
end: range.end,
|
|
97
|
+
replacement: context.source.node_text(value).to_owned(),
|
|
98
|
+
safe: true,
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
76
102
|
// `on_investigation_end`: each candidate's exact correction is verified by reparsing before the
|
|
77
103
|
// offense is registered, so redundancy never rests on a hand-kept list of the grammar's rules.
|
|
78
104
|
let verified = crate::rules::support::verified_by_reparse(
|
|
@@ -168,7 +194,7 @@ fn parent_child_count(context: &RuleContext<'_>, node: Node<'_>) -> usize {
|
|
|
168
194
|
match parent {
|
|
169
195
|
UpstreamParent::Begin(parent) => match parent.kind_str() {
|
|
170
196
|
"parenthesized_statements" | "interpolation" | "begin" => {
|
|
171
|
-
super::nodes::
|
|
197
|
+
super::nodes::children_in(parent, context).len()
|
|
172
198
|
}
|
|
173
199
|
// A statement list the grammar wraps -- a `then`, an `else`, a definition's body --
|
|
174
200
|
// is a `begin` of the statements it holds, less the clauses that are not statements
|
|
@@ -177,7 +203,7 @@ fn parent_child_count(context: &RuleContext<'_>, node: Node<'_>) -> usize {
|
|
|
177
203
|
},
|
|
178
204
|
UpstreamParent::Node(parent) => match parent.kind_str() {
|
|
179
205
|
"array" | "string_array" | "symbol_array" | "exceptions" => {
|
|
180
|
-
super::nodes::
|
|
206
|
+
super::nodes::children_in(parent, context).len()
|
|
181
207
|
}
|
|
182
208
|
"splat_argument"
|
|
183
209
|
| "hash_splat_argument"
|
|
@@ -187,10 +213,10 @@ fn parent_child_count(context: &RuleContext<'_>, node: Node<'_>) -> usize {
|
|
|
187
213
|
| "begin_block"
|
|
188
214
|
| "end_block" => 1,
|
|
189
215
|
kind if JUMPS.contains(&kind) => {
|
|
190
|
-
super::nodes::
|
|
216
|
+
super::nodes::children_in(parent, context)
|
|
191
217
|
.first()
|
|
192
218
|
.map_or(0, |list| match list.kind_str() {
|
|
193
|
-
"argument_list" => super::nodes::
|
|
219
|
+
"argument_list" => super::nodes::children_in(*list, context).len(),
|
|
194
220
|
_ => 1,
|
|
195
221
|
})
|
|
196
222
|
}
|
|
@@ -203,14 +229,34 @@ fn parent_child_count(context: &RuleContext<'_>, node: Node<'_>) -> usize {
|
|
|
203
229
|
true => 1,
|
|
204
230
|
false => 2,
|
|
205
231
|
},
|
|
232
|
+
// **`children.one?` counts what is there, and a missing clause is `nil`.** `if (x)\nend`
|
|
233
|
+
// reaches upstream as `(if (begin …) nil nil)`, whose `one?` is true because the two
|
|
234
|
+
// absent branches are falsy -- so the parentheses are redundant. Reading the keyword
|
|
235
|
+
// and the clauses the grammar writes as children instead made the count 2 and the
|
|
236
|
+
// offense disappear.
|
|
237
|
+
kind if CONDITIONALS.contains(&kind) => {
|
|
238
|
+
["condition", "consequence", "alternative", "body"]
|
|
239
|
+
.iter()
|
|
240
|
+
.filter(|field| {
|
|
241
|
+
// A `while` always carries a `do`, empty or not, and an empty one is the
|
|
242
|
+
// `nil` body upstream holds.
|
|
243
|
+
parent
|
|
244
|
+
.field(field)
|
|
245
|
+
.is_some_and(|clause| clause.named_child_count() > 0)
|
|
246
|
+
})
|
|
247
|
+
.count()
|
|
248
|
+
}
|
|
206
249
|
_ => 2,
|
|
207
250
|
},
|
|
208
251
|
}
|
|
209
252
|
}
|
|
210
253
|
|
|
254
|
+
/// The keywords whose absent clauses upstream spells as `nil` children.
|
|
255
|
+
const CONDITIONALS: &[&str] = &["if", "unless", "while", "until", "elsif"];
|
|
256
|
+
|
|
211
257
|
/// `parens_allowed?`.
|
|
212
258
|
fn parens_allowed(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
213
|
-
let children = super::nodes::
|
|
259
|
+
let children = super::nodes::children_in(node, context);
|
|
214
260
|
// `empty_parentheses?`: `()` says something no rewrite could keep.
|
|
215
261
|
if children.is_empty() {
|
|
216
262
|
return true;
|
|
@@ -251,11 +297,10 @@ fn in_pattern_matching_in_method_argument(
|
|
|
251
297
|
let Some(first) = children.first() else {
|
|
252
298
|
return false;
|
|
253
299
|
};
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
300
|
+
// Upstream names the node differently per version -- 2.7 builds `match_pattern` for `in`, and
|
|
301
|
+
// 3.0 renamed that to `match_pattern_p` while giving `=>` the freed-up name. **Either way the
|
|
302
|
+
// question is about `in`**, which the grammar always spells `test_pattern`.
|
|
303
|
+
first.kind_str() == "test_pattern"
|
|
259
304
|
}
|
|
260
305
|
|
|
261
306
|
/// `allowed_pin_operator?`: `^(pin (begin !{lvar ivar cvar gvar}))`.
|
|
@@ -422,7 +467,7 @@ fn like_method_argument_parentheses(
|
|
|
422
467
|
let Some(arguments) = argument_list(parent) else {
|
|
423
468
|
return false;
|
|
424
469
|
};
|
|
425
|
-
let list = super::nodes::
|
|
470
|
+
let list = super::nodes::children_in(arguments, context);
|
|
426
471
|
if list.len() != 1 || list[0].id() != node.id() {
|
|
427
472
|
return false;
|
|
428
473
|
}
|
|
@@ -476,12 +521,12 @@ fn multiline_control_flow_statements(
|
|
|
476
521
|
|
|
477
522
|
/// `check`.
|
|
478
523
|
fn check_group<'tree>(
|
|
479
|
-
context: &RuleContext<'_>,
|
|
524
|
+
context: &'tree RuleContext<'_>,
|
|
480
525
|
locals: &LocalVariables<'_, '_>,
|
|
481
526
|
node: Node<'tree>,
|
|
482
527
|
pending: &mut Vec<(Node<'tree>, &'static str)>,
|
|
483
528
|
) {
|
|
484
|
-
let children = super::nodes::
|
|
529
|
+
let children = super::nodes::children_in(node, context);
|
|
485
530
|
let Some(inner) = children.first().copied() else {
|
|
486
531
|
return;
|
|
487
532
|
};
|
|
@@ -604,6 +649,23 @@ fn is_block(node: Node<'_>) -> bool {
|
|
|
604
649
|
|
|
605
650
|
/// `node.variable?`: an instance, class or global variable, or a bare name the parser resolved
|
|
606
651
|
/// into a local variable read.
|
|
652
|
+
/// The `(` … `)` a pin operator wrote around its value.
|
|
653
|
+
fn pinned_parentheses(
|
|
654
|
+
node: Node<'_>,
|
|
655
|
+
context: &RuleContext<'_>,
|
|
656
|
+
) -> Option<std::ops::Range<usize>> {
|
|
657
|
+
let _cursor = node.walk();
|
|
658
|
+
let children: Vec<Node<'_>> = all_children_iter(node, context).collect();
|
|
659
|
+
let open = children
|
|
660
|
+
.iter()
|
|
661
|
+
.find(|child| !child.is_named() && context.source.node_text(**child) == "(")?;
|
|
662
|
+
let close = children
|
|
663
|
+
.iter()
|
|
664
|
+
.rev()
|
|
665
|
+
.find(|child| !child.is_named() && context.source.node_text(**child) == ")")?;
|
|
666
|
+
Some(open.start_byte()..close.end_byte())
|
|
667
|
+
}
|
|
668
|
+
|
|
607
669
|
fn is_variable(context: &RuleContext<'_>, locals: &LocalVariables<'_, '_>, node: Node<'_>) -> bool {
|
|
608
670
|
match node.kind_str() {
|
|
609
671
|
"instance_variable" | "class_variable" | "global_variable" => true,
|
|
@@ -947,7 +1009,7 @@ fn body_range(context: &RuleContext<'_>, node: Node<'_>, inner: Node<'_>) -> boo
|
|
|
947
1009
|
return false;
|
|
948
1010
|
};
|
|
949
1011
|
let statements = match parent.kind_str() {
|
|
950
|
-
"parenthesized_statements" | "interpolation" | "begin" => super::nodes::
|
|
1012
|
+
"parenthesized_statements" | "interpolation" | "begin" => super::nodes::children_in(parent, context),
|
|
951
1013
|
_ => super::conditional::self_statements(parent),
|
|
952
1014
|
};
|
|
953
1015
|
let beginless = inner.field("begin").is_none()
|
|
@@ -995,7 +1057,7 @@ fn keyword_with_redundant_parentheses(context: &RuleContext<'_>, node: Node<'_>)
|
|
|
995
1057
|
None => has_own_parentheses(context, node),
|
|
996
1058
|
};
|
|
997
1059
|
}
|
|
998
|
-
_ => super::nodes::
|
|
1060
|
+
_ => super::nodes::children_in(node, context),
|
|
999
1061
|
};
|
|
1000
1062
|
// `only_begin_arg?`: the keyword's single argument is itself a parenthesized group.
|
|
1001
1063
|
if let [only] = arguments.as_slice()
|
|
@@ -1031,6 +1093,9 @@ fn call_has_arguments(node: Node<'_>) -> bool {
|
|
|
1031
1093
|
// A setter call takes the assigned value, and an indexing setter its subscripts too.
|
|
1032
1094
|
"binary" | "assignment" => true,
|
|
1033
1095
|
"element_reference" => !super::nodes::children(node).is_empty(),
|
|
1096
|
+
// A `defined?` node's child **is** its argument upstream; the grammar writes it as a
|
|
1097
|
+
// `unary` with an operand and no argument list, so it read as taking nothing.
|
|
1098
|
+
"unary" => node.field("operand").is_some(),
|
|
1034
1099
|
_ => argument_list(node).is_some_and(|list| !super::nodes::children(list).is_empty()),
|
|
1035
1100
|
}
|
|
1036
1101
|
}
|
|
@@ -1114,7 +1179,19 @@ fn is_prefix_not(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
1114
1179
|
}
|
|
1115
1180
|
|
|
1116
1181
|
fn is_suspect_unary(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
1117
|
-
|
|
1182
|
+
// `node.send_type? && ...`: `defined? arg` is a `defined?` node upstream, not a `send`, so the
|
|
1183
|
+
// walk down through the operators stops in front of it. Stepping past it handed
|
|
1184
|
+
// `method_call_with_redundant_parentheses?` the bare argument, and `foo && (!defined? arg)`
|
|
1185
|
+
// came out as redundant parentheses around a unary operation.
|
|
1186
|
+
!is_defined(context, node) && is_unary_operation(context, node) && !is_prefix_not(context, node)
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/// `defined? x`, which the grammar spells as a `unary` and upstream as a node type of its own.
|
|
1190
|
+
fn is_defined(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
1191
|
+
node.kind_str() == "unary"
|
|
1192
|
+
&& node
|
|
1193
|
+
.field("operator")
|
|
1194
|
+
.is_some_and(|operator| context.source.node_text(operator) == "defined?")
|
|
1118
1195
|
}
|
|
1119
1196
|
|
|
1120
1197
|
/// `method_call_with_redundant_parentheses?`.
|
|
@@ -1137,7 +1214,7 @@ fn method_call_with_redundant_parentheses(
|
|
|
1137
1214
|
if singular_parenthesized_parent(context, node) {
|
|
1138
1215
|
return true;
|
|
1139
1216
|
}
|
|
1140
|
-
!call_has_arguments(call) || has_own_parentheses(context, call) || square_brackets(call)
|
|
1217
|
+
!call_has_arguments(call) || has_own_parentheses(context, call) || square_brackets(call, context)
|
|
1141
1218
|
}
|
|
1142
1219
|
|
|
1143
1220
|
/// `singular_parenthesized_parent?`.
|
|
@@ -1154,14 +1231,14 @@ fn singular_parenthesized_parent(context: &RuleContext<'_>, node: Node<'_>) -> b
|
|
|
1154
1231
|
}
|
|
1155
1232
|
|
|
1156
1233
|
/// `square_brackets?`: an index written on something the parentheses cannot be part of.
|
|
1157
|
-
fn square_brackets(node: Node<'_>) -> bool {
|
|
1234
|
+
fn square_brackets(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
1158
1235
|
if node.kind_str() != "element_reference" {
|
|
1159
1236
|
return false;
|
|
1160
1237
|
}
|
|
1161
1238
|
let Some(object) = node.field("object") else {
|
|
1162
1239
|
return false;
|
|
1163
1240
|
};
|
|
1164
|
-
super::conditional::descendants(object)
|
|
1241
|
+
super::conditional::descendants(object, context)
|
|
1165
1242
|
.into_iter()
|
|
1166
1243
|
.any(|descendant| match descendant.kind_str() {
|
|
1167
1244
|
"string" | "array" | "string_array" | "symbol_array" | "hash" => true,
|
|
@@ -46,7 +46,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
46
46
|
}
|
|
47
47
|
let Some(argument) = node
|
|
48
48
|
.field("arguments")
|
|
49
|
-
.and_then(|arguments| super::nodes::
|
|
49
|
+
.and_then(|arguments| super::nodes::children_in(arguments, context).into_iter().next())
|
|
50
50
|
else {
|
|
51
51
|
continue;
|
|
52
52
|
};
|
|
@@ -14,6 +14,7 @@ use tree_sitter::Node;
|
|
|
14
14
|
use crate::diagnostic::{Edit, Offense};
|
|
15
15
|
use crate::rules::RuleContext;
|
|
16
16
|
use crate::rules::node_ext::NodeExt;
|
|
17
|
+
use crate::rules::send_node::named_children_of;
|
|
17
18
|
|
|
18
19
|
/// `REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS`: what a character class is the escape for.
|
|
19
20
|
const REQUIRES_ESCAPE: [char; 10] = ['.', '*', '+', '?', '{', '}', '(', ')', '|', '$'];
|
|
@@ -114,9 +115,9 @@ impl Pattern {
|
|
|
114
115
|
let flags = &context.source.node_text(closing)[1..];
|
|
115
116
|
let start = opening.end_byte();
|
|
116
117
|
let end = closing.start_byte();
|
|
117
|
-
let
|
|
118
|
-
let interpolations: Vec<Range<usize>> = node
|
|
119
|
-
.
|
|
118
|
+
let _cursor = node.walk();
|
|
119
|
+
let interpolations: Vec<Range<usize>> = named_children_of(node, context)
|
|
120
|
+
.into_iter()
|
|
120
121
|
.filter(|child| child.kind_str() == "interpolation")
|
|
121
122
|
.map(|child| child.byte_range())
|
|
122
123
|
.collect();
|
|
@@ -3,6 +3,7 @@ use tree_sitter::Node;
|
|
|
3
3
|
use crate::diagnostic::{Edit, Offense};
|
|
4
4
|
use crate::rules::RuleContext;
|
|
5
5
|
use crate::rules::node_ext::NodeExt;
|
|
6
|
+
use crate::rules::send_node::named_children_iter;
|
|
6
7
|
|
|
7
8
|
const MSG: &str = "Redundant escape inside regexp literal";
|
|
8
9
|
|
|
@@ -67,8 +68,8 @@ impl Literal {
|
|
|
67
68
|
let mut pattern = context.source.slice(start..closing.start_byte()).to_owned();
|
|
68
69
|
// `with_interpolations_blanked`: the code inside `#{}` is no part of the pattern, but its
|
|
69
70
|
// width has to stay so that the offsets still line up.
|
|
70
|
-
let
|
|
71
|
-
for child in node
|
|
71
|
+
let _cursor = node.walk();
|
|
72
|
+
for child in named_children_iter(node, context) {
|
|
72
73
|
if child.kind_str() != "interpolation" {
|
|
73
74
|
continue;
|
|
74
75
|
}
|
|
@@ -157,8 +157,7 @@ const NOT_A_STATEMENT: &[&str] = &["comment", "empty_statement", "heredoc_body"]
|
|
|
157
157
|
/// protected body -- so it is skipped even though it is a child here.
|
|
158
158
|
fn check_sequence<'tree>(node: Node<'tree>, returns: &mut Vec<Node<'tree>>) {
|
|
159
159
|
let mut cursor = node.walk();
|
|
160
|
-
let children: Vec<Node<'tree>> = node
|
|
161
|
-
.named_children(&mut cursor)
|
|
160
|
+
let children: Vec<Node<'tree>> = node.named_children(&mut cursor)
|
|
162
161
|
.filter(|child| !NOT_A_STATEMENT.contains(&child.kind_str()))
|
|
163
162
|
.collect();
|
|
164
163
|
|
|
@@ -14,8 +14,8 @@ use tree_sitter::Node;
|
|
|
14
14
|
|
|
15
15
|
use crate::diagnostic::{Edit, Offense};
|
|
16
16
|
use crate::rules::RuleContext;
|
|
17
|
-
use crate::rules::send_node;
|
|
18
17
|
use crate::rules::node_ext::NodeExt;
|
|
18
|
+
use crate::rules::send_node;
|
|
19
19
|
|
|
20
20
|
const MSG: &str = "Redundant `self` detected.";
|
|
21
21
|
|
|
@@ -157,6 +157,11 @@ const CONDITIONALS: &[&str] = &[
|
|
|
157
157
|
type Scope = Rc<RefCell<Vec<String>>>;
|
|
158
158
|
|
|
159
159
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
160
|
+
// Without an explicit `self` token no send can be reported. Building the scope table is a
|
|
161
|
+
// whole-file operation, so avoid it for the common file that has no candidate at all.
|
|
162
|
+
if context.nodes_of("self").next().is_none() {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
160
165
|
let mut cop = Cop {
|
|
161
166
|
context,
|
|
162
167
|
allowed: HashSet::new(),
|
|
@@ -204,10 +209,10 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
204
209
|
|
|
205
210
|
/// `add_scope`: every node under this one answers with the same list.
|
|
206
211
|
fn add_scope(&mut self, node: Node<'tree>, scope: Scope) {
|
|
207
|
-
let mut stack = send_node::
|
|
212
|
+
let mut stack = send_node::named_children_of(node, self.context);
|
|
208
213
|
while let Some(current) = stack.pop() {
|
|
209
214
|
self.scopes.insert(current.id(), Scope::clone(&scope));
|
|
210
|
-
stack.extend(send_node::
|
|
215
|
+
stack.extend(send_node::named_children_of(current, self.context));
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
|
|
@@ -245,16 +250,13 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
fn on_assignment(&mut self, node: Node<'tree>) {
|
|
248
|
-
let (Some(left), Some(right)) = (
|
|
249
|
-
node.field("left"),
|
|
250
|
-
node.field("right"),
|
|
251
|
-
) else {
|
|
253
|
+
let (Some(left), Some(right)) = (node.field("left"), node.field("right")) else {
|
|
252
254
|
return;
|
|
253
255
|
};
|
|
254
256
|
match left.kind_str() {
|
|
255
257
|
// `on_masgn`: every name the left-hand side binds.
|
|
256
258
|
"left_assignment_list" => {
|
|
257
|
-
for target in send_node::
|
|
259
|
+
for target in send_node::named_children_of(left, self.context) {
|
|
258
260
|
let name = self.context.source.node_text(target).to_owned();
|
|
259
261
|
self.add_lhs_to_scopes(right, name);
|
|
260
262
|
}
|
|
@@ -288,13 +290,13 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
fn on_resbody(&mut self, node: Node<'tree>) {
|
|
291
|
-
let Some(variable) = send_node::
|
|
293
|
+
let Some(variable) = send_node::named_children_of(node, self.context)
|
|
292
294
|
.into_iter()
|
|
293
295
|
.find(|child| child.kind_str() == "exception_variable")
|
|
294
296
|
else {
|
|
295
297
|
return;
|
|
296
298
|
};
|
|
297
|
-
let Some(name) = send_node::
|
|
299
|
+
let Some(name) = send_node::named_children_of(variable, self.context)
|
|
298
300
|
.first()
|
|
299
301
|
.filter(|bound| bound.kind_str() == "identifier")
|
|
300
302
|
.map(|bound| self.context.source.node_text(*bound).to_owned())
|
|
@@ -307,12 +309,12 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
307
309
|
/// `add_match_var_scopes`: `in [Integer => n]` binds `n` for the branch.
|
|
308
310
|
fn on_in_pattern(&mut self, node: Node<'tree>) {
|
|
309
311
|
let mut names = Vec::new();
|
|
310
|
-
let mut stack = send_node::
|
|
312
|
+
let mut stack = send_node::named_children_of(node, self.context);
|
|
311
313
|
while let Some(current) = stack.pop() {
|
|
312
314
|
if is_match_var(current) {
|
|
313
315
|
names.push(self.context.source.node_text(current).to_owned());
|
|
314
316
|
}
|
|
315
|
-
stack.extend(send_node::
|
|
317
|
+
stack.extend(send_node::named_children_of(current, self.context));
|
|
316
318
|
}
|
|
317
319
|
self.scope(node.id()).borrow_mut().extend(names);
|
|
318
320
|
}
|
|
@@ -324,7 +326,7 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
324
326
|
return;
|
|
325
327
|
};
|
|
326
328
|
let mut names = Vec::new();
|
|
327
|
-
let mut stack = send_node::
|
|
329
|
+
let mut stack = send_node::named_children_of(node, self.context);
|
|
328
330
|
while let Some(current) = stack.pop() {
|
|
329
331
|
if current.kind_str() == "assignment"
|
|
330
332
|
&& let Some(left) = current.field("left")
|
|
@@ -332,14 +334,14 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
332
334
|
match left.kind_str() {
|
|
333
335
|
"identifier" => names.push(self.context.source.node_text(left).to_owned()),
|
|
334
336
|
"left_assignment_list" => names.extend(
|
|
335
|
-
send_node::
|
|
337
|
+
send_node::named_children_of(left, self.context)
|
|
336
338
|
.into_iter()
|
|
337
339
|
.map(|target| self.context.source.node_text(target).to_owned()),
|
|
338
340
|
),
|
|
339
341
|
_ => {}
|
|
340
342
|
}
|
|
341
343
|
}
|
|
342
|
-
stack.extend(send_node::
|
|
344
|
+
stack.extend(send_node::named_children_of(current, self.context));
|
|
343
345
|
}
|
|
344
346
|
for name in names {
|
|
345
347
|
self.add_lhs_to_scopes(condition, name);
|
|
@@ -483,7 +485,7 @@ impl<'tree> Cop<'_, 'tree> {
|
|
|
483
485
|
let mut current = node.parent_of(self.context);
|
|
484
486
|
while let Some(visited) = current {
|
|
485
487
|
if matches!(visited.kind_str(), "do_block" | "block" | "lambda") {
|
|
486
|
-
return send_node::
|
|
488
|
+
return send_node::named_children_of(visited, self.context)
|
|
487
489
|
.iter()
|
|
488
490
|
.all(|child| child.kind_str() != "block_parameters");
|
|
489
491
|
}
|
|
@@ -518,6 +520,16 @@ fn is_match_var(node: Node<'_>) -> bool {
|
|
|
518
520
|
) || parent.kind_str() == "array_pattern"
|
|
519
521
|
|| parent.kind_str() == "find_pattern"
|
|
520
522
|
|| parent.kind_str() == "hash_pattern"
|
|
523
|
+
// `in {x: foo}`: the grammar puts a `keyword_pattern` between the hash pattern
|
|
524
|
+
// and the name. Its key is a `hash_key_symbol`, so an identifier here is always
|
|
525
|
+
// the binding -- `in {x:}` binds `x` the same way.
|
|
526
|
+
|| parent.kind_str() == "keyword_pattern"
|
|
521
527
|
|| parent.kind_str() == "alternative_pattern"
|
|
528
|
+
// `in *bar` is a `match_rest` holding a `match_var` upstream, so the name it
|
|
529
|
+
// catches is bound for the branch just like any other. A bare `*` binds nothing
|
|
530
|
+
// and has no identifier under it.
|
|
531
|
+
|| parent.kind_str() == "splat_parameter"
|
|
532
|
+
// `^foo` is a `pin`, not a binding: the identifier under it stays a read, which is
|
|
533
|
+
// why `variable_reference_pattern` is deliberately absent from this list.
|
|
522
534
|
})
|
|
523
535
|
}
|
|
@@ -85,7 +85,7 @@ fn redundant_sort<'tree>(context: &RuleContext<'_>, node: Node<'tree>) -> Option
|
|
|
85
85
|
// `sorted[0]` is a call to `:[]` upstream, whose selector is the bracket.
|
|
86
86
|
"element_reference" => {
|
|
87
87
|
let object = node.field("object")?;
|
|
88
|
-
let indices = super::nodes::
|
|
88
|
+
let indices = super::nodes::children_in(node, context);
|
|
89
89
|
let index = match indices.as_slice() {
|
|
90
90
|
[_, only] => *only,
|
|
91
91
|
_ => return None,
|
|
@@ -30,7 +30,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
30
30
|
}
|
|
31
31
|
let read = context.source.node_text(*statement);
|
|
32
32
|
let message = match block.field("parameters") {
|
|
33
|
-
Some(parameters) => match super::nodes::
|
|
33
|
+
Some(parameters) => match super::nodes::children_in(parameters, context).as_slice() {
|
|
34
34
|
[only]
|
|
35
35
|
if only.kind_str() == "identifier" && context.source.node_text(*only) == read =>
|
|
36
36
|
{
|
|
@@ -7,6 +7,7 @@ use tree_sitter::Node;
|
|
|
7
7
|
use crate::diagnostic::{Edit, Offense};
|
|
8
8
|
use crate::rules::RuleContext;
|
|
9
9
|
use crate::rules::node_ext::NodeExt;
|
|
10
|
+
use crate::rules::send_node::all_children_of;
|
|
10
11
|
|
|
11
12
|
/// One literal as `on_str` sees it: the text to scan, plus what the delimiters make of a backslash.
|
|
12
13
|
struct Literal {
|
|
@@ -58,7 +59,7 @@ fn literals(context: &RuleContext<'_>) -> Vec<Literal> {
|
|
|
58
59
|
for array in context.nodes_of("string_array") {
|
|
59
60
|
let upper = delimiter_text(array, context, 0).starts_with("%W");
|
|
60
61
|
let delimiters = array_delimiters(array, context);
|
|
61
|
-
for word in super::nodes::
|
|
62
|
+
for word in super::nodes::children_in(array, context) {
|
|
62
63
|
if word.kind_str() != "bare_string" {
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
@@ -73,7 +74,7 @@ fn literals(context: &RuleContext<'_>) -> Vec<Literal> {
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
for (opener, body) in heredocs(context) {
|
|
76
|
-
let Some(terminator) = super::nodes::
|
|
77
|
+
let Some(terminator) = super::nodes::children_in(body, context)
|
|
77
78
|
.into_iter()
|
|
78
79
|
.find(|child| child.kind_str() == "heredoc_end")
|
|
79
80
|
else {
|
|
@@ -171,9 +172,9 @@ fn disabling_interpolation(source: &str, start: usize) -> bool {
|
|
|
171
172
|
|
|
172
173
|
/// The delimiter written at one end of a literal, which the grammar keeps as an unnamed token.
|
|
173
174
|
fn delimiter_text<'a>(node: Node<'_>, context: &'a RuleContext<'_>, end: usize) -> &'a str {
|
|
174
|
-
let
|
|
175
|
-
let children: Vec<Node<'_>> = node
|
|
176
|
-
.
|
|
175
|
+
let _cursor = node.walk();
|
|
176
|
+
let children: Vec<Node<'_>> = all_children_of(node, context)
|
|
177
|
+
.into_iter()
|
|
177
178
|
.filter(|c| !c.is_named())
|
|
178
179
|
.collect();
|
|
179
180
|
let token = match end {
|
|
@@ -5,6 +5,7 @@ use tree_sitter::Node;
|
|
|
5
5
|
use crate::diagnostic::{Edit, Offense};
|
|
6
6
|
use crate::rules::RuleContext;
|
|
7
7
|
use crate::rules::node_ext::NodeExt;
|
|
8
|
+
use crate::rules::send_node::named_children_iter;
|
|
8
9
|
|
|
9
10
|
const MSG_USE_SLASHES: &str = "Use `//` around regular expression.";
|
|
10
11
|
const MSG_USE_PERCENT_R: &str = "Use `%r` around regular expression.";
|
|
@@ -123,8 +124,8 @@ impl<'a> Literal<'a> {
|
|
|
123
124
|
|
|
124
125
|
let mut literal_text = String::new();
|
|
125
126
|
let mut start = begin.end_byte();
|
|
126
|
-
let
|
|
127
|
-
for child in node
|
|
127
|
+
let _cursor = node.walk();
|
|
128
|
+
for child in named_children_iter(node, context) {
|
|
128
129
|
if child.kind_str() != "interpolation" {
|
|
129
130
|
continue;
|
|
130
131
|
}
|
|
@@ -80,7 +80,7 @@ fn method_name<'a>(call: Node<'_>, context: &'a RuleContext<'_>) -> Option<&'a s
|
|
|
80
80
|
/// `find_previous_older_sibling`: the nearest `require` above that this one sorts before, with the
|
|
81
81
|
/// search stopping at anything that is not a `require` of the same kind.
|
|
82
82
|
fn find_previous_older_sibling<'tree>(
|
|
83
|
-
context: &RuleContext<'_>,
|
|
83
|
+
context: &'tree RuleContext<'_>,
|
|
84
84
|
call: Node<'tree>,
|
|
85
85
|
parent: Node<'tree>,
|
|
86
86
|
) -> Option<Node<'tree>> {
|
|
@@ -90,7 +90,7 @@ fn find_previous_older_sibling<'tree>(
|
|
|
90
90
|
false => call,
|
|
91
91
|
};
|
|
92
92
|
let container = search.parent()?;
|
|
93
|
-
let statements = super::nodes::
|
|
93
|
+
let statements = super::nodes::children_in(container, context);
|
|
94
94
|
let position = statements
|
|
95
95
|
.iter()
|
|
96
96
|
.position(|statement| statement.id() == search.id())?;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
use tree_sitter::Node;
|
|
4
4
|
|
|
5
5
|
use crate::diagnostic::{Edit, Offense};
|
|
6
|
+
use crate::ruby_version::RubyVersion;
|
|
6
7
|
use crate::rules::RuleContext;
|
|
7
8
|
use crate::rules::node_ext::NodeExt;
|
|
8
9
|
use crate::rules::send_node;
|
|
@@ -30,9 +31,13 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
30
31
|
// **多重代入のときだけ**である。`w = 1, 2 rescue nil` は `(rescue (lvasgn w (array 1 2))
|
|
31
32
|
// ..)` で修飾子が外に出るが、`a, b = 1, 2 rescue nil` は `(masgn (mlhs ..) (rescue ..))`
|
|
32
33
|
// で中に入る。左辺が代入の並びかどうかで分かれる。
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// その振り分けは **2.7 以降のパーサ**の話である。2.6 では多重代入もろとも `rescue` の
|
|
35
|
+
// 中に入る (`(rescue (masgn ..) ..)`) ので、文法が作る形がそのまま本家の形になる。
|
|
36
|
+
let splits_multiple_assignment = context.target_ruby_version() >= RubyVersion::new(2, 7);
|
|
37
|
+
let operation = match splits_multiple_assignment
|
|
38
|
+
&& operation
|
|
39
|
+
.field("left")
|
|
40
|
+
.is_some_and(|left| left.kind_str() == "left_assignment_list")
|
|
36
41
|
{
|
|
37
42
|
true => match operation.field("right") {
|
|
38
43
|
Some(right) => right,
|
|
@@ -127,7 +132,7 @@ fn heredoc_end(context: &RuleContext<'_>, operation: Node<'_>) -> Option<usize>
|
|
|
127
132
|
.map(send_node::Argument::first)
|
|
128
133
|
.find(|argument| argument.kind_str() == "heredoc_beginning")?;
|
|
129
134
|
let body = send_node::heredoc_body(beginning, context)?;
|
|
130
|
-
super::nodes::
|
|
135
|
+
super::nodes::children_in(body, context)
|
|
131
136
|
.into_iter()
|
|
132
137
|
.find(|child| child.kind_str() == "heredoc_end")
|
|
133
138
|
.map(|terminator| terminator.end_byte())
|
|
@@ -49,7 +49,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
49
49
|
/// `(array (const {nil? cbase} :StandardError))`: the one name, unqualified or written from the
|
|
50
50
|
/// root.
|
|
51
51
|
fn only_standard_error(context: &RuleContext<'_>, list: Node<'_>) -> bool {
|
|
52
|
-
let exceptions = super::nodes::
|
|
52
|
+
let exceptions = super::nodes::children_in(list, context);
|
|
53
53
|
let [only] = exceptions.as_slice() else {
|
|
54
54
|
return false;
|
|
55
55
|
};
|
|
@@ -13,11 +13,11 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
13
13
|
.setting::<String>("EnforcedStyle")
|
|
14
14
|
.is_some_and(|style| style == "return_nil");
|
|
15
15
|
for node in context.nodes_of("return") {
|
|
16
|
-
let written = super::nodes::
|
|
16
|
+
let written = super::nodes::children_in(node, context);
|
|
17
17
|
let returns_nil = matches!(
|
|
18
18
|
written.as_slice(),
|
|
19
19
|
[list] if list.kind_str() == "argument_list"
|
|
20
|
-
&& matches!(super::nodes::
|
|
20
|
+
&& matches!(super::nodes::children_in(*list, context).as_slice(),
|
|
21
21
|
[only] if only.kind_str() == "nil")
|
|
22
22
|
);
|
|
23
23
|
// `correct_style?`: only one of the two spellings is ever wrong, and a `return 1` is
|
|
@@ -43,9 +43,9 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
43
43
|
};
|
|
44
44
|
// `body.each_descendant(:return)`: every `return` **below** the body. A body that is
|
|
45
45
|
// itself a bare `return nil` is not a descendant of itself, so upstream misses it.
|
|
46
|
-
let mut stack: Vec<Node<'_>> = super::nodes::
|
|
46
|
+
let mut stack: Vec<Node<'_>> = super::nodes::children_in(body, context);
|
|
47
47
|
while let Some(current) = stack.pop() {
|
|
48
|
-
stack.extend(super::nodes::
|
|
48
|
+
stack.extend(super::nodes::children_in(current, context));
|
|
49
49
|
if current.kind_str() == "return" && returns_nil(current) {
|
|
50
50
|
offenses.push(replacement(context, current, "return false"));
|
|
51
51
|
}
|
|
@@ -533,7 +533,7 @@ fn and_with_rhs_or(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
533
533
|
return true;
|
|
534
534
|
}
|
|
535
535
|
right.kind_str() == "parenthesized_statements"
|
|
536
|
-
&& matches!(super::nodes::
|
|
536
|
+
&& matches!(super::nodes::children_in(right, context).as_slice(), [only] if is_or(context, *only))
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
/// `find_matching_receiver_invocation`.
|
|
@@ -686,7 +686,7 @@ fn and_parts<'tree>(context: &RuleContext<'_>, node: Node<'tree>) -> Vec<Part<'t
|
|
|
686
686
|
fn and_inside_begin(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
687
687
|
send_node::any_descendant(node, &mut |candidate| {
|
|
688
688
|
candidate.kind_str() == "parenthesized_statements"
|
|
689
|
-
&& super::nodes::
|
|
689
|
+
&& super::nodes::children_in(candidate, context)
|
|
690
690
|
.first()
|
|
691
691
|
.is_some_and(|first| is_and(context, *first))
|
|
692
692
|
})
|
|
@@ -52,7 +52,14 @@ fn safe_navigation_chains<'tree>(node: Node<'tree>, context: &RuleContext<'_>) -
|
|
|
52
52
|
let Some(parent) = enclosing(current) else {
|
|
53
53
|
break;
|
|
54
54
|
};
|
|
55
|
-
|
|
55
|
+
// **`other&.foo = x` is one `csend` of `:foo=` upstream.** The grammar writes it as an
|
|
56
|
+
// assignment whose target is the safe navigation, so the assignment stands where that
|
|
57
|
+
// `csend` would.
|
|
58
|
+
let is_csend_setter = parent.kind_str() == "assignment"
|
|
59
|
+
&& parent
|
|
60
|
+
.field("left")
|
|
61
|
+
.is_some_and(|left| is_safe_navigation(left, context));
|
|
62
|
+
if !is_csend_setter && !is_safe_navigation(parent, context) {
|
|
56
63
|
break;
|
|
57
64
|
}
|
|
58
65
|
chains.push(parent);
|