rubocop 1.69.2 → 1.73.1
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/LICENSE.txt +1 -1
 - data/README.md +4 -4
 - data/config/default.yml +90 -13
 - data/config/internal_affairs.yml +16 -0
 - data/lib/rubocop/cli/command/execute_runner.rb +3 -3
 - data/lib/rubocop/cli/command/show_cops.rb +24 -2
 - data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
 - data/lib/rubocop/comment_config.rb +2 -2
 - data/lib/rubocop/config.rb +17 -4
 - data/lib/rubocop/config_loader.rb +48 -8
 - data/lib/rubocop/config_loader_resolver.rb +35 -10
 - data/lib/rubocop/config_validator.rb +19 -9
 - data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
 - data/lib/rubocop/cop/base.rb +6 -0
 - data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
 - data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
 - data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
 - data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
 - data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
 - data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
 - data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
 - data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
 - data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
 - data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
 - data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +229 -0
 - data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
 - data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
 - data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
 - data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
 - data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +3 -1
 - data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
 - data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
 - data/lib/rubocop/cop/internal_affairs.rb +5 -16
 - data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -1
 - data/lib/rubocop/cop/layout/argument_alignment.rb +2 -8
 - data/lib/rubocop/cop/layout/block_alignment.rb +3 -1
 - data/lib/rubocop/cop/layout/class_structure.rb +9 -9
 - data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
 - data/lib/rubocop/cop/layout/dot_position.rb +1 -1
 - data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
 - data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
 - data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
 - data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +27 -1
 - data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -1
 - data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
 - data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
 - data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
 - data/lib/rubocop/cop/layout/first_argument_indentation.rb +3 -8
 - data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
 - data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
 - data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
 - data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
 - data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
 - data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -1
 - data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
 - data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
 - data/lib/rubocop/cop/layout/line_length.rb +4 -3
 - data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
 - data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
 - data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -0
 - data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +4 -4
 - data/lib/rubocop/cop/layout/redundant_line_break.rb +7 -6
 - data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
 - data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
 - data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
 - data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
 - data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
 - data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
 - data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -0
 - data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
 - data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
 - data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
 - data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
 - data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
 - data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
 - data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
 - data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
 - data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +1 -1
 - data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
 - data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
 - data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
 - data/lib/rubocop/cop/lint/debugger.rb +1 -1
 - data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
 - data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
 - data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
 - data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
 - data/lib/rubocop/cop/lint/empty_conditional_body.rb +10 -5
 - data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
 - data/lib/rubocop/cop/lint/float_comparison.rb +6 -8
 - data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
 - data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +2 -2
 - data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
 - data/lib/rubocop/cop/lint/literal_as_condition.rb +99 -9
 - data/lib/rubocop/cop/lint/literal_in_interpolation.rb +24 -6
 - data/lib/rubocop/cop/lint/missing_super.rb +2 -2
 - data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -3
 - data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
 - data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -4
 - data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
 - data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -3
 - data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
 - data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +18 -31
 - data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -1
 - data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -5
 - data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
 - data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
 - data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
 - data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
 - data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
 - data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -1
 - data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
 - data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
 - data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
 - data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
 - data/lib/rubocop/cop/lint/syntax.rb +4 -1
 - data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
 - data/lib/rubocop/cop/lint/unexpected_block_arity.rb +1 -1
 - data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
 - data/lib/rubocop/cop/lint/unreachable_code.rb +1 -1
 - data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
 - data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
 - data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
 - data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
 - data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
 - data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
 - data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
 - data/lib/rubocop/cop/lint/void.rb +11 -9
 - data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
 - data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
 - data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
 - data/lib/rubocop/cop/metrics/method_length.rb +8 -1
 - data/lib/rubocop/cop/metrics/module_length.rb +1 -1
 - data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
 - data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
 - data/lib/rubocop/cop/mixin/alignment.rb +2 -2
 - data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
 - data/lib/rubocop/cop/mixin/check_line_breakable.rb +11 -11
 - data/lib/rubocop/cop/mixin/comments_help.rb +4 -2
 - data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
 - data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
 - data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +22 -22
 - data/lib/rubocop/cop/mixin/hash_subset.rb +203 -0
 - data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
 - data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
 - data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
 - data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +48 -24
 - data/lib/rubocop/cop/mixin/range_help.rb +3 -3
 - data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
 - data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -3
 - data/lib/rubocop/cop/mixin/string_help.rb +2 -2
 - data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
 - data/lib/rubocop/cop/mixin/trailing_comma.rb +15 -3
 - data/lib/rubocop/cop/naming/block_forwarding.rb +19 -15
 - data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
 - data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -3
 - data/lib/rubocop/cop/naming/variable_name.rb +64 -6
 - data/lib/rubocop/cop/security/compound_hash.rb +1 -0
 - data/lib/rubocop/cop/style/access_modifier_declarations.rb +34 -5
 - data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
 - data/lib/rubocop/cop/style/and_or.rb +1 -1
 - data/lib/rubocop/cop/style/arguments_forwarding.rb +39 -23
 - data/lib/rubocop/cop/style/array_first_last.rb +18 -2
 - data/lib/rubocop/cop/style/block_delimiters.rb +7 -20
 - data/lib/rubocop/cop/style/class_and_module_children.rb +6 -3
 - data/lib/rubocop/cop/style/collection_methods.rb +1 -1
 - data/lib/rubocop/cop/style/combinable_defined.rb +1 -1
 - data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
 - data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
 - data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
 - data/lib/rubocop/cop/style/conditional_assignment.rb +6 -4
 - data/lib/rubocop/cop/style/documentation.rb +1 -1
 - data/lib/rubocop/cop/style/double_negation.rb +3 -3
 - data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
 - data/lib/rubocop/cop/style/each_with_object.rb +2 -3
 - data/lib/rubocop/cop/style/empty_else.rb +4 -2
 - data/lib/rubocop/cop/style/empty_literal.rb +1 -1
 - data/lib/rubocop/cop/style/empty_method.rb +1 -1
 - data/lib/rubocop/cop/style/endless_method.rb +163 -18
 - data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
 - data/lib/rubocop/cop/style/exact_regexp_match.rb +3 -10
 - data/lib/rubocop/cop/style/explicit_block_argument.rb +15 -2
 - data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
 - data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
 - data/lib/rubocop/cop/style/float_division.rb +8 -4
 - data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
 - data/lib/rubocop/cop/style/hash_each_methods.rb +3 -6
 - data/lib/rubocop/cop/style/hash_except.rb +24 -148
 - data/lib/rubocop/cop/style/hash_slice.rb +80 -0
 - data/lib/rubocop/cop/style/hash_syntax.rb +6 -3
 - data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
 - data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
 - data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
 - data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
 - data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
 - data/lib/rubocop/cop/style/inverse_methods.rb +6 -6
 - data/lib/rubocop/cop/style/it_assignment.rb +36 -0
 - data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
 - data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
 - data/lib/rubocop/cop/style/map_into_array.rb +1 -1
 - data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
 - data/lib/rubocop/cop/style/map_to_set.rb +3 -2
 - data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +19 -12
 - data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
 - data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -1
 - data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -4
 - data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
 - data/lib/rubocop/cop/style/missing_else.rb +2 -0
 - data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
 - data/lib/rubocop/cop/style/multiple_comparison.rb +26 -20
 - data/lib/rubocop/cop/style/mutable_constant.rb +3 -3
 - data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -1
 - data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
 - data/lib/rubocop/cop/style/object_then.rb +13 -15
 - data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
 - data/lib/rubocop/cop/style/parallel_assignment.rb +1 -5
 - data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
 - data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
 - data/lib/rubocop/cop/style/proc.rb +1 -2
 - data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
 - data/lib/rubocop/cop/style/raise_args.rb +6 -4
 - data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
 - data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
 - data/lib/rubocop/cop/style/redundant_condition.rb +48 -2
 - data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
 - data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
 - data/lib/rubocop/cop/style/redundant_each.rb +1 -1
 - data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
 - data/lib/rubocop/cop/style/redundant_format.rb +250 -0
 - data/lib/rubocop/cop/style/redundant_freeze.rb +2 -2
 - data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
 - data/lib/rubocop/cop/style/redundant_line_continuation.rb +34 -13
 - data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -14
 - data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
 - data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
 - data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
 - data/lib/rubocop/cop/style/redundant_self_assignment.rb +14 -28
 - data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
 - data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
 - data/lib/rubocop/cop/style/return_nil.rb +1 -1
 - data/lib/rubocop/cop/style/safe_navigation.rb +2 -2
 - data/lib/rubocop/cop/style/semicolon.rb +1 -1
 - data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
 - data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
 - data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
 - data/lib/rubocop/cop/style/single_line_methods.rb +6 -7
 - data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
 - data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
 - data/lib/rubocop/cop/style/string_concatenation.rb +2 -2
 - data/lib/rubocop/cop/style/string_literals.rb +1 -1
 - data/lib/rubocop/cop/style/string_methods.rb +1 -1
 - data/lib/rubocop/cop/style/super_arguments.rb +65 -17
 - data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
 - data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
 - data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
 - data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
 - data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
 - data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
 - data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
 - data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
 - data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
 - data/lib/rubocop/cop/util.rb +12 -5
 - data/lib/rubocop/cop/utils/format_string.rb +7 -5
 - data/lib/rubocop/cop/variable_force/variable.rb +14 -2
 - data/lib/rubocop/cop/variable_force/variable_table.rb +3 -3
 - data/lib/rubocop/cops_documentation_generator.rb +25 -14
 - data/lib/rubocop/directive_comment.rb +44 -10
 - data/lib/rubocop/formatter/formatter_set.rb +1 -1
 - data/lib/rubocop/lsp/diagnostic.rb +189 -0
 - data/lib/rubocop/lsp/logger.rb +2 -2
 - data/lib/rubocop/lsp/routes.rb +7 -23
 - data/lib/rubocop/lsp/runtime.rb +17 -49
 - data/lib/rubocop/lsp/server.rb +0 -2
 - data/lib/rubocop/lsp/stdin_runner.rb +83 -0
 - data/lib/rubocop/options.rb +28 -12
 - data/lib/rubocop/path_util.rb +15 -8
 - data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
 - data/lib/rubocop/plugin/load_error.rb +26 -0
 - data/lib/rubocop/plugin/loader.rb +100 -0
 - data/lib/rubocop/plugin/not_supported_error.rb +29 -0
 - data/lib/rubocop/plugin.rb +46 -0
 - data/lib/rubocop/rake_task.rb +4 -1
 - data/lib/rubocop/result_cache.rb +13 -13
 - data/lib/rubocop/rspec/cop_helper.rb +9 -0
 - data/lib/rubocop/rspec/expect_offense.rb +6 -2
 - data/lib/rubocop/rspec/shared_contexts.rb +19 -1
 - data/lib/rubocop/rspec/support.rb +2 -2
 - data/lib/rubocop/runner.rb +5 -6
 - data/lib/rubocop/server/cache.rb +35 -2
 - data/lib/rubocop/server/cli.rb +2 -2
 - data/lib/rubocop/target_finder.rb +1 -0
 - data/lib/rubocop/target_ruby.rb +15 -0
 - data/lib/rubocop/version.rb +17 -2
 - data/lib/rubocop.rb +11 -1
 - data/lib/ruby_lsp/rubocop/addon.rb +75 -0
 - data/lib/ruby_lsp/rubocop/runtime_adapter.rb +47 -0
 - metadata +53 -16
 - data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
 - data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
 
| 
         @@ -2,16 +2,34 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'pathname'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'yaml'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'plugin'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            module RuboCop
         
     | 
| 
       7 
8 
     | 
    
         
             
              # A help class for ConfigLoader that handles configuration resolution.
         
     | 
| 
       8 
9 
     | 
    
         
             
              # @api private
         
     | 
| 
       9 
10 
     | 
    
         
             
              class ConfigLoaderResolver # rubocop:disable Metrics/ClassLength
         
     | 
| 
      
 11 
     | 
    
         
            +
                def resolve_plugins(rubocop_config, plugins)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  return if (plugins = Array(plugins)).empty?
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  Plugin.integrate_plugins(rubocop_config, plugins)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       10 
17 
     | 
    
         
             
                def resolve_requires(path, hash)
         
     | 
| 
       11 
18 
     | 
    
         
             
                  config_dir = File.dirname(path)
         
     | 
| 
       12 
19 
     | 
    
         
             
                  hash.delete('require').tap do |loaded_features|
         
     | 
| 
       13 
20 
     | 
    
         
             
                    Array(loaded_features).each do |feature|
         
     | 
| 
       14 
     | 
    
         
            -
                       
     | 
| 
      
 21 
     | 
    
         
            +
                      if Plugin.plugin_capable?(feature)
         
     | 
| 
      
 22 
     | 
    
         
            +
                        # NOTE: Compatibility for before plugins style.
         
     | 
| 
      
 23 
     | 
    
         
            +
                        warn Rainbow(<<~MESSAGE).yellow
         
     | 
| 
      
 24 
     | 
    
         
            +
                          #{feature} extension supports plugin, specify `plugins: #{feature}` instead of `require: #{feature}` in #{path}.
         
     | 
| 
      
 25 
     | 
    
         
            +
                          For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
         
     | 
| 
      
 26 
     | 
    
         
            +
                        MESSAGE
         
     | 
| 
      
 27 
     | 
    
         
            +
                        rubocop_config = Config.create(hash, path, check: false)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                        resolve_plugins(rubocop_config, feature)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      else
         
     | 
| 
      
 31 
     | 
    
         
            +
                        FeatureLoader.load(config_directory_path: config_dir, feature: feature)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
       15 
33 
     | 
    
         
             
                    end
         
     | 
| 
       16 
34 
     | 
    
         
             
                  end
         
     | 
| 
       17 
35 
     | 
    
         
             
                end
         
     | 
| 
         @@ -105,7 +123,7 @@ module RuboCop 
     | 
|
| 
       105 
123 
     | 
    
         
             
                    elsif merge_hashes?(base_hash, derived_hash, key)
         
     | 
| 
       106 
124 
     | 
    
         
             
                      result[key] = merge(base_hash[key], derived_hash[key], **opts)
         
     | 
| 
       107 
125 
     | 
    
         
             
                    elsif should_union?(derived_hash, base_hash, opts[:inherit_mode], key)
         
     | 
| 
       108 
     | 
    
         
            -
                      result[key] = base_hash[key] | derived_hash[key]
         
     | 
| 
      
 126 
     | 
    
         
            +
                      result[key] = Array(base_hash[key]) | Array(derived_hash[key])
         
     | 
| 
       109 
127 
     | 
    
         
             
                    elsif opts[:debug]
         
     | 
| 
       110 
128 
     | 
    
         
             
                      warn_on_duplicate_setting(base_hash, derived_hash, key, **opts)
         
     | 
| 
       111 
129 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -157,20 +175,27 @@ module RuboCop 
     | 
|
| 
       157 
175 
     | 
    
         
             
                  return false if inherited_file.nil? # Not inheritance resolving merge
         
     | 
| 
       158 
176 
     | 
    
         
             
                  return false if inherited_file.start_with?('..') # Legitimate override
         
     | 
| 
       159 
177 
     | 
    
         
             
                  return false if base_hash[key] == derived_hash[key] # Same value
         
     | 
| 
       160 
     | 
    
         
            -
                  return false if remote_file?(inherited_file) # Can't change
         
     | 
| 
      
 178 
     | 
    
         
            +
                  return false if PathUtil.remote_file?(inherited_file) # Can't change
         
     | 
| 
       161 
179 
     | 
    
         | 
| 
       162 
180 
     | 
    
         
             
                  Gem.path.none? { |dir| inherited_file.start_with?(dir) } # Can change?
         
     | 
| 
       163 
181 
     | 
    
         
             
                end
         
     | 
| 
       164 
182 
     | 
    
         | 
| 
       165 
183 
     | 
    
         
             
                def warn_on_duplicate_setting(base_hash, derived_hash, key, **opts)
         
     | 
| 
      
 184 
     | 
    
         
            +
                  # If the file being considered is remote, don't bother checking for duplicates
         
     | 
| 
      
 185 
     | 
    
         
            +
                  return if remote_config?(opts[:file])
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
       166 
187 
     | 
    
         
             
                  return unless duplicate_setting?(base_hash, derived_hash, key, opts[:inherited_file])
         
     | 
| 
       167 
188 
     | 
    
         | 
| 
       168 
189 
     | 
    
         
             
                  inherit_mode = opts[:inherit_mode]['merge'] || opts[:inherit_mode]['override']
         
     | 
| 
       169 
190 
     | 
    
         
             
                  return if base_hash[key].is_a?(Array) && inherit_mode&.include?(key)
         
     | 
| 
       170 
191 
     | 
    
         | 
| 
       171 
     | 
    
         
            -
                  puts  
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
      
 192 
     | 
    
         
            +
                  puts duplicate_setting_warning(opts, key)
         
     | 
| 
      
 193 
     | 
    
         
            +
                end
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                def duplicate_setting_warning(opts, key)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  "#{PathUtil.smart_path(opts[:file])}: " \
         
     | 
| 
      
 197 
     | 
    
         
            +
                    "#{opts[:cop_name]}:#{key} overrides " \
         
     | 
| 
      
 198 
     | 
    
         
            +
                    "the same parameter in #{opts[:inherited_file]}"
         
     | 
| 
       174 
199 
     | 
    
         
             
                end
         
     | 
| 
       175 
200 
     | 
    
         | 
| 
       176 
201 
     | 
    
         
             
                def determine_inherit_mode(hash, key)
         
     | 
| 
         @@ -180,7 +205,7 @@ module RuboCop 
     | 
|
| 
       180 
205 
     | 
    
         
             
                end
         
     | 
| 
       181 
206 
     | 
    
         | 
| 
       182 
207 
     | 
    
         
             
                def should_union?(derived_hash, base_hash, root_mode, key)
         
     | 
| 
       183 
     | 
    
         
            -
                  return false unless base_hash[key].is_a?(Array)
         
     | 
| 
      
 208 
     | 
    
         
            +
                  return false unless base_hash[key].is_a?(Array) || derived_hash[key].is_a?(Array)
         
     | 
| 
       184 
209 
     | 
    
         | 
| 
       185 
210 
     | 
    
         
             
                  derived_mode = derived_hash['inherit_mode']
         
     | 
| 
       186 
211 
     | 
    
         
             
                  return false if should_override?(derived_mode, key)
         
     | 
| 
         @@ -218,7 +243,7 @@ module RuboCop 
     | 
|
| 
       218 
243 
     | 
    
         
             
                end
         
     | 
| 
       219 
244 
     | 
    
         | 
| 
       220 
245 
     | 
    
         
             
                def inherited_file(path, inherit_from, file)
         
     | 
| 
       221 
     | 
    
         
            -
                  if remote_file?(inherit_from)
         
     | 
| 
      
 246 
     | 
    
         
            +
                  if PathUtil.remote_file?(inherit_from)
         
     | 
| 
       222 
247 
     | 
    
         
             
                    # A remote configuration, e.g. `inherit_from: http://example.com/rubocop.yml`.
         
     | 
| 
       223 
248 
     | 
    
         
             
                    RemoteConfig.new(inherit_from, File.dirname(path))
         
     | 
| 
       224 
249 
     | 
    
         
             
                  elsif Pathname.new(inherit_from).absolute?
         
     | 
| 
         @@ -238,8 +263,8 @@ module RuboCop 
     | 
|
| 
       238 
263 
     | 
    
         
             
                  end
         
     | 
| 
       239 
264 
     | 
    
         
             
                end
         
     | 
| 
       240 
265 
     | 
    
         | 
| 
       241 
     | 
    
         
            -
                def  
     | 
| 
       242 
     | 
    
         
            -
                   
     | 
| 
      
 266 
     | 
    
         
            +
                def remote_config?(file)
         
     | 
| 
      
 267 
     | 
    
         
            +
                  file.is_a?(RemoteConfig)
         
     | 
| 
       243 
268 
     | 
    
         
             
                end
         
     | 
| 
       244 
269 
     | 
    
         | 
| 
       245 
270 
     | 
    
         
             
                def handle_disabled_by_default(config, new_default_configuration)
         
     | 
| 
         @@ -3,12 +3,13 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module RuboCop
         
     | 
| 
       4 
4 
     | 
    
         
             
              # Handles validation of configuration, for example cop names, parameter
         
     | 
| 
       5 
5 
     | 
    
         
             
              # names, and Ruby versions.
         
     | 
| 
      
 6 
     | 
    
         
            +
              # rubocop:disable Metrics/ClassLength
         
     | 
| 
       6 
7 
     | 
    
         
             
              class ConfigValidator
         
     | 
| 
       7 
8 
     | 
    
         
             
                extend SimpleForwardable
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
                # @api private
         
     | 
| 
       10 
11 
     | 
    
         
             
                COMMON_PARAMS = %w[Exclude Include Severity inherit_mode AutoCorrect StyleGuide Details
         
     | 
| 
       11 
     | 
    
         
            -
                                   Enabled].freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
                                   Enabled Reference].freeze
         
     | 
| 
       12 
13 
     | 
    
         
             
                # @api private
         
     | 
| 
       13 
14 
     | 
    
         
             
                INTERNAL_PARAMS = %w[Description StyleGuide
         
     | 
| 
       14 
15 
     | 
    
         
             
                                     VersionAdded VersionChanged VersionRemoved
         
     | 
| 
         @@ -41,8 +42,9 @@ module RuboCop 
     | 
|
| 
       41 
42 
     | 
    
         
             
                    ConfigLoader.default_configuration.key?(key)
         
     | 
| 
       42 
43 
     | 
    
         
             
                  end
         
     | 
| 
       43 
44 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
      
 45 
     | 
    
         
            +
                  validate_parameter_shape(valid_cop_names)
         
     | 
| 
       45 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
                  check_obsoletions
         
     | 
| 
       46 
48 
     | 
    
         
             
                  alert_about_unrecognized_cops(invalid_cop_names)
         
     | 
| 
       47 
49 
     | 
    
         
             
                  validate_new_cops_parameter
         
     | 
| 
       48 
50 
     | 
    
         
             
                  validate_parameter_names(valid_cop_names)
         
     | 
| 
         @@ -64,12 +66,6 @@ module RuboCop 
     | 
|
| 
       64 
66 
     | 
    
         
             
                  target_ruby.version
         
     | 
| 
       65 
67 
     | 
    
         
             
                end
         
     | 
| 
       66 
68 
     | 
    
         | 
| 
       67 
     | 
    
         
            -
                def validate_section_presence(name)
         
     | 
| 
       68 
     | 
    
         
            -
                  return unless @config.key?(name) && @config[name].nil?
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                  raise ValidationError, "empty section #{name} found in #{smart_loaded_path}"
         
     | 
| 
       71 
     | 
    
         
            -
                end
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
69 
     | 
    
         
             
                private
         
     | 
| 
       74 
70 
     | 
    
         | 
| 
       75 
71 
     | 
    
         
             
                attr_reader :target_ruby
         
     | 
| 
         @@ -177,9 +173,22 @@ module RuboCop 
     | 
|
| 
       177 
173 
     | 
    
         
             
                  raise ValidationError, message
         
     | 
| 
       178 
174 
     | 
    
         
             
                end
         
     | 
| 
       179 
175 
     | 
    
         | 
| 
      
 176 
     | 
    
         
            +
                def validate_parameter_shape(valid_cop_names)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  valid_cop_names.each do |name|
         
     | 
| 
      
 178 
     | 
    
         
            +
                    if @config[name].nil?
         
     | 
| 
      
 179 
     | 
    
         
            +
                      raise ValidationError, "empty section #{name.inspect} found in #{smart_loaded_path}"
         
     | 
| 
      
 180 
     | 
    
         
            +
                    elsif !@config[name].is_a?(Hash)
         
     | 
| 
      
 181 
     | 
    
         
            +
                      raise ValidationError, <<~MESSAGE
         
     | 
| 
      
 182 
     | 
    
         
            +
                        The configuration for #{name.inspect} in #{smart_loaded_path} is not a Hash.
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                        Found: #{@config[name].inspect}
         
     | 
| 
      
 185 
     | 
    
         
            +
                      MESSAGE
         
     | 
| 
      
 186 
     | 
    
         
            +
                    end
         
     | 
| 
      
 187 
     | 
    
         
            +
                  end
         
     | 
| 
      
 188 
     | 
    
         
            +
                end
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
       180 
190 
     | 
    
         
             
                def validate_parameter_names(valid_cop_names)
         
     | 
| 
       181 
191 
     | 
    
         
             
                  valid_cop_names.each do |name|
         
     | 
| 
       182 
     | 
    
         
            -
                    validate_section_presence(name)
         
     | 
| 
       183 
192 
     | 
    
         
             
                    each_invalid_parameter(name) do |param, supported_params|
         
     | 
| 
       184 
193 
     | 
    
         
             
                      warn Rainbow(<<~MESSAGE).yellow
         
     | 
| 
       185 
194 
     | 
    
         
             
                        Warning: #{name} does not support #{param} parameter.
         
     | 
| 
         @@ -277,4 +286,5 @@ module RuboCop 
     | 
|
| 
       277 
286 
     | 
    
         
             
                    "is supposed to be #{supposed_values} and #{Rainbow(value).yellow} is not."
         
     | 
| 
       278 
287 
     | 
    
         
             
                end
         
     | 
| 
       279 
288 
     | 
    
         
             
              end
         
     | 
| 
      
 289 
     | 
    
         
            +
              # rubocop:enable Metrics/ClassLength
         
     | 
| 
       280 
290 
     | 
    
         
             
            end
         
     | 
| 
         @@ -102,7 +102,7 @@ module RuboCop 
     | 
|
| 
       102 
102 
     | 
    
         
             
                  end
         
     | 
| 
       103 
103 
     | 
    
         | 
| 
       104 
104 
     | 
    
         
             
                  def string_continuation?(node)
         
     | 
| 
       105 
     | 
    
         
            -
                     
     | 
| 
      
 105 
     | 
    
         
            +
                    node.type?(:str, :dstr, :xstr) && node.source.match?(/\\\s*$/)
         
     | 
| 
       106 
106 
     | 
    
         
             
                  end
         
     | 
| 
       107 
107 
     | 
    
         | 
| 
       108 
108 
     | 
    
         
             
                  def multiline_string?(node)
         
     | 
    
        data/lib/rubocop/cop/base.rb
    CHANGED
    
    | 
         @@ -261,6 +261,12 @@ module RuboCop 
     | 
|
| 
       261 
261 
     | 
    
         
             
                    @config.target_ruby_version
         
     | 
| 
       262 
262 
     | 
    
         
             
                  end
         
     | 
| 
       263 
263 
     | 
    
         | 
| 
      
 264 
     | 
    
         
            +
                  # Returns a gems locked versions (i.e. from Gemfile.lock or gems.locked)
         
     | 
| 
      
 265 
     | 
    
         
            +
                  # @returns [Gem::Version | nil] The locked gem version, or nil if the gem is not present.
         
     | 
| 
      
 266 
     | 
    
         
            +
                  def target_gem_version(gem_name)
         
     | 
| 
      
 267 
     | 
    
         
            +
                    @config.gem_versions_in_target && @config.gem_versions_in_target[gem_name]
         
     | 
| 
      
 268 
     | 
    
         
            +
                  end
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
       264 
270 
     | 
    
         
             
                  def parser_engine
         
     | 
| 
       265 
271 
     | 
    
         
             
                    @config.parser_engine
         
     | 
| 
       266 
272 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -66,7 +66,7 @@ module RuboCop 
     | 
|
| 
       66 
66 
     | 
    
         | 
| 
       67 
67 
     | 
    
         
             
                    def conditional_declaration?(nodes)
         
     | 
| 
       68 
68 
     | 
    
         
             
                      parent = nodes[0].each_ancestor.find { |ancestor| !ancestor.begin_type? }
         
     | 
| 
       69 
     | 
    
         
            -
                      return false unless parent&. 
     | 
| 
      
 69 
     | 
    
         
            +
                      return false unless parent&.type?(:if, :when)
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                      root_conditional_node = parent.if_type? ? parent : parent.parent
         
     | 
| 
       72 
72 
     | 
    
         
             
                      nodes.all? { |node| within_conditional?(node, root_conditional_node) }
         
     | 
| 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module InternalAffairs
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Use `config.cop_enabled?('Department/CopName')` instead of
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # traversing the config hash.
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #   # `for_cop(...)['Enabled']
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #   config.for_cop('Department/CopName')['Enabled']
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #   config.cop_enabled?('Department/CopName')
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #   # when keeping a cop's config in a local and then checking the `Enabled` key
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   cop_config = config.for_cop('Department/CopName')
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   cop_config['Enabled'] && cop_config['Foo']
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   config.for_enabled_cop('Department/CopName')['Foo']
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  class CopEnabled < Base
         
     | 
| 
      
 29 
     | 
    
         
            +
                    extend AutoCorrector
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    MSG = 'Use `%<replacement>s` instead of `%<source>s`.'
         
     | 
| 
      
 32 
     | 
    
         
            +
                    MSG_HASH = 'Consider replacing uses of `%<hash_name>s` with `config.for_enabled_cop`.'
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    RESTRICT_ON_SEND = [:[]].freeze
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    # @!method for_cop_enabled?(node)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    def_node_matcher :for_cop_enabled?, <<~PATTERN
         
     | 
| 
      
 38 
     | 
    
         
            +
                      (send
         
     | 
| 
      
 39 
     | 
    
         
            +
                        (send
         
     | 
| 
      
 40 
     | 
    
         
            +
                          ${(send nil? :config) (ivar :@config)} :for_cop
         
     | 
| 
      
 41 
     | 
    
         
            +
                          $(str _)) :[]
         
     | 
| 
      
 42 
     | 
    
         
            +
                        (str "Enabled"))
         
     | 
| 
      
 43 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    # @!method config_enabled_lookup?(node)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    def_node_matcher :config_enabled_lookup?, <<~PATTERN
         
     | 
| 
      
 47 
     | 
    
         
            +
                      (send
         
     | 
| 
      
 48 
     | 
    
         
            +
                        {(lvar $_) (ivar $_) (send nil? $_)} :[]
         
     | 
| 
      
 49 
     | 
    
         
            +
                        (str "Enabled"))
         
     | 
| 
      
 50 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def on_send(node)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      if (config_var, cop_name = for_cop_enabled?(node))
         
     | 
| 
      
 54 
     | 
    
         
            +
                        handle_for_cop(node, config_var, cop_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      elsif (config_var = config_enabled_lookup?(node))
         
     | 
| 
      
 56 
     | 
    
         
            +
                        return unless config_var.end_with?('_config')
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                        handle_hash(node, config_var)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      end
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    private
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    def handle_for_cop(node, config_var, cop_name)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      source = node.source
         
     | 
| 
      
 66 
     | 
    
         
            +
                      quote = cop_name.loc.begin.source
         
     | 
| 
      
 67 
     | 
    
         
            +
                      cop_name = cop_name.value
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      replacement = "#{config_var.source}.cop_enabled?(#{quote}#{cop_name}#{quote})"
         
     | 
| 
      
 70 
     | 
    
         
            +
                      message = format(MSG, source: source, replacement: replacement)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      add_offense(node, message: message) do |corrector|
         
     | 
| 
      
 73 
     | 
    
         
            +
                        corrector.replace(node, replacement)
         
     | 
| 
      
 74 
     | 
    
         
            +
                      end
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def handle_hash(node, config_var)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      message = format(MSG_HASH, hash_name: config_var)
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                      add_offense(node, message: message)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -90,8 +90,10 @@ module RuboCop 
     | 
|
| 
       90 
90 
     | 
    
         
             
                      description_text = string_contents(current_description)
         
     | 
| 
       91 
91 
     | 
    
         
             
                      return unless (new_description = correct_description(description_text, description_map))
         
     | 
| 
       92 
92 
     | 
    
         | 
| 
      
 93 
     | 
    
         
            +
                      quote = current_description.dstr_type? ? '"' : "'"
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
       93 
95 
     | 
    
         
             
                      add_offense(current_description, message: message) do |corrector|
         
     | 
| 
       94 
     | 
    
         
            -
                        corrector.replace(current_description, " 
     | 
| 
      
 96 
     | 
    
         
            +
                        corrector.replace(current_description, "#{quote}#{new_description}#{quote}")
         
     | 
| 
       95 
97 
     | 
    
         
             
                      end
         
     | 
| 
       96 
98 
     | 
    
         
             
                    end
         
     | 
| 
       97 
99 
     | 
    
         | 
| 
         @@ -106,7 +108,7 @@ module RuboCop 
     | 
|
| 
       106 
108 
     | 
    
         
             
                    end
         
     | 
| 
       107 
109 
     | 
    
         | 
| 
       108 
110 
     | 
    
         
             
                    def string_contents(node)
         
     | 
| 
       109 
     | 
    
         
            -
                      node. 
     | 
| 
      
 111 
     | 
    
         
            +
                      node.type?(:str, :dstr) ? node.value : node.source
         
     | 
| 
       110 
112 
     | 
    
         
             
                    end
         
     | 
| 
       111 
113 
     | 
    
         
             
                  end
         
     | 
| 
       112 
114 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,116 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module InternalAffairs
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # When a node location may not exist, `Node#loc?` or `Node#loc_is?`
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # can be used instead of calling `Node#respond_to?` before using
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # the value.
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   node.loc.respond_to?(:begin) && node.loc.begin
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   node.loc?(:begin)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #   node.loc.respond_to?(:begin) && node.loc.begin.is?('(')
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #   node.loc_is?(:begin, '(')
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   node.loc.respond_to?(:begin) && node.loc.begin.source == '('
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   node.loc_is?(:begin, '(')
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  class LocationExists < Base
         
     | 
| 
      
 30 
     | 
    
         
            +
                    extend AutoCorrector
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    MSG = 'Use `%<replacement>s` instead of `%<source>s`.'
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    # @!method replaceable_with_loc_is(node)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    def_node_matcher :replaceable_with_loc_is, <<~PATTERN
         
     | 
| 
      
 36 
     | 
    
         
            +
                      (and
         
     | 
| 
      
 37 
     | 
    
         
            +
                        (call
         
     | 
| 
      
 38 
     | 
    
         
            +
                          (call $_receiver :loc) :respond_to?
         
     | 
| 
      
 39 
     | 
    
         
            +
                          $(sym _location))
         
     | 
| 
      
 40 
     | 
    
         
            +
                        {
         
     | 
| 
      
 41 
     | 
    
         
            +
                          (call
         
     | 
| 
      
 42 
     | 
    
         
            +
                            (call
         
     | 
| 
      
 43 
     | 
    
         
            +
                              (call _receiver :loc) _location) :is?
         
     | 
| 
      
 44 
     | 
    
         
            +
                            $(str _))
         
     | 
| 
      
 45 
     | 
    
         
            +
                          (call
         
     | 
| 
      
 46 
     | 
    
         
            +
                            (call
         
     | 
| 
      
 47 
     | 
    
         
            +
                              (call
         
     | 
| 
      
 48 
     | 
    
         
            +
                                (call _receiver :loc) _location) :source) :==
         
     | 
| 
      
 49 
     | 
    
         
            +
                                $(str _))
         
     | 
| 
      
 50 
     | 
    
         
            +
                        })
         
     | 
| 
      
 51 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    # @!method replaceable_with_loc(node)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    def_node_matcher :replaceable_with_loc, <<~PATTERN
         
     | 
| 
      
 55 
     | 
    
         
            +
                      (and
         
     | 
| 
      
 56 
     | 
    
         
            +
                        (call
         
     | 
| 
      
 57 
     | 
    
         
            +
                          (call $_receiver :loc) :respond_to?
         
     | 
| 
      
 58 
     | 
    
         
            +
                          $(sym _location))
         
     | 
| 
      
 59 
     | 
    
         
            +
                        (call
         
     | 
| 
      
 60 
     | 
    
         
            +
                          (call _receiver :loc) _location))
         
     | 
| 
      
 61 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    def on_and(node)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      replace_with_loc(node) || replace_with_loc_is(node)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    private
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    def replace_with_loc(node)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      replaceable_with_loc(node) do |receiver, location|
         
     | 
| 
      
 71 
     | 
    
         
            +
                        if node.parent&.assignment?
         
     | 
| 
      
 72 
     | 
    
         
            +
                          register_offense(node, replace_assignment(receiver, location))
         
     | 
| 
      
 73 
     | 
    
         
            +
                        else
         
     | 
| 
      
 74 
     | 
    
         
            +
                          register_offense(node, replacement(receiver, "loc?(#{location.source})"))
         
     | 
| 
      
 75 
     | 
    
         
            +
                        end
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    def replace_with_loc_is(node)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      replaceable_with_loc_is(node) do |receiver, location, value|
         
     | 
| 
      
 81 
     | 
    
         
            +
                        replacement = replacement(receiver, "loc_is?(#{location.source}, #{value.source})")
         
     | 
| 
      
 82 
     | 
    
         
            +
                        register_offense(node, replacement)
         
     | 
| 
      
 83 
     | 
    
         
            +
                      end
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    def register_offense(node, replacement)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      message = format(MSG, replacement: replacement, source: node.source)
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                      add_offense(node, message: message) do |corrector|
         
     | 
| 
      
 90 
     | 
    
         
            +
                        corrector.replace(node, replacement)
         
     | 
| 
      
 91 
     | 
    
         
            +
                      end
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    def replacement(receiver, rest)
         
     | 
| 
      
 95 
     | 
    
         
            +
                      "#{replace_receiver(receiver)}#{rest}"
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    def replace_assignment(receiver, location)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      prefix = replace_receiver(receiver)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                      "#{prefix}loc#{dot(receiver)}#{location.value} if #{prefix}loc?(#{location.source})"
         
     | 
| 
      
 102 
     | 
    
         
            +
                    end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    def replace_receiver(receiver)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      return '' unless receiver
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                      "#{receiver.source}#{dot(receiver)}"
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    def dot(node)
         
     | 
| 
      
 111 
     | 
    
         
            +
                      node.parent.loc.dot.source
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
              end
         
     | 
| 
      
 116 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -22,7 +22,7 @@ module RuboCop 
     | 
|
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                    def on_send(node)
         
     | 
| 
       24 
24 
     | 
    
         
             
                      return unless (parent = node.parent)
         
     | 
| 
       25 
     | 
    
         
            -
                      return unless parent. 
     | 
| 
      
 25 
     | 
    
         
            +
                      return unless parent.call_type? && parent.method?(:expression)
         
     | 
| 
       26 
26 
     | 
    
         
             
                      return unless parent.receiver.receiver
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
                      offense = node.loc.selector.join(parent.source_range.end)
         
     | 
| 
         @@ -31,6 +31,7 @@ module RuboCop 
     | 
|
| 
       31 
31 
     | 
    
         
             
                        corrector.replace(offense, 'source_range')
         
     | 
| 
       32 
32 
     | 
    
         
             
                      end
         
     | 
| 
       33 
33 
     | 
    
         
             
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                    alias on_csend on_send
         
     | 
| 
       34 
35 
     | 
    
         
             
                  end
         
     | 
| 
       35 
36 
     | 
    
         
             
                end
         
     | 
| 
       36 
37 
     | 
    
         
             
              end
         
     | 
| 
         @@ -27,8 +27,8 @@ module RuboCop 
     | 
|
| 
       27 
27 
     | 
    
         
             
                    # @!method arguments_first_or_last?(node)
         
     | 
| 
       28 
28 
     | 
    
         
             
                    def_node_matcher :arguments_first_or_last?, <<~PATTERN
         
     | 
| 
       29 
29 
     | 
    
         
             
                      {
         
     | 
| 
       30 
     | 
    
         
            -
                        ( 
     | 
| 
       31 
     | 
    
         
            -
                        ( 
     | 
| 
      
 30 
     | 
    
         
            +
                        (call (call !nil? :arguments) ${:first :last})
         
     | 
| 
      
 31 
     | 
    
         
            +
                        (call (call !nil? :arguments) :[] (int ${0 -1}))
         
     | 
| 
       32 
32 
     | 
    
         
             
                      }
         
     | 
| 
       33 
33 
     | 
    
         
             
                    PATTERN
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
         @@ -47,6 +47,7 @@ module RuboCop 
     | 
|
| 
       47 
47 
     | 
    
         
             
                        end
         
     | 
| 
       48 
48 
     | 
    
         
             
                      end
         
     | 
| 
       49 
49 
     | 
    
         
             
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    alias on_csend on_send
         
     | 
| 
       50 
51 
     | 
    
         
             
                  end
         
     | 
| 
       51 
52 
     | 
    
         
             
                end
         
     | 
| 
       52 
53 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module InternalAffairs
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class NodePatternGroups
         
     | 
| 
      
 7 
     | 
    
         
            +
                    # AST Processor for NodePattern ASTs, for use with `InternalAffairs/NodePatternGroups`.
         
     | 
| 
      
 8 
     | 
    
         
            +
                    #
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # Looks for sequences and subsequences where the first item is a `node_type` node,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # and converts them to `node_sequence` nodes (not a true `Rubocop::AST::NodePattern`
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # node type).
         
     | 
| 
      
 12 
     | 
    
         
            +
                    #
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # The resulting AST will be walked by `InternalAffairs::NodePatternGroups::ASTWalker`
         
     | 
| 
      
 14 
     | 
    
         
            +
                    # in order to find node types in a `union` node that can be rewritten as a node group.
         
     | 
| 
      
 15 
     | 
    
         
            +
                    #
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # NOTE: The `on_*` methods in this class relate not to the normal node types but
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # rather to the Node Pattern node types. Not every node type is handled.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    #
         
     | 
| 
      
 19 
     | 
    
         
            +
                    class ASTProcessor
         
     | 
| 
      
 20 
     | 
    
         
            +
                      include ::AST::Processor::Mixin
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      def handler_missing(node)
         
     | 
| 
      
 23 
     | 
    
         
            +
                        node.updated(nil, process_children(node))
         
     | 
| 
      
 24 
     | 
    
         
            +
                      end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                      # Look for `sequence` and `subsequence` nodes that contain a `node_type` node as
         
     | 
| 
      
 27 
     | 
    
         
            +
                      # their first child. These are rewritten as `node_sequence` nodes so that it is
         
     | 
| 
      
 28 
     | 
    
         
            +
                      # possible to compare nodes while looking for replacement candidates for node groups.
         
     | 
| 
      
 29 
     | 
    
         
            +
                      # This is necessary so that extended patterns can be matched and replaced.
         
     | 
| 
      
 30 
     | 
    
         
            +
                      # ie. `{(send _ :foo ...) (csend _ :foo ...)}` can become `(call _ :foo ...)`
         
     | 
| 
      
 31 
     | 
    
         
            +
                      def on_sequence(node)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        first_child = node.child
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        if first_child.type == :node_type
         
     | 
| 
      
 35 
     | 
    
         
            +
                          children = [first_child.child, *process_children(node, 1..)]
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                          # The `node_sequence` node contains the `node_type` symbol as its first child,
         
     | 
| 
      
 38 
     | 
    
         
            +
                          # followed by all the other nodes contained in the `sequence` node.
         
     | 
| 
      
 39 
     | 
    
         
            +
                          # The location is copied from the sequence, so that the entire sequence can
         
     | 
| 
      
 40 
     | 
    
         
            +
                          # eventually be corrected in the cop.
         
     | 
| 
      
 41 
     | 
    
         
            +
                          n(:node_sequence, children, location: node.location)
         
     | 
| 
      
 42 
     | 
    
         
            +
                        else
         
     | 
| 
      
 43 
     | 
    
         
            +
                          node.updated(nil, process_children(node))
         
     | 
| 
      
 44 
     | 
    
         
            +
                        end
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                      alias on_subsequence on_sequence
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      private
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      def n(type, children = [], properties = {})
         
     | 
| 
      
 51 
     | 
    
         
            +
                        NodePattern::Node.new(type, children, properties)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      def process_children(node, range = 0..-1)
         
     | 
| 
      
 55 
     | 
    
         
            +
                        node.children[range].map do |child|
         
     | 
| 
      
 56 
     | 
    
         
            +
                          child.is_a?(::AST::Node) ? process(child) : child
         
     | 
| 
      
 57 
     | 
    
         
            +
                        end
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module InternalAffairs
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # rubocop:disable InternalAffairs/RedundantSourceRange -- node here is a `NodePattern::Node`
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class NodePatternGroups
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # Walks an AST that has been processed by `InternalAffairs::NodePatternGroups::Processor`
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # in order to find `node_type` and `node_sequence` nodes that can be replaced with a node
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # group in `InternalAffairs/NodePatternGroups`.
         
     | 
| 
      
 11 
     | 
    
         
            +
                    #
         
     | 
| 
      
 12 
     | 
    
         
            +
                    # Calling `ASTWalker#walk` sets `node_groups` with an array of `NodeGroup` structs
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # that contain metadata about nodes that can be replaced, including location data. That
         
     | 
| 
      
 14 
     | 
    
         
            +
                    # metadata is used by the cop to register offenses and perform corrections.
         
     | 
| 
      
 15 
     | 
    
         
            +
                    class ASTWalker
         
     | 
| 
      
 16 
     | 
    
         
            +
                      # Struct to contain data about parts of a node pattern that can be replaced
         
     | 
| 
      
 17 
     | 
    
         
            +
                      NodeGroup = Struct.new(
         
     | 
| 
      
 18 
     | 
    
         
            +
                        :name,             # The name of the node group that will be inserted
         
     | 
| 
      
 19 
     | 
    
         
            +
                        :union,            # The entire `union` node
         
     | 
| 
      
 20 
     | 
    
         
            +
                        :node_types,       # An array of `node_type` nodes that will be removed
         
     | 
| 
      
 21 
     | 
    
         
            +
                        :sequence?,        # The pattern matches a node type with given attributes
         
     | 
| 
      
 22 
     | 
    
         
            +
                        :start_index,      # The index in the union of the first node type to remove
         
     | 
| 
      
 23 
     | 
    
         
            +
                        :offense_range,    # The range to mark an offense on
         
     | 
| 
      
 24 
     | 
    
         
            +
                        :ranges,           # Range of each element to remove, since they may not be adjacent
         
     | 
| 
      
 25 
     | 
    
         
            +
                        :pipe,             # Is the union delimited by pipes?
         
     | 
| 
      
 26 
     | 
    
         
            +
                        :other_elements?,  # Does the union have other elements other than those to remove?
         
     | 
| 
      
 27 
     | 
    
         
            +
                        keyword_init: true
         
     | 
| 
      
 28 
     | 
    
         
            +
                      )
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      def initialize
         
     | 
| 
      
 31 
     | 
    
         
            +
                        reset!
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      def reset!
         
     | 
| 
      
 35 
     | 
    
         
            +
                        @node_groups = []
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      attr_reader :node_groups
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      # Recursively walk the AST in a depth-first manner.
         
     | 
| 
      
 41 
     | 
    
         
            +
                      # Only `union` nodes are handled further.
         
     | 
| 
      
 42 
     | 
    
         
            +
                      def walk(node)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        return if node.nil?
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                        on_union(node) if node.type == :union
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                        node.child_nodes.each do |child|
         
     | 
| 
      
 48 
     | 
    
         
            +
                          walk(child)
         
     | 
| 
      
 49 
     | 
    
         
            +
                        end
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                      # Search `union` nodes for `node_type` and `node_sequence` nodes that can be
         
     | 
| 
      
 53 
     | 
    
         
            +
                      # collapsed into a node group.
         
     | 
| 
      
 54 
     | 
    
         
            +
                      # * `node_type` nodes are nodes with no further configuration (ie. `send`)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      # * `node_sequence` nodes are nodes with further configuration (ie. `(send ...)`)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      #
         
     | 
| 
      
 57 
     | 
    
         
            +
                      # Each group of types that can be collapsed will have a `NodeGroup` record added
         
     | 
| 
      
 58 
     | 
    
         
            +
                      # to `node_groups`, which is then used by the cop.
         
     | 
| 
      
 59 
     | 
    
         
            +
                      def on_union(node)
         
     | 
| 
      
 60 
     | 
    
         
            +
                        all_node_types = each_child_node(node, :node_type, :node_sequence).to_a
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                        each_node_group(all_node_types) do |group_name, node_types|
         
     | 
| 
      
 63 
     | 
    
         
            +
                          next unless sequences_match?(node_types)
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                          node_groups << node_group_data(
         
     | 
| 
      
 66 
     | 
    
         
            +
                            group_name, node, node_types,
         
     | 
| 
      
 67 
     | 
    
         
            +
                            all_node_types.index(node_types.first),
         
     | 
| 
      
 68 
     | 
    
         
            +
                            (node.children - node_types).any?
         
     | 
| 
      
 69 
     | 
    
         
            +
                          )
         
     | 
| 
      
 70 
     | 
    
         
            +
                        end
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                      private
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      def each_child_node(node, *types)
         
     | 
| 
      
 76 
     | 
    
         
            +
                        return to_enum(__method__, node, *types) unless block_given?
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                        node.children.each do |child|
         
     | 
| 
      
 79 
     | 
    
         
            +
                          yield child if types.empty? || types.include?(child.type)
         
     | 
| 
      
 80 
     | 
    
         
            +
                        end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                        self
         
     | 
| 
      
 83 
     | 
    
         
            +
                      end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                      def each_node_group(types_to_check)
         
     | 
| 
      
 86 
     | 
    
         
            +
                        # Find all node groups where all of the members are present in the union
         
     | 
| 
      
 87 
     | 
    
         
            +
                        type_names = types_to_check.map(&:child)
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                        NODE_GROUPS.select { |_, group| group & type_names == group }.each_key do |name|
         
     | 
| 
      
 90 
     | 
    
         
            +
                          nodes = get_relevant_nodes(types_to_check, name)
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                          yield name, nodes
         
     | 
| 
      
 93 
     | 
    
         
            +
                        end
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                      def get_relevant_nodes(node_types, group_name)
         
     | 
| 
      
 97 
     | 
    
         
            +
                        node_types.each_with_object([]) do |node_type, arr|
         
     | 
| 
      
 98 
     | 
    
         
            +
                          next unless NODE_GROUPS[group_name].include?(node_type.child)
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                          arr << node_type
         
     | 
| 
      
 101 
     | 
    
         
            +
                        end
         
     | 
| 
      
 102 
     | 
    
         
            +
                      end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      def node_group_data(name, union, node_types, start_index, other)
         
     | 
| 
      
 105 
     | 
    
         
            +
                        NodeGroup.new(
         
     | 
| 
      
 106 
     | 
    
         
            +
                          name: name,
         
     | 
| 
      
 107 
     | 
    
         
            +
                          union: union,
         
     | 
| 
      
 108 
     | 
    
         
            +
                          node_types: node_types,
         
     | 
| 
      
 109 
     | 
    
         
            +
                          sequence?: node_types.first.type == :node_sequence,
         
     | 
| 
      
 110 
     | 
    
         
            +
                          start_index: start_index,
         
     | 
| 
      
 111 
     | 
    
         
            +
                          pipe: union.source_range.source['|'],
         
     | 
| 
      
 112 
     | 
    
         
            +
                          other_elements?: other
         
     | 
| 
      
 113 
     | 
    
         
            +
                        )
         
     | 
| 
      
 114 
     | 
    
         
            +
                      end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                      def sequences_match?(types)
         
     | 
| 
      
 117 
     | 
    
         
            +
                        # Ensure all given types have the same type and the same sequence
         
     | 
| 
      
 118 
     | 
    
         
            +
                        # ie. `(send ...)` and `(csend ...) is a match
         
     | 
| 
      
 119 
     | 
    
         
            +
                        #     `(send)` and `(csend ...)` is not a match
         
     | 
| 
      
 120 
     | 
    
         
            +
                        #     `send` and `(csend ...)` is not a match
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                        types.each_cons(2).all? do |left, right|
         
     | 
| 
      
 123 
     | 
    
         
            +
                          left.type == right.type && left.children[1..] == right.children[1..]
         
     | 
| 
      
 124 
     | 
    
         
            +
                        end
         
     | 
| 
      
 125 
     | 
    
         
            +
                      end
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  # rubocop:enable InternalAffairs/RedundantSourceRange
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     |