rubocop 1.86.2 → 1.88.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 +82 -71
- data/config/obsoletion.yml +21 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
- data/lib/rubocop/cli.rb +2 -0
- data/lib/rubocop/config_loader.rb +17 -2
- data/lib/rubocop/config_loader_resolver.rb +11 -3
- data/lib/rubocop/config_store.rb +1 -1
- data/lib/rubocop/cop/base.rb +25 -4
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +14 -5
- data/lib/rubocop/cop/layout/end_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/indentation_width.rb +13 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
- data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/constant_reassignment.rb +36 -4
- data/lib/rubocop/cop/lint/constant_resolution.rb +5 -5
- data/lib/rubocop/cop/lint/debugger.rb +0 -1
- data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
- data/lib/rubocop/cop/lint/empty_block.rb +3 -3
- data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +5 -5
- data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +18 -9
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/rand_one.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -3
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
- data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
- data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +6 -6
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
- data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
- data/lib/rubocop/cop/lint/script_permission.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
- data/lib/rubocop/cop/lint/useless_times.rb +22 -1
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
- 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/mixin/project_index_help.rb +48 -0
- data/lib/rubocop/cop/mixin.rb +1 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_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/registry.rb +28 -6
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/style/alias.rb +11 -2
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +4 -0
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
- data/lib/rubocop/cop/style/case_equality.rb +14 -2
- data/lib/rubocop/cop/style/character_literal.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
- data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
- data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
- data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
- data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
- data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/dig_chain.rb +5 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
- data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
- data/lib/rubocop/cop/style/file_write.rb +21 -16
- data/lib/rubocop/cop/style/format_string.rb +4 -3
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
- data/lib/rubocop/cop/style/hash_slice.rb +16 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
- data/lib/rubocop/cop/style/parallel_assignment.rb +8 -1
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
- data/lib/rubocop/cop/style/redundant_format.rb +1 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
- data/lib/rubocop/cop/style/regexp_literal.rb +2 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +16 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
- data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
- data/lib/rubocop/cop/style/while_until_do.rb +7 -0
- data/lib/rubocop/cop/style/word_array.rb +1 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
- data/lib/rubocop/file_patterns.rb +9 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
- data/lib/rubocop/options.rb +18 -0
- data/lib/rubocop/project_index_loader.rb +66 -0
- data/lib/rubocop/runner.rb +47 -3
- data/lib/rubocop/server/core.rb +6 -0
- data/lib/rubocop/version.rb +20 -2
- data/lib/rubocop.rb +1 -0
- metadata +5 -3
|
@@ -79,6 +79,11 @@ module RuboCop
|
|
|
79
79
|
corrector.replace(range, replacement)
|
|
80
80
|
|
|
81
81
|
comments_in_range(node).reverse_each do |comment|
|
|
82
|
+
# Only relocate comments that the replacement destroys. A trailing
|
|
83
|
+
# comment after the chain survives in place, so moving it would
|
|
84
|
+
# duplicate it (and splitting the line drops the indentation).
|
|
85
|
+
next if comment.source_range.begin_pos >= range.end_pos
|
|
86
|
+
|
|
82
87
|
corrector.insert_before(node, "#{comment.source}\n")
|
|
83
88
|
end
|
|
84
89
|
end
|
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
|
6
6
|
module Cop
|
|
7
7
|
module Style
|
|
8
8
|
# Detects comments to enable/disable RuboCop.
|
|
9
|
-
# This is useful if want to make sure that every RuboCop error gets fixed
|
|
9
|
+
# This is useful if you want to make sure that every RuboCop error gets fixed
|
|
10
10
|
# and not quickly disabled with a comment.
|
|
11
11
|
#
|
|
12
12
|
# Specific cops can be allowed with the `AllowedCops` configuration. Note that
|
|
@@ -70,7 +70,7 @@ module RuboCop
|
|
|
70
70
|
|
|
71
71
|
def allowed_var?(node)
|
|
72
72
|
env_key_node = node.children.last
|
|
73
|
-
env_key_node.str_type? && cop_config['
|
|
73
|
+
env_key_node.str_type? && cop_config['AllowedVariables'].include?(env_key_node.value)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def used_as_flag?(node)
|
|
@@ -104,28 +104,33 @@ module RuboCop
|
|
|
104
104
|
|
|
105
105
|
def replacement(mode, filename, content, write_node)
|
|
106
106
|
replacement = "#{write_method(mode)}(#{filename.source}, #{content.source})"
|
|
107
|
+
heredocs = removed_heredocs(filename, content, write_node)
|
|
108
|
+
return replacement if heredocs.empty?
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
[replacement, *heredocs.map { |heredoc| heredoc_range(heredoc).source }].join("\n")
|
|
111
|
+
end
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
# Heredocs opened in the arguments keep working in the replacement, but their
|
|
114
|
+
# bodies are lost when they lie within the replaced range, so they need to be
|
|
115
|
+
# restored after the replacement.
|
|
116
|
+
def removed_heredocs(filename, content, write_node)
|
|
117
|
+
[filename, content].flat_map { |argument| find_heredocs(argument) }
|
|
118
|
+
.select { |heredoc| removed?(heredoc, write_node) }
|
|
119
|
+
.sort_by { |heredoc| heredoc.loc.heredoc_body.begin_pos }
|
|
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_heredocs(node)
|
|
127
|
+
[node, *node.each_descendant(:any_str)].select do |child|
|
|
128
|
+
child.respond_to?(:heredoc?) && child.heredoc?
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def removed?(heredoc, write_node)
|
|
133
|
+
heredoc.loc.heredoc_end.end_pos <= write_node.source_range.end_pos
|
|
129
134
|
end
|
|
130
135
|
end
|
|
131
136
|
end
|
|
@@ -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]
|
|
@@ -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)
|
|
@@ -19,6 +19,22 @@ module RuboCop
|
|
|
19
19
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
|
20
20
|
# is a `Hash` or responds to the replacement method.
|
|
21
21
|
#
|
|
22
|
+
# Additionally, the replacement may change the order of the resulting
|
|
23
|
+
# hash: `Hash#slice` returns entries in the order the keys are given,
|
|
24
|
+
# whereas `select`, `filter`, and `reject` preserve the entry order of
|
|
25
|
+
# the receiver.
|
|
26
|
+
#
|
|
27
|
+
# For example:
|
|
28
|
+
#
|
|
29
|
+
# [source,ruby]
|
|
30
|
+
# ----
|
|
31
|
+
# hash = {foo: 1, bar: 2, baz: 3}
|
|
32
|
+
# keys = %i[baz foo]
|
|
33
|
+
#
|
|
34
|
+
# hash.select { |k, _v| keys.include?(k) } # => {foo: 1, baz: 3}
|
|
35
|
+
# hash.slice(*keys) # => {baz: 3, foo: 1}
|
|
36
|
+
# ----
|
|
37
|
+
#
|
|
22
38
|
# @example
|
|
23
39
|
#
|
|
24
40
|
# # bad
|
|
@@ -86,7 +86,7 @@ module RuboCop
|
|
|
86
86
|
MSG_USE_NORMAL = 'Modifier form of `%<keyword>s` makes the line too long.'
|
|
87
87
|
|
|
88
88
|
def self.autocorrect_incompatible_with
|
|
89
|
-
[Style::SoleNestedConditional]
|
|
89
|
+
[Style::Next, Style::SoleNestedConditional]
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
|
10
10
|
# Required capitalization can be set with the `DirectiveCapitalization` and
|
|
11
11
|
# `ValueCapitalization` configuration keys.
|
|
12
12
|
#
|
|
13
|
-
# NOTE: If one of these
|
|
13
|
+
# NOTE: If one of these configurations is set to nil, any capitalization is allowed.
|
|
14
14
|
#
|
|
15
15
|
# @example EnforcedStyle: snake_case (default)
|
|
16
16
|
# # The `snake_case` style will enforce that the frozen string literal
|
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
|
6
6
|
# Enforces the use of `max` or `min` instead of comparison for greater or less.
|
|
7
7
|
#
|
|
8
8
|
# NOTE: It can be used if you want to present limit or threshold in Ruby 2.7+.
|
|
9
|
-
#
|
|
9
|
+
# It is slow though. So autocorrection will apply generic `max` or `min`:
|
|
10
10
|
#
|
|
11
11
|
# [source,ruby]
|
|
12
12
|
# ----
|
|
@@ -6,6 +6,14 @@ module RuboCop
|
|
|
6
6
|
# Checks whether some constant value isn't a
|
|
7
7
|
# mutable literal (e.g. array or hash).
|
|
8
8
|
#
|
|
9
|
+
# When the `Recursive` option is enabled, mutable literals nested inside
|
|
10
|
+
# arrays and hashes are also frozen, so an offense on the outermost
|
|
11
|
+
# unfrozen literal will autocorrect every nested mutable literal as well.
|
|
12
|
+
# When the outer literal already has `.freeze` appended, the cop descends
|
|
13
|
+
# into it and reports each outermost unfrozen literal underneath. The
|
|
14
|
+
# option is disabled by default to preserve existing behavior; opt in to
|
|
15
|
+
# get strict nested freezing.
|
|
16
|
+
#
|
|
9
17
|
# Strict mode can be used to freeze all constants, rather than
|
|
10
18
|
# just literals.
|
|
11
19
|
# Strict mode is considered an experimental feature. It has not been
|
|
@@ -49,6 +57,17 @@ module RuboCop
|
|
|
49
57
|
# CONST = Something.new
|
|
50
58
|
#
|
|
51
59
|
#
|
|
60
|
+
# @example Recursive: false (default)
|
|
61
|
+
# # good - only the outer container needs to be frozen
|
|
62
|
+
# CONST = [{ a: [], b: 'foo' }].freeze
|
|
63
|
+
#
|
|
64
|
+
# @example Recursive: true
|
|
65
|
+
# # bad - nested mutable literals must be frozen too
|
|
66
|
+
# CONST = [{ a: [], b: 'foo' }].freeze
|
|
67
|
+
#
|
|
68
|
+
# # good
|
|
69
|
+
# CONST = [{ a: [].freeze, b: 'foo'.freeze }.freeze].freeze
|
|
70
|
+
#
|
|
52
71
|
# @example EnforcedStyle: strict
|
|
53
72
|
# # bad
|
|
54
73
|
# CONST = Something.new
|
|
@@ -138,10 +157,30 @@ module RuboCop
|
|
|
138
157
|
private
|
|
139
158
|
|
|
140
159
|
def on_assignment(value)
|
|
141
|
-
|
|
142
|
-
|
|
160
|
+
nodes = mutable_nodes(value) do |node|
|
|
161
|
+
if style == :strict
|
|
162
|
+
strict_check(node)
|
|
163
|
+
else
|
|
164
|
+
literal_check(node)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
nodes.each do |node|
|
|
169
|
+
add_offense(node) { |corrector| autocorrect(corrector, node) }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def mutable_nodes(value, &block)
|
|
174
|
+
if recursive? && explicitly_frozen_literal?(value)
|
|
175
|
+
literal_children(value.receiver).flat_map { |c| mutable_nodes(c, &block) }
|
|
143
176
|
else
|
|
144
|
-
|
|
177
|
+
node_offending = yield(value)
|
|
178
|
+
|
|
179
|
+
if node_offending
|
|
180
|
+
[value]
|
|
181
|
+
else
|
|
182
|
+
[]
|
|
183
|
+
end
|
|
145
184
|
end
|
|
146
185
|
end
|
|
147
186
|
|
|
@@ -151,18 +190,20 @@ module RuboCop
|
|
|
151
190
|
return if frozen_string_literal?(value)
|
|
152
191
|
return if shareable_constant_value?(value)
|
|
153
192
|
|
|
154
|
-
|
|
193
|
+
true
|
|
155
194
|
end
|
|
156
195
|
|
|
157
|
-
def
|
|
158
|
-
|
|
159
|
-
return unless mutable_literal?(value) ||
|
|
160
|
-
(target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
|
|
161
|
-
|
|
196
|
+
def literal_check(value)
|
|
197
|
+
return unless mutable_or_unfrozen_range?(value)
|
|
162
198
|
return if frozen_string_literal?(value)
|
|
163
199
|
return if shareable_constant_value?(value)
|
|
164
200
|
|
|
165
|
-
|
|
201
|
+
true
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def mutable_or_unfrozen_range?(value)
|
|
205
|
+
mutable_literal?(value) ||
|
|
206
|
+
(target_ruby_version <= 2.7 && range_enclosed_in_parentheses?(value))
|
|
166
207
|
end
|
|
167
208
|
|
|
168
209
|
def autocorrect(corrector, node)
|
|
@@ -171,13 +212,66 @@ module RuboCop
|
|
|
171
212
|
splat_value = splat_value(node)
|
|
172
213
|
if splat_value
|
|
173
214
|
correct_splat_expansion(corrector, expr, splat_value)
|
|
174
|
-
|
|
215
|
+
corrector.insert_after(expr, '.freeze')
|
|
216
|
+
return
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if node.array_type? && !node.bracketed?
|
|
175
220
|
corrector.wrap(expr, '[', ']')
|
|
176
221
|
elsif requires_parentheses?(node)
|
|
177
222
|
corrector.wrap(expr, '(', ')')
|
|
178
223
|
end
|
|
179
224
|
|
|
180
225
|
corrector.insert_after(expr, '.freeze')
|
|
226
|
+
|
|
227
|
+
freeze_nested_literals(corrector, node) if recursive?
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Recursively freezes every nested mutable literal inside an array or
|
|
231
|
+
# hash literal. Already-frozen subtrees are not re-frozen, but their
|
|
232
|
+
# children are still inspected for unfrozen literals deeper down.
|
|
233
|
+
def freeze_nested_literals(corrector, node)
|
|
234
|
+
literal_children(node).each do |child|
|
|
235
|
+
if explicitly_frozen_literal?(child)
|
|
236
|
+
freeze_nested_literals(corrector, child.receiver)
|
|
237
|
+
elsif freezable_nested_literal?(child)
|
|
238
|
+
autocorrect(corrector, child)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def freezable_nested_literal?(node)
|
|
244
|
+
return false if frozen_string_literal?(node)
|
|
245
|
+
return false if shareable_constant_value?(node)
|
|
246
|
+
|
|
247
|
+
mutable_literal?(node)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Returns the child literals of an array or hash node that may
|
|
251
|
+
# themselves need freezing. For hashes, both keys and values are
|
|
252
|
+
# included. Percent-literal arrays (e.g. `%w(a b)`) are skipped because
|
|
253
|
+
# `.freeze` cannot be appended to their contents.
|
|
254
|
+
def literal_children(node)
|
|
255
|
+
case node.type
|
|
256
|
+
when :array
|
|
257
|
+
return [] if node.percent_literal?
|
|
258
|
+
|
|
259
|
+
node.children
|
|
260
|
+
when :hash
|
|
261
|
+
node.children.flat_map { |child| child.pair_type? ? child.children : [] }
|
|
262
|
+
else
|
|
263
|
+
[]
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def explicitly_frozen_literal?(node)
|
|
268
|
+
return false unless node.send_type? && node.method?(:freeze)
|
|
269
|
+
|
|
270
|
+
node.receiver && mutable_literal?(node.receiver)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def recursive?
|
|
274
|
+
cop_config.fetch('Recursive', false)
|
|
181
275
|
end
|
|
182
276
|
|
|
183
277
|
def mutable_literal?(value)
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
rhs_elements = Array(rhs).compact # edge case for one constant
|
|
39
39
|
|
|
40
40
|
return if allowed_lhs?(node.assignments) || allowed_rhs?(rhs) ||
|
|
41
|
-
allowed_masign?(node.assignments, rhs_elements)
|
|
41
|
+
allowed_masign?(node.assignments, rhs_elements) || contains_heredoc?(rhs)
|
|
42
42
|
|
|
43
43
|
range = node.source_range.begin.join(rhs.source_range.end)
|
|
44
44
|
|
|
@@ -77,6 +77,13 @@ module RuboCop
|
|
|
77
77
|
!node.array_type? || elements.any?(&:splat_type?)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
# Autocorrection splits the assignment into single assignments on
|
|
81
|
+
# consecutive lines, which would put following assignments into the
|
|
82
|
+
# heredoc body unless the heredoc bodies were moved along.
|
|
83
|
+
def contains_heredoc?(node)
|
|
84
|
+
node.each_descendant(:any_str).any?(&:heredoc?)
|
|
85
|
+
end
|
|
86
|
+
|
|
80
87
|
def assignment_corrector(node, rhs, order)
|
|
81
88
|
if node.parent&.rescue_type?
|
|
82
89
|
_assignment, modifier = *node.parent
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for the instantiation of array using redundant `Array` constructor.
|
|
7
|
-
# Autocorrect replaces
|
|
6
|
+
# Checks for the instantiation of an array using a redundant `Array` constructor.
|
|
7
|
+
# Autocorrect replaces it with an array literal which is the simplest and fastest.
|
|
8
8
|
#
|
|
9
9
|
# @example
|
|
10
10
|
#
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Avoid redundant `::` prefix on constant.
|
|
6
|
+
# Avoid redundant `::` prefix on a constant.
|
|
7
7
|
#
|
|
8
|
-
# How Ruby searches
|
|
8
|
+
# How Ruby searches constants is a bit complicated, and it can often be difficult to
|
|
9
9
|
# understand from the code whether the `::` is intended or not. Where `Module.nesting`
|
|
10
10
|
# is empty, there is no need to prepend `::`, so it would be nice to consistently
|
|
11
11
|
# avoid such meaningless `::` prefix to avoid confusion.
|
|
12
12
|
#
|
|
13
|
-
# NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled
|
|
14
|
-
# conflicting rules.
|
|
15
|
-
# `Lint/ConstantResolution` cop which is disabled by default.
|
|
13
|
+
# NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled,
|
|
14
|
+
# to prevent conflicting rules. This is because it respects user configurations
|
|
15
|
+
# that want to enable `Lint/ConstantResolution` cop which is disabled by default.
|
|
16
16
|
#
|
|
17
17
|
# @example
|
|
18
18
|
# # bad
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for the instantiation of regexp using redundant `Regexp.new` or `Regexp.compile`.
|
|
7
|
-
# Autocorrect replaces
|
|
6
|
+
# Checks for the instantiation of a regexp using a redundant `Regexp.new` or `Regexp.compile`.
|
|
7
|
+
# Autocorrect replaces it with a regexp literal which is the simplest and fastest.
|
|
8
8
|
#
|
|
9
9
|
# @example
|
|
10
10
|
#
|
|
@@ -5,8 +5,8 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Enforces using `//` or `%r` around regular expressions.
|
|
7
7
|
#
|
|
8
|
-
# NOTE: The following `%r` cases using a regexp starts with a blank or `=`
|
|
9
|
-
# as a method argument allowed to prevent syntax errors.
|
|
8
|
+
# NOTE: The following `%r` cases using a regexp that starts with a blank or `=`
|
|
9
|
+
# as a method argument are allowed to prevent syntax errors.
|
|
10
10
|
#
|
|
11
11
|
# [source,ruby]
|
|
12
12
|
# ----
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for uses of `rescue` in its modifier form is added for following
|
|
6
|
+
# Checks for uses of `rescue` in its modifier form. It is added for the following
|
|
7
7
|
# reasons:
|
|
8
8
|
#
|
|
9
9
|
# * The syntax of modifier form `rescue` can be misleading because it
|
|
10
10
|
# might lead us to believe that `rescue` handles the given exception
|
|
11
|
-
# but it actually
|
|
11
|
+
# but it actually rescues all exceptions to return the given rescue
|
|
12
12
|
# block. In this case, value returned by handle_error or
|
|
13
13
|
# SomeException.
|
|
14
14
|
#
|
|
15
15
|
# * Modifier form `rescue` would rescue all the exceptions. It would
|
|
16
|
-
# silently skip all
|
|
16
|
+
# silently skip all exceptions or errors and handle the error.
|
|
17
17
|
# Example: If `NoMethodError` is raised, modifier form rescue would
|
|
18
18
|
# handle the exception.
|
|
19
19
|
#
|
|
@@ -128,7 +128,7 @@ module RuboCop
|
|
|
128
128
|
|
|
129
129
|
add_offense(range) do |corrector|
|
|
130
130
|
if after_expression
|
|
131
|
-
corrector
|
|
131
|
+
replace_semicolon_with_line_break(corrector, range)
|
|
132
132
|
else
|
|
133
133
|
# Prevents becoming one range instance with subsequent line when endless range
|
|
134
134
|
# without parentheses.
|
|
@@ -148,6 +148,21 @@ module RuboCop
|
|
|
148
148
|
end
|
|
149
149
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
150
150
|
|
|
151
|
+
def replace_semicolon_with_line_break(corrector, range)
|
|
152
|
+
# Replacing the semicolon with a newline would move the rest of the
|
|
153
|
+
# line into the body of a heredoc opened earlier on that line.
|
|
154
|
+
return if heredoc_opened_before_semicolon?(range)
|
|
155
|
+
|
|
156
|
+
corrector.replace(range, "\n")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def heredoc_opened_before_semicolon?(semicolon_range)
|
|
160
|
+
processed_source.ast.each_descendant(:any_str).select(&:heredoc?).any? do |heredoc|
|
|
161
|
+
heredoc.first_line == semicolon_range.line &&
|
|
162
|
+
heredoc.source_range.end_pos <= semicolon_range.begin_pos
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
151
166
|
def expressions_per_line(exprs)
|
|
152
167
|
# create a map matching lines to the number of expressions on them
|
|
153
168
|
exprs_lines = exprs.map(&:last_line)
|
|
@@ -61,6 +61,8 @@ module RuboCop
|
|
|
61
61
|
corrector.remove(range_with_surrounding_space(parent.loc.end, newlines: false))
|
|
62
62
|
elsif (class_node = parent.parent).body.nil?
|
|
63
63
|
corrector.remove(range_for_empty_class_body(class_node, parent))
|
|
64
|
+
elsif unparenthesized_struct_new?(parent)
|
|
65
|
+
wrap_unparenthesized_call_with_do(corrector, parent)
|
|
64
66
|
else
|
|
65
67
|
corrector.insert_after(parent, ' do')
|
|
66
68
|
end
|
|
@@ -73,6 +75,17 @@ module RuboCop
|
|
|
73
75
|
range_by_whole_lines(class_node.loc.end, include_final_newline: true)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
78
|
+
|
|
79
|
+
def unparenthesized_struct_new?(parent)
|
|
80
|
+
parent.send_type? && parent.arguments.any? && !parent.parenthesized?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def wrap_unparenthesized_call_with_do(corrector, parent)
|
|
84
|
+
args_source = parent.arguments.map(&:source).join(', ')
|
|
85
|
+
range = parent.loc.selector.end.join(parent.source_range.end)
|
|
86
|
+
|
|
87
|
+
corrector.replace(range, "(#{args_source}) do")
|
|
88
|
+
end
|
|
76
89
|
end
|
|
77
90
|
end
|
|
78
91
|
end
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Newcomers to
|
|
6
|
+
# Newcomers to Ruby applications may write top-level methods,
|
|
7
7
|
# when ideally they should be organized in appropriate classes or modules.
|
|
8
8
|
# This cop looks for definitions of top-level methods and warns about them.
|
|
9
9
|
#
|
|
10
|
-
# However for
|
|
10
|
+
# However, for Ruby scripts it is perfectly fine to use top-level methods.
|
|
11
11
|
# Hence this cop is disabled by default.
|
|
12
12
|
#
|
|
13
13
|
# @example
|
|
@@ -14,11 +14,11 @@ module RuboCop
|
|
|
14
14
|
#
|
|
15
15
|
# `forbid_mixed_logical_operators` style forbids the use of more than one type
|
|
16
16
|
# of logical operators. This makes the `unless` condition easier to read
|
|
17
|
-
# because either all conditions need to be met or any condition
|
|
17
|
+
# because either all conditions need to be met or any condition needs to be met
|
|
18
18
|
# in order for the expression to be truthy or falsey.
|
|
19
19
|
#
|
|
20
|
-
# `forbid_logical_operators` style forbids any use of logical
|
|
21
|
-
# This makes it even
|
|
20
|
+
# `forbid_logical_operators` style forbids any use of logical operators.
|
|
21
|
+
# This makes it even easier to read the `unless` condition as
|
|
22
22
|
# there is only one condition in the expression.
|
|
23
23
|
#
|
|
24
24
|
# @example EnforcedStyle: forbid_mixed_logical_operators (default)
|
|
@@ -33,6 +33,7 @@ module RuboCop
|
|
|
33
33
|
|
|
34
34
|
def on_while(node)
|
|
35
35
|
return unless node.multiline? && node.do?
|
|
36
|
+
return if same_line_body?(node)
|
|
36
37
|
|
|
37
38
|
add_offense(node.loc.begin, message: format(MSG, keyword: node.keyword)) do |corrector|
|
|
38
39
|
do_range = node.condition.source_range.end.join(node.loc.begin)
|
|
@@ -41,6 +42,12 @@ module RuboCop
|
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
alias on_until on_while
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def same_line_body?(node)
|
|
49
|
+
node.body && same_line?(node.loc.begin, node.body)
|
|
50
|
+
end
|
|
44
51
|
end
|
|
45
52
|
end
|
|
46
53
|
end
|
|
@@ -9,8 +9,10 @@ module RuboCop
|
|
|
9
9
|
# `receiver.length < 1` and `receiver.size == 0` that can be
|
|
10
10
|
# replaced by `receiver.empty?` and `!receiver.empty?`.
|
|
11
11
|
#
|
|
12
|
-
# NOTE: `File`, `Tempfile`, and `
|
|
13
|
-
# so allow `size == 0` and `size.zero?`.
|
|
12
|
+
# NOTE: `File`, `Tempfile`, `StringIO`, and `File::Stat` do not have `empty?`
|
|
13
|
+
# so allow `size == 0` and `size.zero?`. Note that when a `File::Stat` object
|
|
14
|
+
# is stored in a variable (e.g. `stat = File.stat(path); stat.size.zero?`),
|
|
15
|
+
# the cop cannot detect the type and may still register a false positive.
|
|
14
16
|
#
|
|
15
17
|
# @safety
|
|
16
18
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
|
@@ -146,7 +148,8 @@ module RuboCop
|
|
|
146
148
|
# @!method non_polymorphic_collection?(node)
|
|
147
149
|
def_node_matcher :non_polymorphic_collection?, <<~PATTERN
|
|
148
150
|
{(send (send (send (const {nil? cbase} :File) :stat _) ...) ...)
|
|
149
|
-
(send (send (send (const {nil? cbase} {:File :Tempfile :StringIO}) {:new :open} ...) ...) ...)
|
|
151
|
+
(send (send (send (const {nil? cbase} {:File :Tempfile :StringIO}) {:new :open} ...) ...) ...)
|
|
152
|
+
(send (send (send (const (const {nil? cbase} :File) :Stat) :new ...) ...) ...)}
|
|
150
153
|
PATTERN
|
|
151
154
|
end
|
|
152
155
|
end
|
|
@@ -21,11 +21,19 @@ module RuboCop
|
|
|
21
21
|
def initialize(patterns)
|
|
22
22
|
@strings = Set.new
|
|
23
23
|
@patterns = []
|
|
24
|
+
@match_cache = {}
|
|
24
25
|
partition_patterns(patterns)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def match?(path)
|
|
28
|
-
|
|
29
|
+
# `FilePatterns.from` memoizes one instance per pattern array (by identity),
|
|
30
|
+
# so this cache is shared across every cop using the same Include/Exclude
|
|
31
|
+
# list. Patterns are immutable within a run, so caching by path is safe.
|
|
32
|
+
cached = @match_cache[path]
|
|
33
|
+
return cached unless cached.nil?
|
|
34
|
+
|
|
35
|
+
@match_cache[path] =
|
|
36
|
+
@strings.include?(path) || @patterns.any? { |pattern| PathUtil.match_path?(pattern, path) }
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
private
|