rubocop 1.71.2 → 1.75.2
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/README.md +3 -3
- data/config/default.yml +118 -21
- data/config/internal_affairs.yml +20 -0
- data/config/obsoletion.yml +3 -1
- data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +39 -6
- data/lib/rubocop/config_loader.rb +48 -9
- data/lib/rubocop/config_loader_resolver.rb +24 -9
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
- data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
- data/lib/rubocop/config_obsoletion.rb +46 -2
- data/lib/rubocop/config_validator.rb +2 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +7 -3
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
- data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
- data/lib/rubocop/cop/internal_affairs.rb +2 -16
- data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
- data/lib/rubocop/cop/layout/def_end_alignment.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 +3 -3
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +27 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
- data/lib/rubocop/cop/layout/line_length.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -5
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
- data/lib/rubocop/cop/lint/debugger.rb +2 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -17
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +103 -9
- data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -3
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
- data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +261 -0
- data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
- data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
- data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -1
- 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/to_enum_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +71 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.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 +2 -2
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +0 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
- data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
- data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/range_help.rb +15 -3
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +64 -8
- data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
- data/lib/rubocop/cop/naming/variable_name.rb +51 -6
- data/lib/rubocop/cop/registry.rb +9 -6
- data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
- data/lib/rubocop/cop/style/array_intersect.rb +39 -28
- data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
- data/lib/rubocop/cop/style/class_and_module_children.rb +29 -7
- data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +1 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +10 -3
- data/lib/rubocop/cop/style/comparable_between.rb +75 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +3 -0
- data/lib/rubocop/cop/style/double_negation.rb +2 -2
- data/lib/rubocop/cop/style/empty_literal.rb +4 -0
- data/lib/rubocop/cop/style/endless_method.rb +163 -18
- data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
- data/lib/rubocop/cop/style/for.rb +1 -0
- data/lib/rubocop/cop/style/format_string_token.rb +38 -11
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
- data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
- data/lib/rubocop/cop/style/guard_clause.rb +2 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
- data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
- data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
- data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
- data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
- data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +13 -7
- data/lib/rubocop/cop/style/lambda.rb +1 -0
- data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
- data/lib/rubocop/cop/style/map_into_array.rb +1 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -4
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
- data/lib/rubocop/cop/style/next.rb +44 -0
- data/lib/rubocop/cop/style/object_then.rb +1 -0
- data/lib/rubocop/cop/style/proc.rb +1 -0
- data/lib/rubocop/cop/style/raise_args.rb +8 -8
- data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +57 -0
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
- data/lib/rubocop/cop/style/redundant_format.rb +257 -0
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +20 -5
- data/lib/rubocop/cop/style/redundant_self.rb +1 -0
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
- data/lib/rubocop/cop/style/return_nil.rb +2 -2
- data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -106
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
- 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/util.rb +2 -2
- data/lib/rubocop/cop/utils/format_string.rb +10 -5
- data/lib/rubocop/cop/variable_force/scope.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +2 -7
- data/lib/rubocop/cop/variable_force.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +12 -1
- data/lib/rubocop/directive_comment.rb +36 -3
- data/lib/rubocop/ext/regexp_node.rb +0 -1
- data/lib/rubocop/lsp/runtime.rb +6 -4
- data/lib/rubocop/lsp/server.rb +0 -2
- data/lib/rubocop/lsp/stdin_runner.rb +3 -1
- data/lib/rubocop/magic_comment.rb +8 -0
- data/lib/rubocop/options.rb +26 -11
- data/lib/rubocop/path_util.rb +4 -0
- 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/rspec/cop_helper.rb +13 -1
- data/lib/rubocop/rspec/shared_contexts.rb +35 -0
- data/lib/rubocop/rspec/support.rb +3 -0
- data/lib/rubocop/runner.rb +5 -1
- data/lib/rubocop/server/cache.rb +47 -11
- data/lib/rubocop/server/cli.rb +2 -2
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +30 -8
- data/lib/rubocop.rb +10 -1
- data/lib/ruby_lsp/rubocop/addon.rb +7 -10
- data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +25 -10
- metadata +43 -12
- data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -0,0 +1,261 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`,
|
7
|
+
# `to_a`, `to_h`, and `to_set`.
|
8
|
+
#
|
9
|
+
# When one of these methods is called on an object of the same type, that object
|
10
|
+
# is returned, making the call unnecessary. The cop detects conversion methods called
|
11
|
+
# on object literals, class constructors, class `[]` methods, and the `Kernel` methods
|
12
|
+
# `String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`.
|
13
|
+
#
|
14
|
+
# Specifically, these cases are detected for each conversion method:
|
15
|
+
#
|
16
|
+
# * `to_s` when called on a string literal, interpolated string, heredoc,
|
17
|
+
# or with `String.new` or `String()`.
|
18
|
+
# * `to_sym` when called on a symbol literal or interpolated symbol.
|
19
|
+
# * `to_i` when called on an integer literal or with `Integer()`.
|
20
|
+
# * `to_f` when called on a float literal of with `Float()`.
|
21
|
+
# * `to_r` when called on a rational literal or with `Rational()`.
|
22
|
+
# * `to_c` when called on a complex literal of with `Complex()`.
|
23
|
+
# * `to_a` when called on an array literal, or with `Array.new`, `Array()` or `Array[]`.
|
24
|
+
# * `to_h` when called on a hash literal, or with `Hash.new`, `Hash()` or `Hash[]`.
|
25
|
+
# * `to_set` when called on `Set.new` or `Set[]`.
|
26
|
+
#
|
27
|
+
# In all cases, chaining one same `to_*` conversion methods listed above is redundant.
|
28
|
+
#
|
29
|
+
# The cop can also register an offense for chaining conversion methods on methods that are
|
30
|
+
# expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s` and
|
31
|
+
# `foo.to_json.to_s`).
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# # bad
|
35
|
+
# "text".to_s
|
36
|
+
# :sym.to_sym
|
37
|
+
# 42.to_i
|
38
|
+
# 8.5.to_f
|
39
|
+
# 12r.to_r
|
40
|
+
# 1i.to_c
|
41
|
+
# [].to_a
|
42
|
+
# {}.to_h
|
43
|
+
# Set.new.to_set
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# "text"
|
47
|
+
# :sym
|
48
|
+
# 42
|
49
|
+
# 8.5
|
50
|
+
# 12r
|
51
|
+
# 1i
|
52
|
+
# []
|
53
|
+
# {}
|
54
|
+
# Set.new
|
55
|
+
#
|
56
|
+
# # bad
|
57
|
+
# Integer(var).to_i
|
58
|
+
#
|
59
|
+
# # good
|
60
|
+
# Integer(var)
|
61
|
+
#
|
62
|
+
# # good - chaining to a type constructor with exceptions suppressed
|
63
|
+
# # in this case, `Integer()` could return `nil`
|
64
|
+
# Integer(var, exception: false).to_i
|
65
|
+
#
|
66
|
+
# # bad - chaining the same conversion
|
67
|
+
# foo.to_s.to_s
|
68
|
+
#
|
69
|
+
# # good
|
70
|
+
# foo.to_s
|
71
|
+
#
|
72
|
+
# # bad - chaining a conversion to a method that is expected to return the same type
|
73
|
+
# foo.inspect.to_s
|
74
|
+
# foo.to_json.to_s
|
75
|
+
#
|
76
|
+
# # good
|
77
|
+
# foo.inspect
|
78
|
+
# foo.to_json
|
79
|
+
#
|
80
|
+
class RedundantTypeConversion < Base
|
81
|
+
extend AutoCorrector
|
82
|
+
|
83
|
+
MSG = 'Redundant `%<method>s` detected.'
|
84
|
+
|
85
|
+
# Maps conversion methods to the node types for the literals of that type
|
86
|
+
LITERAL_NODE_TYPES = {
|
87
|
+
to_s: %i[str dstr],
|
88
|
+
to_sym: %i[sym dsym],
|
89
|
+
to_i: %i[int],
|
90
|
+
to_f: %i[float],
|
91
|
+
to_r: %i[rational],
|
92
|
+
to_c: %i[complex],
|
93
|
+
to_a: %i[array],
|
94
|
+
to_h: %i[hash],
|
95
|
+
to_set: [] # sets don't have a literal or node type
|
96
|
+
}.freeze
|
97
|
+
|
98
|
+
# Maps each conversion method to the pattern matcher for that type's constructors
|
99
|
+
# Not every type has a constructor, for instance Symbol.
|
100
|
+
CONSTRUCTOR_MAPPING = {
|
101
|
+
to_s: 'string_constructor?',
|
102
|
+
to_i: 'integer_constructor?',
|
103
|
+
to_f: 'float_constructor?',
|
104
|
+
to_d: 'bigdecimal_constructor?',
|
105
|
+
to_r: 'rational_constructor?',
|
106
|
+
to_c: 'complex_constructor?',
|
107
|
+
to_a: 'array_constructor?',
|
108
|
+
to_h: 'hash_constructor?',
|
109
|
+
to_set: 'set_constructor?'
|
110
|
+
}.freeze
|
111
|
+
|
112
|
+
# Methods that already are expected to return a given type, which makes a further
|
113
|
+
# conversion redundant.
|
114
|
+
TYPED_METHODS = { to_s: %i[inspect to_json] }.freeze
|
115
|
+
|
116
|
+
CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
|
117
|
+
RESTRICT_ON_SEND = CONVERSION_METHODS + [:to_d]
|
118
|
+
|
119
|
+
private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING
|
120
|
+
|
121
|
+
# @!method type_constructor?(node, type_symbol)
|
122
|
+
def_node_matcher :type_constructor?, <<~PATTERN
|
123
|
+
(send {nil? (const {cbase nil?} :Kernel)} %1 ...)
|
124
|
+
PATTERN
|
125
|
+
|
126
|
+
# @!method string_constructor?(node)
|
127
|
+
def_node_matcher :string_constructor?, <<~PATTERN
|
128
|
+
{
|
129
|
+
(send (const {cbase nil?} :String) :new ...)
|
130
|
+
#type_constructor?(:String)
|
131
|
+
}
|
132
|
+
PATTERN
|
133
|
+
|
134
|
+
# @!method integer_constructor?(node)
|
135
|
+
def_node_matcher :integer_constructor?, <<~PATTERN
|
136
|
+
#type_constructor?(:Integer)
|
137
|
+
PATTERN
|
138
|
+
|
139
|
+
# @!method float_constructor?(node)
|
140
|
+
def_node_matcher :float_constructor?, <<~PATTERN
|
141
|
+
#type_constructor?(:Float)
|
142
|
+
PATTERN
|
143
|
+
|
144
|
+
# @!method bigdecimal_constructor?(node)
|
145
|
+
def_node_matcher :bigdecimal_constructor?, <<~PATTERN
|
146
|
+
#type_constructor?(:BigDecimal)
|
147
|
+
PATTERN
|
148
|
+
|
149
|
+
# @!method rational_constructor?(node)
|
150
|
+
def_node_matcher :rational_constructor?, <<~PATTERN
|
151
|
+
#type_constructor?(:Rational)
|
152
|
+
PATTERN
|
153
|
+
|
154
|
+
# @!method complex_constructor?(node)
|
155
|
+
def_node_matcher :complex_constructor?, <<~PATTERN
|
156
|
+
#type_constructor?(:Complex)
|
157
|
+
PATTERN
|
158
|
+
|
159
|
+
# @!method array_constructor?(node)
|
160
|
+
def_node_matcher :array_constructor?, <<~PATTERN
|
161
|
+
{
|
162
|
+
(send (const {cbase nil?} :Array) {:new :[]} ...)
|
163
|
+
#type_constructor?(:Array)
|
164
|
+
}
|
165
|
+
PATTERN
|
166
|
+
|
167
|
+
# @!method hash_constructor?(node)
|
168
|
+
def_node_matcher :hash_constructor?, <<~PATTERN
|
169
|
+
{
|
170
|
+
(block (send (const {cbase nil?} :Hash) :new) ...)
|
171
|
+
(send (const {cbase nil?} :Hash) {:new :[]} ...)
|
172
|
+
(send {nil? (const {cbase nil?} :Kernel)} :Hash ...)
|
173
|
+
}
|
174
|
+
PATTERN
|
175
|
+
|
176
|
+
# @!method set_constructor?(node)
|
177
|
+
def_node_matcher :set_constructor?, <<~PATTERN
|
178
|
+
{
|
179
|
+
(send (const {cbase nil?} :Set) {:new :[]} ...)
|
180
|
+
}
|
181
|
+
PATTERN
|
182
|
+
|
183
|
+
# @!method exception_false_keyword_argument?(node)
|
184
|
+
def_node_matcher :exception_false_keyword_argument?, <<~PATTERN
|
185
|
+
(hash (pair (sym :exception) false))
|
186
|
+
PATTERN
|
187
|
+
|
188
|
+
# rubocop:disable Metrics/AbcSize
|
189
|
+
def on_send(node)
|
190
|
+
return if hash_or_set_with_block?(node)
|
191
|
+
|
192
|
+
receiver = find_receiver(node)
|
193
|
+
return unless literal_receiver?(node, receiver) ||
|
194
|
+
constructor?(node, receiver) ||
|
195
|
+
chained_conversion?(node, receiver) ||
|
196
|
+
chained_to_typed_method?(node, receiver)
|
197
|
+
|
198
|
+
message = format(MSG, method: node.method_name)
|
199
|
+
|
200
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
201
|
+
corrector.remove(node.loc.dot.join(node.loc.selector))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
# rubocop:enable Metrics/AbcSize
|
205
|
+
alias on_csend on_send
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def hash_or_set_with_block?(node)
|
210
|
+
return false if !node.method?(:to_h) && !node.method?(:to_set)
|
211
|
+
|
212
|
+
node.parent&.any_block_type? || node.last_argument&.block_pass_type?
|
213
|
+
end
|
214
|
+
|
215
|
+
def find_receiver(node)
|
216
|
+
receiver = node.receiver
|
217
|
+
return unless receiver
|
218
|
+
|
219
|
+
while receiver.begin_type?
|
220
|
+
break unless receiver.children.one?
|
221
|
+
|
222
|
+
receiver = receiver.children.first
|
223
|
+
end
|
224
|
+
|
225
|
+
receiver
|
226
|
+
end
|
227
|
+
|
228
|
+
def literal_receiver?(node, receiver)
|
229
|
+
return false unless receiver
|
230
|
+
|
231
|
+
receiver.type?(*LITERAL_NODE_TYPES[node.method_name])
|
232
|
+
end
|
233
|
+
|
234
|
+
def constructor?(node, receiver)
|
235
|
+
matcher = CONSTRUCTOR_MAPPING[node.method_name]
|
236
|
+
return false unless matcher
|
237
|
+
|
238
|
+
public_send(matcher, receiver) && !constructor_suppresses_exceptions?(receiver)
|
239
|
+
end
|
240
|
+
|
241
|
+
def constructor_suppresses_exceptions?(receiver)
|
242
|
+
# If the constructor suppresses exceptions with `exception: false`, it is possible
|
243
|
+
# it could return `nil`, and therefore a chained conversion is not redundant.
|
244
|
+
receiver.arguments.any? { |arg| exception_false_keyword_argument?(arg) }
|
245
|
+
end
|
246
|
+
|
247
|
+
def chained_conversion?(node, receiver)
|
248
|
+
return false unless receiver&.call_type?
|
249
|
+
|
250
|
+
receiver.method?(node.method_name)
|
251
|
+
end
|
252
|
+
|
253
|
+
def chained_to_typed_method?(node, receiver)
|
254
|
+
return false unless receiver&.call_type?
|
255
|
+
|
256
|
+
TYPED_METHODS.fetch(node.method_name, []).include?(receiver.method_name)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -53,6 +53,7 @@ module RuboCop
|
|
53
53
|
# rubocop:enable Metrics/AbcSize
|
54
54
|
|
55
55
|
alias on_numblock on_block
|
56
|
+
alias on_itblock on_block
|
56
57
|
|
57
58
|
private
|
58
59
|
|
@@ -64,6 +65,8 @@ module RuboCop
|
|
64
65
|
(args (arg _)) ...)
|
65
66
|
(numblock
|
66
67
|
$(call _ {:each_with_index :with_index} ...) 1 ...)
|
68
|
+
(itblock
|
69
|
+
$(call _ {:each_with_index :with_index} ...) _ ...)
|
67
70
|
}
|
68
71
|
PATTERN
|
69
72
|
|
@@ -49,6 +49,7 @@ module RuboCop
|
|
49
49
|
end
|
50
50
|
|
51
51
|
alias on_numblock on_block
|
52
|
+
alias on_itblock on_block
|
52
53
|
|
53
54
|
private
|
54
55
|
|
@@ -59,6 +60,8 @@ module RuboCop
|
|
59
60
|
$(call _ {:each_with_object :with_object} _) (args (arg _)) ...)
|
60
61
|
(numblock
|
61
62
|
$(call _ {:each_with_object :with_object} _) 1 ...)
|
63
|
+
(itblock
|
64
|
+
$(call _ {:each_with_object :with_object} _) _ ...)
|
62
65
|
}
|
63
66
|
PATTERN
|
64
67
|
|
@@ -32,25 +32,23 @@ module RuboCop
|
|
32
32
|
class ReturnInVoidContext < Base
|
33
33
|
MSG = 'Do not return a value in `%<method>s`.'
|
34
34
|
|
35
|
+
# Returning out of these methods only exits the block itself.
|
36
|
+
SCOPE_CHANGING_METHODS = %i[lambda define_method define_singleton_method].freeze
|
37
|
+
|
35
38
|
def on_return(return_node)
|
36
39
|
return unless return_node.descendants.any?
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
return
|
41
|
-
|
41
|
+
def_node = return_node.each_ancestor(:any_def).first
|
42
|
+
return unless def_node&.void_context?
|
43
|
+
return if return_node.each_ancestor(:any_block).any? do |block_node|
|
44
|
+
SCOPE_CHANGING_METHODS.include?(block_node.method_name)
|
45
|
+
end
|
42
46
|
|
43
47
|
add_offense(
|
44
48
|
return_node.loc.keyword,
|
45
|
-
message: format(message, method:
|
49
|
+
message: format(message, method: def_node.method_name)
|
46
50
|
)
|
47
51
|
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def non_void_context(return_node)
|
52
|
-
return_node.each_ancestor(:block, :def, :defs).first
|
53
|
-
end
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
@@ -56,19 +56,26 @@ module RuboCop
|
|
56
56
|
|
57
57
|
outer_local_variable = variable_table.find_variable(variable.name)
|
58
58
|
return unless outer_local_variable
|
59
|
+
return if variable_used_in_declaration_of_outer?(variable, outer_local_variable)
|
59
60
|
return if same_conditions_node_different_branch?(variable, outer_local_variable)
|
60
61
|
|
61
62
|
message = format(MSG, variable: variable.name)
|
62
63
|
add_offense(variable.declaration_node, message: message)
|
63
64
|
end
|
64
65
|
|
66
|
+
private
|
67
|
+
|
68
|
+
def variable_used_in_declaration_of_outer?(variable, outer_local_variable)
|
69
|
+
variable.scope.node.each_ancestor.any?(outer_local_variable.declaration_node)
|
70
|
+
end
|
71
|
+
|
65
72
|
def same_conditions_node_different_branch?(variable, outer_local_variable)
|
66
73
|
variable_node = variable_node(variable)
|
67
74
|
return false unless node_or_its_ascendant_conditional?(variable_node)
|
68
75
|
|
69
76
|
outer_local_variable_node =
|
70
77
|
find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
|
71
|
-
return
|
78
|
+
return false unless outer_local_variable_node
|
72
79
|
return false unless outer_local_variable_node.conditional?
|
73
80
|
return true if variable_node == outer_local_variable_node
|
74
81
|
|
@@ -51,7 +51,18 @@ module RuboCop
|
|
51
51
|
|
52
52
|
# @!method hash_initialized_with_mutable_shared_object?(node)
|
53
53
|
def_node_matcher :hash_initialized_with_mutable_shared_object?, <<~PATTERN
|
54
|
-
|
54
|
+
{
|
55
|
+
(send (const {nil? cbase} :Hash) :new [
|
56
|
+
{array hash (send (const {nil? cbase} {:Array :Hash}) :new)}
|
57
|
+
!#capacity_keyword_argument?
|
58
|
+
])
|
59
|
+
(send (const {nil? cbase} :Hash) :new hash #capacity_keyword_argument?)
|
60
|
+
}
|
61
|
+
PATTERN
|
62
|
+
|
63
|
+
# @!method capacity_keyword_argument?(node)
|
64
|
+
def_node_matcher :capacity_keyword_argument?, <<~PATTERN
|
65
|
+
(hash (pair (sym :capacity) _))
|
55
66
|
PATTERN
|
56
67
|
|
57
68
|
def on_send(node)
|
@@ -116,7 +116,7 @@ module RuboCop
|
|
116
116
|
private
|
117
117
|
|
118
118
|
def comment_between_rescue_and_end?(node)
|
119
|
-
ancestor = node.each_ancestor(:kwbegin, :
|
119
|
+
ancestor = node.each_ancestor(:kwbegin, :any_def, :any_block).first
|
120
120
|
return false unless ancestor
|
121
121
|
|
122
122
|
end_line = ancestor.loc.end&.line || ancestor.loc.last_line
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for cases where exceptions unrelated to the numeric constructors `Integer()`,
|
7
|
+
# `Float()`, `BigDecimal()`, `Complex()`, and `Rational()` may be unintentionally swallowed.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# The cop is unsafe for autocorrection because unexpected errors occurring in the argument
|
11
|
+
# passed to numeric constructor (e.g., `Integer()`) can lead to incompatible behavior.
|
12
|
+
# For example, changing it to `Integer(potential_exception_method_call, exception: false)`
|
13
|
+
# ensures that exceptions raised by `potential_exception_method_call` are not ignored.
|
14
|
+
#
|
15
|
+
# [source,ruby]
|
16
|
+
# ----
|
17
|
+
# Integer(potential_exception_method_call) rescue nil
|
18
|
+
# ----
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# Integer(arg) rescue nil
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# begin
|
27
|
+
# Integer(arg)
|
28
|
+
# rescue
|
29
|
+
# nil
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # bad
|
33
|
+
# begin
|
34
|
+
# Integer(arg)
|
35
|
+
# rescue
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # good
|
39
|
+
# Integer(arg, exception: false)
|
40
|
+
#
|
41
|
+
class SuppressedExceptionInNumberConversion < Base
|
42
|
+
extend AutoCorrector
|
43
|
+
extend TargetRubyVersion
|
44
|
+
|
45
|
+
MSG = 'Use `%<prefer>s` instead.'
|
46
|
+
EXPECTED_EXCEPTION_CLASSES = %w[ArgumentError TypeError ::ArgumentError ::TypeError].freeze
|
47
|
+
|
48
|
+
# @!method numeric_constructor_rescue_nil(node)
|
49
|
+
def_node_matcher :numeric_constructor_rescue_nil, <<~PATTERN
|
50
|
+
(rescue
|
51
|
+
$#numeric_method?
|
52
|
+
(resbody nil? nil? (nil)) nil?)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
# @!method begin_numeric_constructor_rescue_nil(node)
|
56
|
+
def_node_matcher :begin_numeric_constructor_rescue_nil, <<~PATTERN
|
57
|
+
(kwbegin
|
58
|
+
(rescue
|
59
|
+
$#numeric_method?
|
60
|
+
(resbody $_? nil?
|
61
|
+
{(nil) nil?}) nil?))
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
# @!method numeric_method?(node)
|
65
|
+
def_node_matcher :numeric_method?, <<~PATTERN
|
66
|
+
{
|
67
|
+
(call #constructor_receiver? {:Integer :BigDecimal :Complex :Rational}
|
68
|
+
_ _?)
|
69
|
+
(call #constructor_receiver? :Float
|
70
|
+
_)
|
71
|
+
}
|
72
|
+
PATTERN
|
73
|
+
|
74
|
+
# @!method constructor_receiver?(node)
|
75
|
+
def_node_matcher :constructor_receiver?, <<~PATTERN
|
76
|
+
{nil? (const {nil? cbase} :Kernel)}
|
77
|
+
PATTERN
|
78
|
+
|
79
|
+
minimum_target_ruby_version 2.6
|
80
|
+
|
81
|
+
# rubocop:disable Metrics/AbcSize
|
82
|
+
def on_rescue(node)
|
83
|
+
if (method, exception_classes = begin_numeric_constructor_rescue_nil(node.parent))
|
84
|
+
return unless expected_exception_classes_only?(exception_classes)
|
85
|
+
|
86
|
+
node = node.parent
|
87
|
+
else
|
88
|
+
return unless (method = numeric_constructor_rescue_nil(node))
|
89
|
+
end
|
90
|
+
|
91
|
+
arguments = method.arguments.map(&:source) << 'exception: false'
|
92
|
+
prefer = "#{method.method_name}(#{arguments.join(', ')})"
|
93
|
+
prefer = "#{method.receiver.source}#{method.loc.dot.source}#{prefer}" if method.receiver
|
94
|
+
|
95
|
+
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
96
|
+
corrector.replace(node, prefer)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
# rubocop:enable Metrics/AbcSize
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def expected_exception_classes_only?(exception_classes)
|
104
|
+
return true unless (arguments = exception_classes.first)
|
105
|
+
|
106
|
+
(arguments.values.map(&:source) - EXPECTED_EXCEPTION_CLASSES).none?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
40
40
|
# top-level return node's ancestors should not be of block, def, or
|
41
41
|
# defs type.
|
42
42
|
def top_level_return?(return_node)
|
43
|
-
return_node.each_ancestor(:block, :
|
43
|
+
return_node.each_ancestor(:block, :any_def).none?
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -53,6 +53,7 @@ module RuboCop
|
|
53
53
|
end
|
54
54
|
|
55
55
|
alias on_numblock on_block
|
56
|
+
alias on_itblock on_block
|
56
57
|
|
57
58
|
private
|
58
59
|
|
@@ -74,6 +75,7 @@ module RuboCop
|
|
74
75
|
|
75
76
|
def arg_count(node)
|
76
77
|
return node.children[1] if node.numblock_type? # the maximum numbered param for the block
|
78
|
+
return 1 if node.itblock_type? # `it` block parameter is always one
|
77
79
|
|
78
80
|
# Only `arg`, `optarg` and `mlhs` (destructuring) count as arguments that
|
79
81
|
# can be used. Keyword arguments are not used for these methods so are
|
@@ -101,9 +101,8 @@ module RuboCop
|
|
101
101
|
check(node) if loop_method?(node)
|
102
102
|
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
end
|
104
|
+
alias on_numblock on_block
|
105
|
+
alias on_itblock on_block
|
107
106
|
|
108
107
|
private
|
109
108
|
|
@@ -189,8 +188,9 @@ module RuboCop
|
|
189
188
|
|
190
189
|
def preceded_by_continue_statement?(break_statement)
|
191
190
|
break_statement.left_siblings.any? do |sibling|
|
192
|
-
# Numblocks have the arguments count as a number
|
193
|
-
|
191
|
+
# Numblocks have the arguments count as a number or,
|
192
|
+
# itblocks have `:it` symbol in the AST.
|
193
|
+
next if sibling.is_a?(Integer) || sibling.is_a?(Symbol)
|
194
194
|
next if sibling.loop_keyword? || loop_method?(sibling)
|
195
195
|
|
196
196
|
sibling.each_descendant(*CONTINUE_KEYWORDS).any?
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for useless constant scoping. Private constants must be defined using
|
7
|
+
# `private_constant`. Even if `private` access modifier is used, it is public scope despite
|
8
|
+
# its appearance.
|
9
|
+
#
|
10
|
+
# It does not support autocorrection due to behavior change and multiple ways to fix it.
|
11
|
+
# Or a public constant may be intended.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class Foo
|
17
|
+
# private
|
18
|
+
# PRIVATE_CONST = 42
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# class Foo
|
23
|
+
# PRIVATE_CONST = 42
|
24
|
+
# private_constant :PRIVATE_CONST
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# class Foo
|
29
|
+
# PUBLIC_CONST = 42 # If private scope is not intended.
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
class UselessConstantScoping < Base
|
33
|
+
MSG = 'Useless `private` access modifier for constant scope.'
|
34
|
+
|
35
|
+
# @!method private_constants(node)
|
36
|
+
def_node_matcher :private_constants, <<~PATTERN
|
37
|
+
(send nil? :private_constant $...)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def on_casgn(node)
|
41
|
+
return unless after_private_modifier?(node.left_siblings)
|
42
|
+
return if private_constantize?(node.right_siblings, node.name)
|
43
|
+
|
44
|
+
add_offense(node)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def after_private_modifier?(left_siblings)
|
50
|
+
access_modifier_candidates = left_siblings.compact.select do |left_sibling|
|
51
|
+
left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
|
52
|
+
end
|
53
|
+
|
54
|
+
access_modifier_candidates.any? do |candidate|
|
55
|
+
candidate.command?(:private) && candidate.arguments.none?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def private_constantize?(right_siblings, const_value)
|
60
|
+
private_constant_arguments = right_siblings.map { |node| private_constants(node) }
|
61
|
+
|
62
|
+
private_constant_values = private_constant_arguments.flatten.filter_map do |constant|
|
63
|
+
constant.value.to_sym if constant.respond_to?(:value)
|
64
|
+
end
|
65
|
+
|
66
|
+
private_constant_values.include?(const_value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -86,6 +86,7 @@ module RuboCop
|
|
86
86
|
check_expression(node.body)
|
87
87
|
end
|
88
88
|
alias on_numblock on_block
|
89
|
+
alias on_itblock on_block
|
89
90
|
|
90
91
|
def on_begin(node)
|
91
92
|
check_begin(node)
|
@@ -125,9 +126,14 @@ module RuboCop
|
|
125
126
|
check_nonmutating(expr)
|
126
127
|
end
|
127
128
|
|
129
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
128
130
|
def check_void_op(node, &block)
|
129
131
|
node = node.children.first while node.begin_type?
|
130
132
|
return unless node.call_type? && OPERATORS.include?(node.method_name)
|
133
|
+
if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
|
134
|
+
return
|
135
|
+
end
|
136
|
+
|
131
137
|
return if block && yield(node)
|
132
138
|
|
133
139
|
add_offense(node.loc.selector,
|
@@ -135,6 +141,7 @@ module RuboCop
|
|
135
141
|
autocorrect_void_op(corrector, node)
|
136
142
|
end
|
137
143
|
end
|
144
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
138
145
|
|
139
146
|
def check_var(node)
|
140
147
|
return unless node.variable? || node.const_type?
|