rubocop 1.84.2 → 1.87.0
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/config/default.yml +106 -16
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- data/lib/rubocop/cli/command/mcp.rb +19 -0
- data/lib/rubocop/cli/command/show_cops.rb +2 -2
- data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +9 -7
- data/lib/rubocop/comment_config.rb +12 -15
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader.rb +17 -2
- data/lib/rubocop/config_loader_resolver.rb +13 -4
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +2 -2
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
- data/lib/rubocop/cop/base.rb +8 -2
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors.rb +28 -0
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/exclude_limit.rb +31 -5
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +23 -7
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +8 -5
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +12 -0
- data/lib/rubocop/cop/layout/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
- data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
- data/lib/rubocop/cop/lint/number_conversion.rb +6 -6
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +2 -11
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +3 -3
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +3 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +4 -9
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
- data/lib/rubocop/cop/lint/void.rb +32 -12
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
- data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
- data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
- data/lib/rubocop/cop/mixin.rb +86 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
- data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +8 -0
- data/lib/rubocop/cop/registry.rb +62 -38
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +14 -2
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
- data/lib/rubocop/cop/style/case_equality.rb +4 -0
- data/lib/rubocop/cop/style/character_literal.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +18 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
- data/lib/rubocop/cop/style/copyright.rb +22 -11
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +8 -3
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/file_write.rb +18 -16
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string.rb +4 -3
- data/lib/rubocop/cop/style/format_string_token.rb +29 -2
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
- data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
- data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
- data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
- data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
- data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
- data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
- data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
- data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
- data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
- data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
- data/lib/rubocop/cop/style/select_by_range.rb +197 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +2 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
- data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/team.rb +86 -35
- data/lib/rubocop/cop/variable_force/branch.rb +2 -2
- data/lib/rubocop/directive_comment.rb +2 -1
- data/lib/rubocop/file_patterns.rb +9 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -2
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +10 -3
- data/lib/rubocop/lsp/runtime.rb +1 -2
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +35 -4
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/project_index_loader.rb +66 -0
- data/lib/rubocop/result_cache.rb +22 -10
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +32 -2
- data/lib/rubocop/runner.rb +124 -53
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +14 -7
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +21 -3
- data/lib/rubocop.rb +22 -96
- metadata +27 -5
|
@@ -5,15 +5,26 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Enforces consistent style for empty class definitions.
|
|
7
7
|
#
|
|
8
|
-
# This cop can enforce either a
|
|
8
|
+
# This cop can enforce either a standard class definition or `Class.new`
|
|
9
9
|
# for classes with no body.
|
|
10
10
|
#
|
|
11
11
|
# The supported styles are:
|
|
12
12
|
#
|
|
13
|
-
# *
|
|
13
|
+
# * class_keyword (default) - prefer standard class definition over `Class.new`
|
|
14
14
|
# * class_new - prefer `Class.new` over class definition
|
|
15
15
|
#
|
|
16
|
-
#
|
|
16
|
+
# One difference between the two styles is that the `Class.new` form does not make
|
|
17
|
+
# the subclass name available to the base class's `inherited` callback.
|
|
18
|
+
# For this reason, `EnforcedStyle: class_keyword` is set as the default style.
|
|
19
|
+
# Class definitions without a superclass, which are not involved in inheritance,
|
|
20
|
+
# are not detected. This ensures safe detection regardless of the applied style.
|
|
21
|
+
# This avoids overlapping responsibilities with the `Lint/EmptyClass` cop.
|
|
22
|
+
#
|
|
23
|
+
# Use `AllowedParentClasses` to permit both styles for specific parent classes.
|
|
24
|
+
# For example, adding `StandardError` allows both `Error = Class.new(StandardError)`
|
|
25
|
+
# and `class Error < StandardError; end` regardless of the enforced style.
|
|
26
|
+
#
|
|
27
|
+
# @example EnforcedStyle: class_keyword (default)
|
|
17
28
|
# # bad
|
|
18
29
|
# FooError = Class.new(StandardError)
|
|
19
30
|
#
|
|
@@ -35,33 +46,43 @@ module RuboCop
|
|
|
35
46
|
# # good
|
|
36
47
|
# FooError = Class.new(StandardError)
|
|
37
48
|
#
|
|
49
|
+
# @example AllowedParentClasses: ['StandardError']
|
|
50
|
+
# # good - allowed regardless of EnforcedStyle
|
|
51
|
+
# FooError = Class.new(StandardError)
|
|
52
|
+
#
|
|
53
|
+
# # good - allowed regardless of EnforcedStyle
|
|
54
|
+
# class FooError < StandardError
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
38
57
|
class EmptyClassDefinition < Base
|
|
39
58
|
include ConfigurableEnforcedStyle
|
|
40
|
-
include RangeHelp
|
|
41
59
|
extend AutoCorrector
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
'
|
|
45
|
-
MSG_CLASS_NEW = '
|
|
61
|
+
MSG_CLASS_KEYWORD =
|
|
62
|
+
'Use the `class` keyword instead of `Class.new` to define an empty class.'
|
|
63
|
+
MSG_CLASS_NEW = 'Use `Class.new` instead of the `class` keyword to define an empty class.'
|
|
46
64
|
|
|
47
65
|
# @!method class_new_assignment(node)
|
|
48
66
|
def_node_matcher :class_new_assignment, <<~PATTERN
|
|
49
|
-
(casgn _ _ $(send (const _ :Class) :new
|
|
67
|
+
(casgn _ _ $(send (const _ :Class) :new _))
|
|
50
68
|
PATTERN
|
|
51
69
|
|
|
52
70
|
def on_casgn(node)
|
|
53
|
-
return unless
|
|
71
|
+
return unless %i[class_keyword class_definition].include?(style)
|
|
54
72
|
return unless (class_new_node = class_new_assignment(node))
|
|
55
73
|
return if (arg = class_new_node.first_argument) && !arg.const_type?
|
|
74
|
+
return if allowed_parent_class?(class_new_node.first_argument.source)
|
|
56
75
|
|
|
57
|
-
add_offense(node, message:
|
|
76
|
+
add_offense(node, message: MSG_CLASS_KEYWORD) do |corrector|
|
|
58
77
|
autocorrect_class_new(corrector, node, class_new_node)
|
|
59
78
|
end
|
|
60
79
|
end
|
|
61
80
|
|
|
62
81
|
def on_class(node)
|
|
63
82
|
return unless style == :class_new
|
|
83
|
+
return unless node.parent_class
|
|
64
84
|
return if (body = node.body) && !body.children.empty?
|
|
85
|
+
return if allowed_parent_class?(node.parent_class.source)
|
|
65
86
|
|
|
66
87
|
add_offense(node, message: MSG_CLASS_NEW) do |corrector|
|
|
67
88
|
autocorrect_class_definition(corrector, node)
|
|
@@ -70,25 +91,27 @@ module RuboCop
|
|
|
70
91
|
|
|
71
92
|
private
|
|
72
93
|
|
|
94
|
+
def allowed_parent_class?(parent_class_name)
|
|
95
|
+
allowed_parent_classes.include?(parent_class_name)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def allowed_parent_classes
|
|
99
|
+
cop_config.fetch('AllowedParentClasses', [])
|
|
100
|
+
end
|
|
101
|
+
|
|
73
102
|
def autocorrect_class_new(corrector, node, class_new_node)
|
|
74
103
|
indent = ' ' * node.loc.column
|
|
75
104
|
class_name = node.name
|
|
76
|
-
|
|
77
|
-
parent_class_name = " < #{parent_class.source}"
|
|
78
|
-
end
|
|
105
|
+
parent_class_name = class_new_node.first_argument.source
|
|
79
106
|
|
|
80
|
-
corrector.replace(node, "class #{class_name}#{parent_class_name}\n#{indent}end")
|
|
107
|
+
corrector.replace(node, "class #{class_name} < #{parent_class_name}\n#{indent}end")
|
|
81
108
|
end
|
|
82
109
|
|
|
83
110
|
def autocorrect_class_definition(corrector, node)
|
|
84
|
-
indent = ' ' * node.loc.column
|
|
85
111
|
class_name = node.identifier.source
|
|
86
|
-
|
|
87
|
-
parent_class_name = "(#{parent_class.source})"
|
|
88
|
-
end
|
|
89
|
-
range = range_by_whole_lines(node.source_range, include_final_newline: true)
|
|
112
|
+
parent_class_name = node.parent_class.source
|
|
90
113
|
|
|
91
|
-
corrector.replace(
|
|
114
|
+
corrector.replace(node, "#{class_name} = Class.new(#{parent_class_name})")
|
|
92
115
|
end
|
|
93
116
|
end
|
|
94
117
|
end
|
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
|
23
23
|
|
|
24
24
|
MSG = 'Omit parentheses for the empty lambda parameters.'
|
|
25
25
|
|
|
26
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
26
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
27
27
|
send_node = node.send_node
|
|
28
28
|
return unless send_node.send_type?
|
|
29
29
|
|
|
@@ -3,12 +3,18 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks
|
|
6
|
+
# Checks that source files have no utf-8 encoding comments.
|
|
7
|
+
# Since Ruby 2.0, UTF-8 is the default source encoding, so
|
|
8
|
+
# these comments are no longer necessary and just add noise.
|
|
9
|
+
#
|
|
7
10
|
# @example
|
|
8
11
|
# # bad
|
|
9
12
|
# # encoding: UTF-8
|
|
10
13
|
# # coding: UTF-8
|
|
11
14
|
# # -*- coding: UTF-8 -*-
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# # No encoding comment needed
|
|
12
18
|
class Encoding < Base
|
|
13
19
|
include RangeHelp
|
|
14
20
|
extend AutoCorrector
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for END blocks.
|
|
6
|
+
# Checks for `END` blocks. `END` blocks are Perl-style constructs
|
|
7
|
+
# and `Kernel#at_exit` is the idiomatic Ruby alternative, as it's
|
|
8
|
+
# explicit and can be used anywhere.
|
|
7
9
|
#
|
|
8
10
|
# @example
|
|
9
11
|
# # bad
|
|
@@ -157,6 +157,7 @@ module RuboCop
|
|
|
157
157
|
handle_require_always_style(node)
|
|
158
158
|
end
|
|
159
159
|
end
|
|
160
|
+
alias on_defs on_def
|
|
160
161
|
|
|
161
162
|
private
|
|
162
163
|
|
|
@@ -170,7 +171,7 @@ module RuboCop
|
|
|
170
171
|
end
|
|
171
172
|
|
|
172
173
|
def handle_require_single_line_style(node)
|
|
173
|
-
if node.endless? &&
|
|
174
|
+
if node.endless? && node.multiline?
|
|
174
175
|
add_offense(node, message: MSG_MULTI_LINE) do |corrector|
|
|
175
176
|
correct_to_multiline(corrector, node)
|
|
176
177
|
end
|
|
@@ -207,7 +208,7 @@ module RuboCop
|
|
|
207
208
|
|
|
208
209
|
def correct_to_multiline(corrector, node)
|
|
209
210
|
replacement = <<~RUBY.strip
|
|
210
|
-
def #{node.method_name}#{arguments(node)}
|
|
211
|
+
def #{receiver(node)}#{node.method_name}#{arguments(node)}
|
|
211
212
|
#{node.body.source}
|
|
212
213
|
end
|
|
213
214
|
RUBY
|
|
@@ -217,10 +218,14 @@ module RuboCop
|
|
|
217
218
|
|
|
218
219
|
def endless_replacement(node)
|
|
219
220
|
<<~RUBY.strip
|
|
220
|
-
def #{node.method_name}#{arguments(node)} = #{node.body.source}
|
|
221
|
+
def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
|
|
221
222
|
RUBY
|
|
222
223
|
end
|
|
223
224
|
|
|
225
|
+
def receiver(node)
|
|
226
|
+
node.receiver ? "#{node.receiver.source}#{node.loc.operator.source}" : ''
|
|
227
|
+
end
|
|
228
|
+
|
|
224
229
|
def arguments(node, missing = '')
|
|
225
230
|
node.arguments.any? ? node.arguments.source : missing
|
|
226
231
|
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for `File.open` without a block, which can leak file descriptors.
|
|
7
|
+
#
|
|
8
|
+
# When `File.open` is called without a block, the caller is responsible
|
|
9
|
+
# for closing the file descriptor. If it is not explicitly closed, it
|
|
10
|
+
# will only be closed when the garbage collector runs, which may lead
|
|
11
|
+
# to resource exhaustion. Using the block form ensures the file is
|
|
12
|
+
# automatically closed when the block exits.
|
|
13
|
+
#
|
|
14
|
+
# This cop only registers an offense when the result of `File.open` is
|
|
15
|
+
# assigned to a variable or has a method chained on it, as those are the
|
|
16
|
+
# clearest indicators that the block form should be used instead. When
|
|
17
|
+
# `File.open` is used as a return value or passed as an argument, the
|
|
18
|
+
# caller is likely managing the file descriptor intentionally.
|
|
19
|
+
#
|
|
20
|
+
# @safety
|
|
21
|
+
# This cop is unsafe because it relies on syntax heuristics and cannot
|
|
22
|
+
# verify whether the file descriptor is safely managed. For example, it
|
|
23
|
+
# still flags intentional one-shot reads (`File.open("f").read`) where
|
|
24
|
+
# the file descriptor is closed by the garbage collector.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# # bad
|
|
28
|
+
# f = File.open('file')
|
|
29
|
+
#
|
|
30
|
+
# # bad
|
|
31
|
+
# File.open('file').read
|
|
32
|
+
#
|
|
33
|
+
# # good
|
|
34
|
+
# File.open('file') do |f|
|
|
35
|
+
# f.read
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# File.open('file', &:read)
|
|
40
|
+
#
|
|
41
|
+
# # good - pass an open file object to an API that manages its lifecycle
|
|
42
|
+
# process(io: File.open('file'))
|
|
43
|
+
#
|
|
44
|
+
# # good - return an open file object for the caller to manage
|
|
45
|
+
# def json_key_io
|
|
46
|
+
# File.open('file')
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
# # good - use File.read for one-shot reads
|
|
50
|
+
# File.read('file')
|
|
51
|
+
#
|
|
52
|
+
class FileOpen < Base
|
|
53
|
+
MSG = '`File.open` without a block may leak a file descriptor; use the block form.'
|
|
54
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
|
55
|
+
|
|
56
|
+
# @!method file_open?(node)
|
|
57
|
+
def_node_matcher :file_open?, <<~PATTERN
|
|
58
|
+
(send (const {nil? cbase} :File) :open ...)
|
|
59
|
+
PATTERN
|
|
60
|
+
|
|
61
|
+
def on_send(node)
|
|
62
|
+
return unless file_open?(node)
|
|
63
|
+
return if node.block_argument?
|
|
64
|
+
return unless offensive_usage?(node)
|
|
65
|
+
|
|
66
|
+
add_offense(node)
|
|
67
|
+
end
|
|
68
|
+
alias on_csend on_send
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def offensive_usage?(node)
|
|
73
|
+
return true unless node.value_used?
|
|
74
|
+
|
|
75
|
+
node.parent.lvasgn_type? || receiver_of_chained_call?(node)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def receiver_of_chained_call?(node)
|
|
79
|
+
node.parent.receiver == node
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -104,28 +104,30 @@ module RuboCop
|
|
|
104
104
|
|
|
105
105
|
def replacement(mode, filename, content, write_node)
|
|
106
106
|
replacement = "#{write_method(mode)}(#{filename.source}, #{content.source})"
|
|
107
|
+
heredoc = heredoc_in_write(write_node)
|
|
108
|
+
return replacement unless heredoc
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
<<~REPLACEMENT.chomp
|
|
111
|
+
#{replacement}
|
|
112
|
+
#{heredoc_range(heredoc).source}
|
|
113
|
+
REPLACEMENT
|
|
114
|
+
end
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
else
|
|
116
|
-
replacement
|
|
117
|
-
end
|
|
116
|
+
def heredoc_in_write(write_node)
|
|
117
|
+
return unless write_node.block_type? && (first_argument = write_node.body.first_argument)
|
|
118
|
+
|
|
119
|
+
find_heredoc(first_argument)
|
|
118
120
|
end
|
|
119
121
|
|
|
120
|
-
def heredoc
|
|
121
|
-
|
|
122
|
-
first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
|
|
122
|
+
def heredoc_range(heredoc)
|
|
123
|
+
range_between(heredoc.loc.heredoc_body.begin_pos, heredoc.loc.heredoc_end.end_pos)
|
|
123
124
|
end
|
|
124
125
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
def find_heredoc(node)
|
|
127
|
+
return node if node.respond_to?(:heredoc?) && node.heredoc?
|
|
128
|
+
return if node.send_type? && !(receiver = node.receiver)
|
|
129
|
+
|
|
130
|
+
find_heredoc(receiver)
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
133
|
end
|
|
@@ -8,6 +8,9 @@ module RuboCop
|
|
|
8
8
|
# parameter. An `each` call with a block on a single line is always
|
|
9
9
|
# allowed.
|
|
10
10
|
#
|
|
11
|
+
# NOTE: `each` is preferred in idiomatic Ruby because `for` leaks
|
|
12
|
+
# its loop variable into the surrounding scope.
|
|
13
|
+
#
|
|
11
14
|
# @example EnforcedStyle: each (default)
|
|
12
15
|
# # bad
|
|
13
16
|
# def foo
|
|
@@ -11,9 +11,10 @@ module RuboCop
|
|
|
11
11
|
# if the first argument is a string literal and if the second
|
|
12
12
|
# argument is an array literal.
|
|
13
13
|
#
|
|
14
|
-
# Autocorrection will be applied when
|
|
15
|
-
#
|
|
16
|
-
# provided that their return value is not an array.
|
|
14
|
+
# Autocorrection will be applied when the argument is a literal or uses a known
|
|
15
|
+
# built-in conversion method such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`,
|
|
16
|
+
# and `to_sym` on variables, provided that their return value is not an array.
|
|
17
|
+
# For example, when using `to_s`,
|
|
17
18
|
# `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
|
|
18
19
|
#
|
|
19
20
|
# [source,ruby]
|
|
@@ -18,6 +18,11 @@ module RuboCop
|
|
|
18
18
|
# of `EnforcedStyle`) are only considered if used in the format string argument to the
|
|
19
19
|
# methods `printf`, `sprintf`, `format` and `%`.
|
|
20
20
|
#
|
|
21
|
+
# NOTE: In `aggressive` mode, offenses are registered for all strings containing tokens,
|
|
22
|
+
# but autocorrection is only applied when the string appears in a known formatting context
|
|
23
|
+
# (`format`, `sprintf`, `printf`, or `%`). This is done in order to prevent false
|
|
24
|
+
# autocorrections for strings that are not actually format strings.
|
|
25
|
+
#
|
|
21
26
|
# NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
|
|
22
27
|
# configured with `Conservative: true`. This is done in order to prevent false positives,
|
|
23
28
|
# because this format is very similar to encoded URLs or Date/Time formatting strings.
|
|
@@ -90,9 +95,25 @@ module RuboCop
|
|
|
90
95
|
# # good
|
|
91
96
|
# redirect('foo/%{bar_id}')
|
|
92
97
|
#
|
|
98
|
+
# @example Mode: aggressive (default), EnforcedStyle: annotated
|
|
99
|
+
#
|
|
100
|
+
# # bad
|
|
101
|
+
# "%{greeting}"
|
|
102
|
+
# foo("%{greeting}")
|
|
103
|
+
#
|
|
104
|
+
# # bad
|
|
105
|
+
# format("%{greeting}", greeting: 'Hello')
|
|
106
|
+
# printf("%{greeting}", greeting: 'Hello')
|
|
107
|
+
# sprintf("%{greeting}", greeting: 'Hello')
|
|
108
|
+
# "%{greeting}" % { greeting: 'Hello' }
|
|
109
|
+
#
|
|
110
|
+
# # good
|
|
111
|
+
# format("%<greeting>s", greeting: 'Hello')
|
|
112
|
+
# printf("%<greeting>s", greeting: 'Hello')
|
|
113
|
+
# sprintf("%<greeting>s", greeting: 'Hello')
|
|
114
|
+
# "%<greeting>s" % { greeting: 'Hello' }
|
|
115
|
+
#
|
|
93
116
|
# @example Mode: conservative, EnforcedStyle: annotated
|
|
94
|
-
# # In `conservative` mode, offenses are only registered for strings
|
|
95
|
-
# # given to a known formatting method.
|
|
96
117
|
#
|
|
97
118
|
# # good
|
|
98
119
|
# "%{greeting}"
|
|
@@ -104,6 +125,12 @@ module RuboCop
|
|
|
104
125
|
# sprintf("%{greeting}", greeting: 'Hello')
|
|
105
126
|
# "%{greeting}" % { greeting: 'Hello' }
|
|
106
127
|
#
|
|
128
|
+
# # good
|
|
129
|
+
# format("%<greeting>s", greeting: 'Hello')
|
|
130
|
+
# printf("%<greeting>s", greeting: 'Hello')
|
|
131
|
+
# sprintf("%<greeting>s", greeting: 'Hello')
|
|
132
|
+
# "%<greeting>s" % { greeting: 'Hello' }
|
|
133
|
+
#
|
|
107
134
|
class FormatStringToken < Base
|
|
108
135
|
include ConfigurableEnforcedStyle
|
|
109
136
|
include AllowedMethods
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Looks for uses of global variables.
|
|
6
|
+
# Looks for uses of global variables. Global variables introduce
|
|
7
|
+
# shared mutable state that makes code harder to test, debug,
|
|
8
|
+
# and reason about, since any part of the program can read or modify them.
|
|
9
|
+
#
|
|
7
10
|
# It does not report offenses for built-in global variables.
|
|
8
11
|
# Built-in global variables are allowed by default. Additionally
|
|
9
12
|
# users can allow additional variables via the AllowedVariables option.
|
|
@@ -54,7 +57,7 @@ module RuboCop
|
|
|
54
57
|
].map(&:to_sym)
|
|
55
58
|
|
|
56
59
|
def user_vars
|
|
57
|
-
cop_config['AllowedVariables'].map(&:to_sym)
|
|
60
|
+
@user_vars ||= cop_config['AllowedVariables'].map(&:to_sym).freeze
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def allowed_var?(global_var)
|
|
@@ -224,15 +224,18 @@ module RuboCop
|
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
def find_heredoc_argument(node)
|
|
227
|
-
return unless node
|
|
227
|
+
return unless node
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
node = node.children.first while node.begin_type?
|
|
230
|
+
return node if heredoc?(node)
|
|
231
|
+
return unless node.call_type?
|
|
230
232
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
find_heredoc_argument(last_arg)
|
|
233
|
+
node.arguments.reverse_each do |argument|
|
|
234
|
+
heredoc_argument = find_heredoc_argument(argument)
|
|
235
|
+
return heredoc_argument if heredoc_argument
|
|
235
236
|
end
|
|
237
|
+
|
|
238
|
+
find_heredoc_argument(node.receiver)
|
|
236
239
|
end
|
|
237
240
|
|
|
238
241
|
def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
|
|
@@ -6,8 +6,13 @@ module RuboCop
|
|
|
6
6
|
# Checks for presence or absence of braces around hash literal as a last
|
|
7
7
|
# array item depending on configuration.
|
|
8
8
|
#
|
|
9
|
-
# NOTE: This cop will ignore arrays where
|
|
10
|
-
# EnforcedStyle
|
|
9
|
+
# NOTE: This cop will ignore arrays where multiple items are all hashes,
|
|
10
|
+
# regardless of `EnforcedStyle`.
|
|
11
|
+
#
|
|
12
|
+
# [source,ruby]
|
|
13
|
+
# ----
|
|
14
|
+
# [{ one: 1 }, { two: 2 }]
|
|
15
|
+
# ----
|
|
11
16
|
#
|
|
12
17
|
# @example EnforcedStyle: braces (default)
|
|
13
18
|
# # bad
|
|
@@ -16,8 +21,11 @@ module RuboCop
|
|
|
16
21
|
# # good
|
|
17
22
|
# [1, 2, { one: 1, two: 2 }]
|
|
18
23
|
#
|
|
24
|
+
# # bad
|
|
25
|
+
# [one: 1, two: 2]
|
|
26
|
+
#
|
|
19
27
|
# # good
|
|
20
|
-
# [{ one: 1
|
|
28
|
+
# [{ one: 1, two: 2 }]
|
|
21
29
|
#
|
|
22
30
|
# @example EnforcedStyle: no_braces
|
|
23
31
|
# # bad
|
|
@@ -26,8 +34,11 @@ module RuboCop
|
|
|
26
34
|
# # good
|
|
27
35
|
# [1, 2, one: 1, two: 2]
|
|
28
36
|
#
|
|
37
|
+
# # bad
|
|
38
|
+
# [{ one: 1, two: 2 }]
|
|
39
|
+
#
|
|
29
40
|
# # good
|
|
30
|
-
# [
|
|
41
|
+
# [one: 1, two: 2]
|
|
31
42
|
class HashAsLastArrayItem < Base
|
|
32
43
|
include RangeHelp
|
|
33
44
|
include ConfigurableEnforcedStyle
|
|
@@ -69,7 +80,12 @@ module RuboCop
|
|
|
69
80
|
return if node.braces?
|
|
70
81
|
|
|
71
82
|
add_offense(node, message: 'Wrap hash in `{` and `}`.') do |corrector|
|
|
72
|
-
|
|
83
|
+
if node.single_line? || same_line?(node, node.parent)
|
|
84
|
+
corrector.wrap(node, '{', '}')
|
|
85
|
+
else
|
|
86
|
+
indent = indent(node)
|
|
87
|
+
corrector.wrap(node, "{\n#{indent}", "\n#{indent}}")
|
|
88
|
+
end
|
|
73
89
|
end
|
|
74
90
|
end
|
|
75
91
|
|
|
@@ -73,7 +73,7 @@ module RuboCop
|
|
|
73
73
|
first_argument = node.first_argument
|
|
74
74
|
if first_argument.hash_type?
|
|
75
75
|
register_offense_for_hash(node, first_argument)
|
|
76
|
-
elsif first_argument.
|
|
76
|
+
elsif first_argument.type?(:splat, :forwarded_restarg)
|
|
77
77
|
add_offense(node, message: MSG_SPLAT) unless allowed_splat_argument?
|
|
78
78
|
elsif use_zip_method_without_argument?(first_argument)
|
|
79
79
|
register_offense_for_zip_method(node, first_argument)
|
|
@@ -41,8 +41,13 @@ module RuboCop
|
|
|
41
41
|
# # good
|
|
42
42
|
# hash.fetch(key)
|
|
43
43
|
#
|
|
44
|
+
# @example AllowedReceivers: ['Rails.cache']
|
|
45
|
+
# # good
|
|
46
|
+
# Rails.cache.fetch(name, options) { block }
|
|
47
|
+
#
|
|
44
48
|
class HashLookupMethod < Base
|
|
45
49
|
include ConfigurableEnforcedStyle
|
|
50
|
+
include AllowedReceivers
|
|
46
51
|
extend AutoCorrector
|
|
47
52
|
|
|
48
53
|
BRACKET_MSG = 'Use `Hash#[]` instead of `Hash#fetch`.'
|
|
@@ -51,6 +56,8 @@ module RuboCop
|
|
|
51
56
|
RESTRICT_ON_SEND = %i[[] fetch].freeze
|
|
52
57
|
|
|
53
58
|
def on_send(node)
|
|
59
|
+
return if (receiver = node.receiver) && allowed_receiver?(receiver)
|
|
60
|
+
|
|
54
61
|
if offense_for_brackets?(node)
|
|
55
62
|
add_offense(node.loc.selector, message: BRACKET_MSG) do |corrector|
|
|
56
63
|
correct_fetch_to_brackets(corrector, node)
|
|
@@ -75,18 +82,23 @@ module RuboCop
|
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
def correct_fetch_to_brackets(corrector, node)
|
|
78
|
-
receiver = node.receiver.source
|
|
79
85
|
key = node.first_argument.source
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
|
|
87
|
+
if node.csend_type?
|
|
88
|
+
corrector.replace(node, "(#{node.receiver.source}[#{key}])")
|
|
89
|
+
else
|
|
90
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
91
|
+
end
|
|
83
92
|
end
|
|
84
93
|
|
|
85
94
|
def correct_brackets_to_fetch(corrector, node)
|
|
86
|
-
receiver = node.receiver.source
|
|
87
95
|
key = node.first_argument.source
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
|
|
97
|
+
if node.csend_type?
|
|
98
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "&.fetch(#{key})")
|
|
99
|
+
else
|
|
100
|
+
corrector.replace(node.loc.selector.join(node.source_range.end), ".fetch(#{key})")
|
|
101
|
+
end
|
|
90
102
|
end
|
|
91
103
|
end
|
|
92
104
|
end
|
|
@@ -11,9 +11,11 @@ module RuboCop
|
|
|
11
11
|
# (`transform_keys` was added in Ruby 2.5.)
|
|
12
12
|
#
|
|
13
13
|
# @safety
|
|
14
|
-
# This cop
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# This cop identifies the receiver as a hash by checking for literal hash
|
|
15
|
+
# syntax and common methods that are known to return hashes (e.g. `to_h`,
|
|
16
|
+
# `merge`, `invert`, `group_by`, etc.). However, it is unsafe because it
|
|
17
|
+
# is possible for a custom class to define one of these methods and return
|
|
18
|
+
# something other than a hash.
|
|
17
19
|
#
|
|
18
20
|
# @example
|
|
19
21
|
# # bad
|
|
@@ -21,10 +23,18 @@ module RuboCop
|
|
|
21
23
|
# Hash[{a: 1, b: 2}.collect { |k, v| [foo(k), v] }]
|
|
22
24
|
# {a: 1, b: 2}.map { |k, v| [k.to_s, v] }.to_h
|
|
23
25
|
# {a: 1, b: 2}.to_h { |k, v| [k.to_s, v] }
|
|
26
|
+
# foo.to_h.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
|
27
|
+
# foo.merge(bar).map { |k, v| [k.to_s, v] }.to_h
|
|
24
28
|
#
|
|
25
29
|
# # good
|
|
26
30
|
# {a: 1, b: 2}.transform_keys { |k| foo(k) }
|
|
27
31
|
# {a: 1, b: 2}.transform_keys { |k| k.to_s }
|
|
32
|
+
# foo.to_h.transform_keys { |k| k.to_sym }
|
|
33
|
+
# foo.merge(bar).transform_keys { |k| k.to_s }
|
|
34
|
+
#
|
|
35
|
+
# # Won't register an offense - receiver is not known to be a hash
|
|
36
|
+
# foo.bar.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
|
37
|
+
# baz.map { |k, v| [k.to_s, v] }.to_h
|
|
28
38
|
class HashTransformKeys < Base
|
|
29
39
|
include HashTransformMethod
|
|
30
40
|
extend AutoCorrector
|
|
@@ -35,7 +45,7 @@ module RuboCop
|
|
|
35
45
|
# @!method on_bad_each_with_object(node)
|
|
36
46
|
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
|
37
47
|
(block
|
|
38
|
-
(call
|
|
48
|
+
(call #hash_receiver? :each_with_object (hash))
|
|
39
49
|
(args
|
|
40
50
|
(mlhs
|
|
41
51
|
(arg $_)
|
|
@@ -50,7 +60,7 @@ module RuboCop
|
|
|
50
60
|
(const _ :Hash)
|
|
51
61
|
:[]
|
|
52
62
|
(block
|
|
53
|
-
(call
|
|
63
|
+
(call #hash_receiver? {:map :collect})
|
|
54
64
|
(args
|
|
55
65
|
(arg $_)
|
|
56
66
|
(arg _val))
|
|
@@ -61,7 +71,7 @@ module RuboCop
|
|
|
61
71
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
|
62
72
|
(call
|
|
63
73
|
(block
|
|
64
|
-
(call
|
|
74
|
+
(call #hash_receiver? {:map :collect})
|
|
65
75
|
(args
|
|
66
76
|
(arg $_)
|
|
67
77
|
(arg _val))
|
|
@@ -72,7 +82,7 @@ module RuboCop
|
|
|
72
82
|
# @!method on_bad_to_h(node)
|
|
73
83
|
def_node_matcher :on_bad_to_h, <<~PATTERN
|
|
74
84
|
(block
|
|
75
|
-
(call
|
|
85
|
+
(call #hash_receiver? :to_h)
|
|
76
86
|
(args
|
|
77
87
|
(arg $_)
|
|
78
88
|
(arg _val))
|