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
|
@@ -58,14 +58,14 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
58
58
|
/// `single_negative?`: the condition, once its parentheses are peeled off, is one `!` applied to
|
|
59
59
|
/// something that is not itself a negation. `not x` is the same `send` upstream, so it counts too.
|
|
60
60
|
fn single_negative<'tree>(
|
|
61
|
-
context: &RuleContext<'_>,
|
|
61
|
+
context: &'tree RuleContext<'_>,
|
|
62
62
|
condition: Node<'tree>,
|
|
63
63
|
) -> Option<send_node::Negation<'tree>> {
|
|
64
64
|
let mut current = condition;
|
|
65
65
|
// `condition = condition.children.last while condition.begin_type?`, with `(begin)` -- an
|
|
66
66
|
// empty condition -- excluded before the loop even starts.
|
|
67
67
|
while current.kind_str() == "parenthesized_statements" {
|
|
68
|
-
current = *super::nodes::
|
|
68
|
+
current = *super::nodes::children_in(current, context).last()?;
|
|
69
69
|
}
|
|
70
70
|
let found = send_node::negation(current, context)?;
|
|
71
71
|
// `!(send _ :!)`: `!!x` negates a negation and is left alone. **The inner one may be written
|
|
@@ -29,7 +29,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
29
29
|
// `else_branch &&`: an `else` that holds nothing is no branch at all upstream, where an
|
|
30
30
|
// empty body is `nil` rather than a node. The grammar still writes the `else` keyword as a
|
|
31
31
|
// node, so asking whether the field is present answers a different question.
|
|
32
|
-
if alternative.kind_str() == "else" && super::nodes::
|
|
32
|
+
if alternative.kind_str() == "else" && super::nodes::children_in(alternative, context).is_empty() {
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
35
|
let Some(condition) = node.field("condition").map(unwrap_parentheses) else {
|
|
@@ -118,7 +118,7 @@ fn swap_branches(
|
|
|
118
118
|
// line goes away instead.
|
|
119
119
|
if node
|
|
120
120
|
.field("consequence")
|
|
121
|
-
.is_none_or(|branch| super::nodes::
|
|
121
|
+
.is_none_or(|branch| super::nodes::children_in(branch, context).is_empty())
|
|
122
122
|
{
|
|
123
123
|
let keyword = alternative.start_byte()..alternative.start_byte() + ELSE_LENGTH;
|
|
124
124
|
return vec![replace(support::whole_lines(keyword, context), String::new())];
|
|
@@ -140,9 +140,11 @@ fn swap_branches(
|
|
|
140
140
|
/// `unwrap_begin_nodes`.
|
|
141
141
|
fn unwrap_parentheses<'tree>(node: Node<'tree>) -> Node<'tree> {
|
|
142
142
|
let mut current = node;
|
|
143
|
+
// `unwrap_begin_nodes` peels a `begin` as well as a `kwbegin`, which the grammar writes as
|
|
144
|
+
// `begin … end`. Leaving that one on hid `begin / x !~ y / end ? a : b` from the cop.
|
|
143
145
|
while matches!(
|
|
144
146
|
current.kind_str(),
|
|
145
|
-
"parenthesized_statements" | "begin_block"
|
|
147
|
+
"parenthesized_statements" | "begin_block" | "begin"
|
|
146
148
|
) {
|
|
147
149
|
match super::nodes::children(current).as_slice() {
|
|
148
150
|
[only] => current = *only,
|
|
@@ -52,7 +52,7 @@ pub(super) fn single_negative<'tree>(
|
|
|
52
52
|
) -> Option<send_node::Negation<'tree>> {
|
|
53
53
|
// `empty_condition?` is `(begin)`: `while ()` has nothing to negate.
|
|
54
54
|
if condition.kind_str() == "parenthesized_statements"
|
|
55
|
-
&& super::nodes::
|
|
55
|
+
&& super::nodes::children_in(condition, context).is_empty()
|
|
56
56
|
{
|
|
57
57
|
return None;
|
|
58
58
|
}
|
|
@@ -136,10 +136,21 @@ fn range_offense(
|
|
|
136
136
|
/// The receiver and the single index of a subscript, in both spellings of `[]`.
|
|
137
137
|
fn subscript<'tree>(
|
|
138
138
|
node: Node<'tree>,
|
|
139
|
-
context: &RuleContext<'_>,
|
|
139
|
+
context: &'tree RuleContext<'_>,
|
|
140
140
|
) -> Option<(Node<'tree>, Node<'tree>)> {
|
|
141
141
|
match node.kind_str() {
|
|
142
142
|
"element_reference" => {
|
|
143
|
+
// `RESTRICT_ON_SEND = %i[[]]`: `arr[i] = value` dispatches `[]=`, which this cop never
|
|
144
|
+
// sees. The grammar writes the assignment around the same `element_reference`, so the
|
|
145
|
+
// read and the write look alike until the parent is checked.
|
|
146
|
+
if node.parent_of(context).is_some_and(|parent| {
|
|
147
|
+
matches!(parent.kind_str(), "assignment" | "operator_assignment")
|
|
148
|
+
&& parent
|
|
149
|
+
.field("left")
|
|
150
|
+
.is_some_and(|left| left.id() == node.id())
|
|
151
|
+
}) {
|
|
152
|
+
return None;
|
|
153
|
+
}
|
|
143
154
|
let object = node.field("object")?;
|
|
144
155
|
let children = super::nodes::children(node);
|
|
145
156
|
Some((object, *children.get(1)?))
|
|
@@ -150,7 +161,7 @@ fn subscript<'tree>(
|
|
|
150
161
|
return None;
|
|
151
162
|
}
|
|
152
163
|
let receiver = node.field("receiver")?;
|
|
153
|
-
let arguments = super::nodes::
|
|
164
|
+
let arguments = super::nodes::children_in(node.field("arguments")?, context);
|
|
154
165
|
Some((receiver, *arguments.first()?))
|
|
155
166
|
}
|
|
156
167
|
}
|
|
@@ -179,7 +190,13 @@ fn length_subtraction<'tree>(
|
|
|
179
190
|
}
|
|
180
191
|
let left = node.field("left")?;
|
|
181
192
|
match left.kind_str() {
|
|
182
|
-
|
|
193
|
+
// `(send $_ {:length :size :count})` matches a call, and a local variable named `length`
|
|
194
|
+
// is an `lvar` -- `length = do_something; self[length - 1]` subtracts from a number nobody
|
|
195
|
+
// asked the receiver for, so there is no `[-1]` to rewrite it to.
|
|
196
|
+
"identifier"
|
|
197
|
+
if LENGTH_METHODS.contains(&context.source.node_text(left))
|
|
198
|
+
&& !context.variable_analysis().is_variable_reference(left) =>
|
|
199
|
+
{
|
|
183
200
|
Some((None, count))
|
|
184
201
|
}
|
|
185
202
|
"call" => {
|
|
@@ -187,6 +204,11 @@ fn length_subtraction<'tree>(
|
|
|
187
204
|
if !LENGTH_METHODS.contains(&context.source.node_text(selector)) {
|
|
188
205
|
return None;
|
|
189
206
|
}
|
|
207
|
+
// `(send $_ {:length :size :count})` names no arguments, so it matches only the call
|
|
208
|
+
// that asks for the length: `arr.size(1)` counts something else entirely.
|
|
209
|
+
if left.field("arguments").is_some() || left.field("block").is_some() {
|
|
210
|
+
return None;
|
|
211
|
+
}
|
|
190
212
|
Some((left.field("receiver"), count))
|
|
191
213
|
}
|
|
192
214
|
_ => None,
|
|
@@ -117,7 +117,7 @@ fn parenthesize_arguments(context: &RuleContext<'_>, node: Node<'_>) -> Option<S
|
|
|
117
117
|
if selector.kind_str() == "operator" {
|
|
118
118
|
return None;
|
|
119
119
|
}
|
|
120
|
-
let arguments = super::nodes::
|
|
120
|
+
let arguments = super::nodes::children_in(node.field("arguments")?, context);
|
|
121
121
|
if arguments.is_empty() {
|
|
122
122
|
return None;
|
|
123
123
|
}
|
|
@@ -44,7 +44,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
44
44
|
{
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
|
-
let outer_arguments = super::nodes::
|
|
47
|
+
let outer_arguments = super::nodes::children_in(arguments, context);
|
|
48
48
|
// `each_child_node(:call)` walks the receiver too, since it is a child of the send.
|
|
49
49
|
let children = node
|
|
50
50
|
.field("receiver")
|
|
@@ -57,7 +57,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
57
57
|
let Some(nested_arguments) = nested.field("arguments") else {
|
|
58
58
|
continue;
|
|
59
59
|
};
|
|
60
|
-
let inner = super::nodes::
|
|
60
|
+
let inner = super::nodes::children_in(nested_arguments, context);
|
|
61
61
|
if inner.is_empty()
|
|
62
62
|
|| nested_arguments
|
|
63
63
|
.child(0)
|
|
@@ -15,9 +15,9 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
15
15
|
let mut reported: HashSet<(usize, usize)> = HashSet::new();
|
|
16
16
|
|
|
17
17
|
for node in context.nodes_of("conditional") {
|
|
18
|
-
let nested: Vec<Node<'_>> = super::nodes::
|
|
18
|
+
let nested: Vec<Node<'_>> = super::nodes::children_in(node, context)
|
|
19
19
|
.into_iter()
|
|
20
|
-
.flat_map(super::conditional::descendants)
|
|
20
|
+
.flat_map(|node| super::conditional::descendants(node, context))
|
|
21
21
|
.filter(|descendant| descendant.kind_str() == "conditional")
|
|
22
22
|
.collect();
|
|
23
23
|
for descendant in nested {
|
data/src/rules/style/next.rs
CHANGED
|
@@ -128,9 +128,13 @@ impl Cop<'_, '_> {
|
|
|
128
128
|
let Some(condition) = last.field("condition") else {
|
|
129
129
|
return;
|
|
130
130
|
};
|
|
131
|
+
// The reported range stops at the condition, but the correction reaches the `then` beyond
|
|
132
|
+
// it and the `end` that closes the branch. Anchoring on the whole conditional is what keeps
|
|
133
|
+
// those edits inside the offense they belong to.
|
|
131
134
|
let offense = self
|
|
132
135
|
.context
|
|
133
|
-
.offense(MSG, last.start_byte()..condition.end_byte())
|
|
136
|
+
.offense(MSG, last.start_byte()..condition.end_byte())
|
|
137
|
+
.corrections_anchored_at(last.byte_range());
|
|
134
138
|
offenses.push(offense.corrected_by_all(self.autocorrect(last, condition, reindented)));
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -164,16 +168,12 @@ impl Cop<'_, '_> {
|
|
|
164
168
|
.field("consequence")
|
|
165
169
|
.map(body_of)
|
|
166
170
|
.or_else(|| node.field("body").map(Body::One));
|
|
167
|
-
[
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.any(|child| {
|
|
174
|
-
matches!(child.kind_str(), "if" | "unless")
|
|
175
|
-
&& child.field("alternative").is_some()
|
|
176
|
-
})
|
|
171
|
+
[node.field("condition"), body.and_then(|body| body.single())]
|
|
172
|
+
.into_iter()
|
|
173
|
+
.flatten()
|
|
174
|
+
.any(|child| {
|
|
175
|
+
matches!(child.kind_str(), "if" | "unless") && child.field("alternative").is_some()
|
|
176
|
+
})
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
fn min_body_length(&self, node: Node<'_>) -> bool {
|
|
@@ -306,7 +306,7 @@ impl Cop<'_, '_> {
|
|
|
306
306
|
|
|
307
307
|
/// `heredoc_lines`: `(body.line...body.last_line)` of every heredoc the node opens.
|
|
308
308
|
fn heredoc_lines(&self, node: Node<'_>) -> Vec<usize> {
|
|
309
|
-
let beginnings: Vec<usize> = descendants(node)
|
|
309
|
+
let beginnings: Vec<usize> = descendants(node, self.context)
|
|
310
310
|
.into_iter()
|
|
311
311
|
.filter(|inner| inner.kind_str() == "heredoc_beginning")
|
|
312
312
|
.map(|inner| inner.start_byte())
|
|
@@ -108,7 +108,7 @@ fn returns_nil(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
108
108
|
match node.kind_str() {
|
|
109
109
|
"nil" => true,
|
|
110
110
|
"return" | "next" | "break" => {
|
|
111
|
-
let arguments = super::nodes::
|
|
111
|
+
let arguments = super::nodes::children_in(node, context);
|
|
112
112
|
let [list] = arguments.as_slice() else {
|
|
113
113
|
return false;
|
|
114
114
|
};
|
|
@@ -116,7 +116,7 @@ fn returns_nil(node: Node<'_>, context: &RuleContext<'_>) -> bool {
|
|
|
116
116
|
return false;
|
|
117
117
|
}
|
|
118
118
|
let _ = context;
|
|
119
|
-
matches!(super::nodes::
|
|
119
|
+
matches!(super::nodes::children_in(*list, context).as_slice(), [only] if only.kind_str() == "nil")
|
|
120
120
|
}
|
|
121
121
|
_ => false,
|
|
122
122
|
}
|
data/src/rules/style/nodes.rs
CHANGED
|
@@ -57,6 +57,10 @@ pub(super) fn is_operator_method(name: &str) -> bool {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/// The node's children as upstream's AST holds them.
|
|
60
|
+
///
|
|
61
|
+
/// The list comes from the file's index rather than from a tree cursor opened here: this is one of
|
|
62
|
+
/// the most-called helpers in the registry, and the cursor walk was among the largest single costs
|
|
63
|
+
/// a sampling profile found.
|
|
60
64
|
pub(super) fn children<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
|
|
61
65
|
let mut cursor = node.walk();
|
|
62
66
|
node.named_children(&mut cursor)
|
|
@@ -64,6 +68,23 @@ pub(super) fn children<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
|
|
|
64
68
|
.collect()
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
/// [`children`] answered from the file's index, for a caller that has the cop's context to hand.
|
|
72
|
+
/// This is one of the most-called helpers in the registry, and the cursor walk `children` opens
|
|
73
|
+
/// was among the largest single costs a sampling profile found.
|
|
74
|
+
pub(super) fn children_in<'tree>(
|
|
75
|
+
node: Node<'tree>,
|
|
76
|
+
context: &'tree RuleContext<'_>,
|
|
77
|
+
) -> Vec<Node<'tree>> {
|
|
78
|
+
match context.named_children(node) {
|
|
79
|
+
Some(found) => found
|
|
80
|
+
.iter()
|
|
81
|
+
.copied()
|
|
82
|
+
.filter(|child| is_child(*child))
|
|
83
|
+
.collect(),
|
|
84
|
+
None => children(node),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
/// `ProcessedSource#contains_comment?`: whether a comment sits on any line the range spans. It
|
|
68
89
|
/// reads *lines* rather than the range itself, so a trailing comment on the closing line counts.
|
|
69
90
|
pub(super) fn contains_comment(range: &std::ops::Range<usize>, context: &RuleContext<'_>) -> bool {
|
|
@@ -26,6 +26,17 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
for node in context.nodes_of_any(&["binary", "call", "unary"]) {
|
|
29
|
+
if semantic_changes
|
|
30
|
+
&& !is_predicate_method_result(context, node)
|
|
31
|
+
&& let Some(edits) = semantic_change(context, node)
|
|
32
|
+
{
|
|
33
|
+
offenses.push(
|
|
34
|
+
context
|
|
35
|
+
.offense(MSG_FOR_REDUNDANCY, node.byte_range())
|
|
36
|
+
.corrected_by_all(edits),
|
|
37
|
+
);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
29
40
|
let Some(receiver) = comparison_with_nil(context, node) else {
|
|
30
41
|
continue;
|
|
31
42
|
};
|
|
@@ -56,6 +67,75 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
69
|
|
|
70
|
+
/// The two checks `IncludeSemanticChanges` adds: `not_and_nil_check?` (`!x.nil?`) and
|
|
71
|
+
/// `unless_and_nil_check?` (`unless x.nil?`). Both drop the `nil?` call itself, which is the
|
|
72
|
+
/// semantic change the option is named for -- `x` is falsy for `false` too.
|
|
73
|
+
fn semantic_change(context: &RuleContext<'_>, node: Node<'_>) -> Option<Vec<Edit>> {
|
|
74
|
+
// `autocorrect_non_nil`: `!x.nil?` becomes `x`, and a receiverless `!nil?` becomes `self`.
|
|
75
|
+
// `not x.nil?` is the same `:!` send as `!x.nil?`. The grammar gives `!` an `operator` field
|
|
76
|
+
// and leaves `not` as an anonymous child, so both spellings have to be read.
|
|
77
|
+
let negation = node.kind_str() == "unary"
|
|
78
|
+
&& node
|
|
79
|
+
.field("operator")
|
|
80
|
+
.or_else(|| node.child(0))
|
|
81
|
+
.is_some_and(|operator| matches!(context.source.node_text(operator), "!" | "not"));
|
|
82
|
+
if negation {
|
|
83
|
+
let operand = node.field("operand")?;
|
|
84
|
+
if !is_nil_predicate(context, operand) {
|
|
85
|
+
return None;
|
|
86
|
+
}
|
|
87
|
+
let replacement = match operand.field("receiver") {
|
|
88
|
+
Some(receiver) => context.source.node_text(receiver).to_owned(),
|
|
89
|
+
None => "self".to_owned(),
|
|
90
|
+
};
|
|
91
|
+
return Some(vec![Edit {
|
|
92
|
+
start: node.start_byte(),
|
|
93
|
+
end: node.end_byte(),
|
|
94
|
+
replacement,
|
|
95
|
+
safe: true,
|
|
96
|
+
}]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// `autocorrect_unless_nil`: the keyword becomes `if` and the condition loses its `nil?`.
|
|
100
|
+
if !is_nil_predicate(context, node) {
|
|
101
|
+
return None;
|
|
102
|
+
}
|
|
103
|
+
let parent = node.parent_of(context)?;
|
|
104
|
+
if !matches!(parent.kind_str(), "unless" | "unless_modifier") {
|
|
105
|
+
return None;
|
|
106
|
+
}
|
|
107
|
+
if parent.field("condition")?.id() != node.id() {
|
|
108
|
+
return None;
|
|
109
|
+
}
|
|
110
|
+
let keyword = (0..parent.child_count())
|
|
111
|
+
.filter_map(|index| parent.child(index as u32))
|
|
112
|
+
.find(|child| context.source.node_text(*child) == "unless")?;
|
|
113
|
+
let receiver = node.field("receiver")?;
|
|
114
|
+
Some(vec![
|
|
115
|
+
Edit {
|
|
116
|
+
start: keyword.start_byte(),
|
|
117
|
+
end: keyword.end_byte(),
|
|
118
|
+
replacement: "if".to_owned(),
|
|
119
|
+
safe: true,
|
|
120
|
+
},
|
|
121
|
+
Edit {
|
|
122
|
+
start: node.start_byte(),
|
|
123
|
+
end: node.end_byte(),
|
|
124
|
+
replacement: context.source.node_text(receiver).to_owned(),
|
|
125
|
+
safe: true,
|
|
126
|
+
},
|
|
127
|
+
])
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// `nil_check?`: `(send _ :nil?)` with no arguments.
|
|
131
|
+
fn is_nil_predicate(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
132
|
+
node.kind_str() == "call"
|
|
133
|
+
&& node
|
|
134
|
+
.field("method")
|
|
135
|
+
.is_some_and(|method| context.source.node_text(method) == "nil?")
|
|
136
|
+
&& node.field("arguments").is_none()
|
|
137
|
+
}
|
|
138
|
+
|
|
59
139
|
/// `not_equal_to_nil?`: `(send _ :!= nil)`. The two checks `IncludeSemanticChanges` adds are
|
|
60
140
|
/// `!x.nil?` and `unless x.nil?`, which the default configuration leaves alone.
|
|
61
141
|
fn comparison_with_nil<'t>(context: &RuleContext<'_>, node: Node<'t>) -> Option<Node<'t>> {
|
|
@@ -66,7 +146,7 @@ fn comparison_with_nil<'t>(context: &RuleContext<'_>, node: Node<'t>) -> Option<
|
|
|
66
146
|
node.field("right")?,
|
|
67
147
|
),
|
|
68
148
|
"call" => {
|
|
69
|
-
let arguments = super::nodes::
|
|
149
|
+
let arguments = super::nodes::children_in(node.field("arguments")?, context);
|
|
70
150
|
let [only] = arguments.as_slice() else {
|
|
71
151
|
return None;
|
|
72
152
|
};
|
|
@@ -103,7 +183,7 @@ fn is_predicate_method_result(context: &RuleContext<'_>, node: Node<'_>) -> bool
|
|
|
103
183
|
return false;
|
|
104
184
|
}
|
|
105
185
|
// The body is the statement itself when it stands alone, and its last statement otherwise.
|
|
106
|
-
super::nodes::
|
|
186
|
+
super::nodes::children_in(parent, context)
|
|
107
187
|
.last()
|
|
108
188
|
.is_some_and(|last| last.id() == node.id())
|
|
109
189
|
}
|
|
@@ -20,9 +20,12 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
20
20
|
.setting::<String>("EnforcedStyle")
|
|
21
21
|
.is_some_and(|style| style == "disallow");
|
|
22
22
|
let locals = LocalVariables::new(context);
|
|
23
|
-
|
|
23
|
+
// **A lambda literal is a `block` upstream too.** `-> { _1 }` is a `numblock` whose send is
|
|
24
|
+
// the `lambda` call, while the grammar gives the arrow a node of its own that holds the block.
|
|
25
|
+
for node in context.nodes_of_any(&["call", "lambda"]) {
|
|
24
26
|
let Some(block) = node
|
|
25
27
|
.field("block")
|
|
28
|
+
.or_else(|| (node.kind_str() == "lambda").then(|| node.field("body")).flatten())
|
|
26
29
|
.filter(|block| BLOCK_KINDS.contains(&block.kind_str()))
|
|
27
30
|
else {
|
|
28
31
|
continue;
|
|
@@ -53,9 +53,9 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
53
53
|
/// `each_descendant(:lvar)` filtered by `/\A_[1-9]\z/`.
|
|
54
54
|
fn numbered_parameters(node: Node<'_>, context: &RuleContext<'_>) -> HashSet<String> {
|
|
55
55
|
let mut found = HashSet::new();
|
|
56
|
-
let mut stack: Vec<Node<'_>> = super::nodes::
|
|
56
|
+
let mut stack: Vec<Node<'_>> = super::nodes::children_in(node, context);
|
|
57
57
|
while let Some(current) = stack.pop() {
|
|
58
|
-
stack.extend(super::nodes::
|
|
58
|
+
stack.extend(super::nodes::children_in(current, context));
|
|
59
59
|
if current.kind_str() != "identifier" {
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
@@ -14,6 +14,19 @@ const MSG: &str =
|
|
|
14
14
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
15
15
|
let min_digits: usize = context.setting("MinDigits").unwrap_or(5);
|
|
16
16
|
let strict: bool = context.setting("Strict").unwrap_or(false);
|
|
17
|
+
// `allowed_numbers.include?(int)`: compared as **strings**, against the integer part alone.
|
|
18
|
+
let allowed: Vec<String> = context
|
|
19
|
+
.setting::<Vec<serde_yaml_ng::Value>>("AllowedNumbers")
|
|
20
|
+
.unwrap_or_default()
|
|
21
|
+
.iter()
|
|
22
|
+
.map(|value| match value {
|
|
23
|
+
serde_yaml_ng::Value::String(text) => text.clone(),
|
|
24
|
+
other => serde_yaml_ng::to_string(other)
|
|
25
|
+
.unwrap_or_default()
|
|
26
|
+
.trim()
|
|
27
|
+
.to_owned(),
|
|
28
|
+
})
|
|
29
|
+
.collect();
|
|
17
30
|
|
|
18
31
|
for node in context.nodes_of_any(&["integer", "float"]) {
|
|
19
32
|
// A rational or imaginary suffix makes a different literal upstream (`rational` / `complex`
|
|
@@ -30,7 +43,10 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
30
43
|
let integer = integer_part(source);
|
|
31
44
|
// Anything starting with `0` is a non-decimal literal (`0x`, `0b`, `0o`, or a leading-zero
|
|
32
45
|
// octal), which this cop does not know how to group.
|
|
33
|
-
if integer.starts_with('0')
|
|
46
|
+
if integer.starts_with('0')
|
|
47
|
+
|| allowed.iter().any(|entry| entry == integer)
|
|
48
|
+
|| integer.chars().count() < min_digits
|
|
49
|
+
{
|
|
34
50
|
continue;
|
|
35
51
|
}
|
|
36
52
|
if !offending(integer, strict) {
|
|
@@ -4,6 +4,11 @@ use tree_sitter::Node;
|
|
|
4
4
|
use crate::diagnostic::{Edit, Offense};
|
|
5
5
|
use crate::rules::RuleContext;
|
|
6
6
|
use crate::rules::node_ext::NodeExt;
|
|
7
|
+
use crate::rules::send_node;
|
|
8
|
+
|
|
9
|
+
/// The version `positive?` and `negative?` were added in.
|
|
10
|
+
const PREDICATES_SINCE: crate::ruby_version::RubyVersion =
|
|
11
|
+
crate::ruby_version::RubyVersion::new(2, 3);
|
|
7
12
|
|
|
8
13
|
pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
9
14
|
let predicate_style = context
|
|
@@ -25,6 +30,11 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
25
30
|
{
|
|
26
31
|
continue;
|
|
27
32
|
}
|
|
33
|
+
// `replacement_supported?`: `positive?` and `negative?` arrived in 2.3, so a comparison is
|
|
34
|
+
// left alone below that -- `zero?` has always been there.
|
|
35
|
+
if matches!(operator, ">" | "<") && context.target_ruby_version() < PREDICATES_SINCE {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
28
38
|
|
|
29
39
|
let replacement = match predicate_style {
|
|
30
40
|
true => format!(
|
|
@@ -86,7 +96,13 @@ fn predicate<'tree>(
|
|
|
86
96
|
context: &RuleContext<'_>,
|
|
87
97
|
node: Node<'tree>,
|
|
88
98
|
) -> Option<(Node<'tree>, &'static str)> {
|
|
89
|
-
|
|
99
|
+
// Upstream dispatches `on_send` but does not alias it to `on_csend`: a predicate reached
|
|
100
|
+
// through `&.` is deliberately outside this cop even though the grammar calls both forms a
|
|
101
|
+
// `call`.
|
|
102
|
+
if node.kind_str() != "call"
|
|
103
|
+
|| !send_node::is_plain_send(node, context)
|
|
104
|
+
|| node.field("arguments").is_some()
|
|
105
|
+
{
|
|
90
106
|
return None;
|
|
91
107
|
}
|
|
92
108
|
let receiver = node.field("receiver")?;
|
|
@@ -127,6 +143,11 @@ fn operands<'a, 'tree>(
|
|
|
127
143
|
))
|
|
128
144
|
}
|
|
129
145
|
_ => {
|
|
146
|
+
// `comparison` is a `(send …)` pattern, and `&.` is a `csend`: the grammar spells both
|
|
147
|
+
// a `call`, so the operator reached through a safe navigation has to be excluded here.
|
|
148
|
+
if !send_node::is_plain_send(node, context) {
|
|
149
|
+
return None;
|
|
150
|
+
}
|
|
130
151
|
let method = node.field("method")?;
|
|
131
152
|
let arguments = super::nodes::children(node.field("arguments")?);
|
|
132
153
|
match arguments.as_slice() {
|
|
@@ -174,11 +195,9 @@ fn parenthesized_source(context: &RuleContext<'_>, node: Node<'_>) -> String {
|
|
|
174
195
|
fn require_parentheses(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
175
196
|
match node.kind_str() {
|
|
176
197
|
// `&&` and `||` are not sends upstream, so they are not binary operations either.
|
|
177
|
-
"binary" => node
|
|
178
|
-
.
|
|
179
|
-
|
|
180
|
-
super::nodes::is_operator_method(context.source.node_text(operator))
|
|
181
|
-
}),
|
|
198
|
+
"binary" => node.field("operator").is_some_and(|operator| {
|
|
199
|
+
super::nodes::is_operator_method(context.source.node_text(operator))
|
|
200
|
+
}),
|
|
182
201
|
// `a[b]` is a call to `:[]`, and its `loc.begin` is a bracket rather than a parenthesis.
|
|
183
202
|
"element_reference" => true,
|
|
184
203
|
"call" => {
|
|
@@ -34,6 +34,15 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
34
34
|
let Some(alternative) = node.field("alternative") else {
|
|
35
35
|
continue;
|
|
36
36
|
};
|
|
37
|
+
// `return unless node.else_branch`: upstream's `else_branch` is what the clause **holds**,
|
|
38
|
+
// so a bare `else end` answers `nil`. The grammar keeps the empty clause as a node, and
|
|
39
|
+
// taking its presence for a branch made `if cond then run else end` look convertible.
|
|
40
|
+
if !crate::rules::send_node::named_children_of(alternative, context)
|
|
41
|
+
.iter()
|
|
42
|
+
.any(|child| child.kind_str() != "comment")
|
|
43
|
+
{
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
37
46
|
let consequence = node.field("consequence");
|
|
38
47
|
// `node.if_branch&.begin_type?`: a then-branch upstream reads as a `begin` has no ternary
|
|
39
48
|
// arm to become.
|
|
@@ -141,7 +150,7 @@ fn branch(context: &RuleContext<'_>, node: Option<Node<'_>>) -> String {
|
|
|
141
150
|
return "nil".to_owned();
|
|
142
151
|
};
|
|
143
152
|
let (source, node) = match node.kind_str() {
|
|
144
|
-
"then" | "else" => match super::nodes::
|
|
153
|
+
"then" | "else" => match super::nodes::children_in(node, context).as_slice() {
|
|
145
154
|
[only] => (context.source.node_text(*only).to_owned(), Some(*only)),
|
|
146
155
|
[] => return "nil".to_owned(),
|
|
147
156
|
[first, .., last] => (
|
|
@@ -191,11 +200,19 @@ fn requires_parentheses(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
191
200
|
{
|
|
192
201
|
return true;
|
|
193
202
|
}
|
|
194
|
-
// `keyword_with_changed_precedence?`: `not x`, and a keyword written with arguments
|
|
203
|
+
// `keyword_with_changed_precedence?`: `not x`, and a keyword written with arguments -- which
|
|
204
|
+
// includes `defined? :A`, a `defined?` node upstream and a `unary` here. Only `not` was read,
|
|
205
|
+
// so `defined? :A` lost the parentheses that keep the `?` of the ternary out of its argument.
|
|
195
206
|
if node.kind_str() == "unary" {
|
|
196
|
-
return node
|
|
197
|
-
.
|
|
198
|
-
|
|
207
|
+
return node.field("operator").is_some_and(|operator| {
|
|
208
|
+
match context.source.node_text(operator) {
|
|
209
|
+
"not" => true,
|
|
210
|
+
"defined?" => node
|
|
211
|
+
.field("operand")
|
|
212
|
+
.is_some_and(|operand| !context.source.node_text(operand).starts_with('(')),
|
|
213
|
+
_ => false,
|
|
214
|
+
}
|
|
215
|
+
});
|
|
199
216
|
}
|
|
200
217
|
// `node.arguments? && !node.parenthesized_call?`: a keyword whose arguments are already in
|
|
201
218
|
// parentheses takes nothing more from what follows, so it needs no parentheses of its own.
|
|
@@ -204,7 +221,7 @@ fn requires_parentheses(context: &RuleContext<'_>, node: Node<'_>) -> bool {
|
|
|
204
221
|
&& node
|
|
205
222
|
.child(1)
|
|
206
223
|
.is_some_and(|arguments| !context.source.node_text(arguments).starts_with('('))
|
|
207
|
-
&& !super::nodes::
|
|
224
|
+
&& !super::nodes::children_in(node, context).is_empty()
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
/// `IfThenCorrector`: the same conditional written over several lines.
|
|
@@ -246,7 +263,7 @@ fn expanded(context: &RuleContext<'_>, node: Node<'_>, width: usize, column: usi
|
|
|
246
263
|
|
|
247
264
|
/// The source of everything a `then` or `else` clause holds.
|
|
248
265
|
fn clause_source(context: &RuleContext<'_>, clause: Node<'_>) -> String {
|
|
249
|
-
let written = super::nodes::
|
|
266
|
+
let written = super::nodes::children_in(clause, context);
|
|
250
267
|
match (written.first(), written.last()) {
|
|
251
268
|
(Some(first), Some(last)) => context
|
|
252
269
|
.source
|
|
@@ -127,6 +127,11 @@ fn has_first_child(node: Node<'_>, locals: &LocalVariables<'_, '_>) -> bool {
|
|
|
127
127
|
// A bare name is an `lvar` when it names a variable and a receiverless `send` otherwise.
|
|
128
128
|
"identifier" => locals.is_lvar(node),
|
|
129
129
|
"call" => node.field("receiver").is_some(),
|
|
130
|
+
// `(const nil :CONST)`: the namespace is the first child, and a plain constant has none.
|
|
131
|
+
// `::Foo` and `Foo::Bar` both carry one, which the grammar spells as a `scope_resolution`.
|
|
132
|
+
"constant" => false,
|
|
133
|
+
// An empty literal has no children either.
|
|
134
|
+
"array" | "hash" | "string_array" | "symbol_array" => node.named_child_count() > 0,
|
|
130
135
|
"nil" | "true" | "false" | "self" | "super" => false,
|
|
131
136
|
_ => true,
|
|
132
137
|
}
|
|
@@ -14,7 +14,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
14
14
|
let Some(parameters) = node.field("parameters") else {
|
|
15
15
|
continue;
|
|
16
16
|
};
|
|
17
|
-
let arguments = super::nodes::
|
|
17
|
+
let arguments = super::nodes::children_in(parameters, context);
|
|
18
18
|
let optional: Vec<usize> = positions(&arguments, "optional_parameter");
|
|
19
19
|
// `arg_type?`: only a plain argument counts. A destructuring `(a, b)` is an `mlhs`
|
|
20
20
|
// upstream, and a keyword or splat argument is a type of its own.
|
|
@@ -72,7 +72,7 @@ pub(super) fn check(context: &RuleContext<'_>, offenses: &mut Vec<Offense>) {
|
|
|
72
72
|
if empty_if
|
|
73
73
|
&& node
|
|
74
74
|
.field("consequence")
|
|
75
|
-
.is_some_and(|then| !super::nodes::
|
|
75
|
+
.is_some_and(|then| !super::nodes::children_in(then, context).is_empty())
|
|
76
76
|
{
|
|
77
77
|
continue;
|
|
78
78
|
}
|