rubocop 1.88.0 → 1.89.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 +72 -12
- data/lib/rubocop/comment_config.rb +35 -19
- data/lib/rubocop/config.rb +53 -0
- data/lib/rubocop/config_validator.rb +38 -1
- data/lib/rubocop/cop/badge.rb +8 -0
- data/lib/rubocop/cop/base.rb +19 -12
- data/lib/rubocop/cop/bundler/gem_comment.rb +3 -1
- data/lib/rubocop/cop/bundler.rb +19 -0
- data/lib/rubocop/cop/commissioner.rb +13 -11
- data/lib/rubocop/cop/corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -1
- data/lib/rubocop/cop/gemspec.rb +22 -0
- data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +17 -0
- data/lib/rubocop/cop/layout/class_structure.rb +14 -4
- data/lib/rubocop/cop/layout/comment_indentation.rb +9 -2
- data/lib/rubocop/cop/layout/condition_position.rb +13 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
- data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +14 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +13 -14
- data/lib/rubocop/cop/layout/indentation_width.rb +31 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
- data/lib/rubocop/cop/layout/line_length.rb +95 -17
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
- data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +6 -2
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
- data/lib/rubocop/cop/layout.rb +114 -0
- data/lib/rubocop/cop/lazy_loader.rb +41 -0
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -1
- data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
- data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
- data/lib/rubocop/cop/lint/debugger.rb +13 -1
- data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
- data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_super.rb +31 -1
- data/lib/rubocop/cop/lint/name_typo.rb +223 -0
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +7 -0
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +44 -2
- data/lib/rubocop/cop/lint/to_json.rb +8 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +32 -8
- data/lib/rubocop/cop/lint/unreachable_code.rb +9 -4
- data/lib/rubocop/cop/lint/unused_private_method.rb +157 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +19 -8
- data/lib/rubocop/cop/lint.rb +176 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics.rb +31 -0
- data/lib/rubocop/cop/migration/department_name.rb +0 -4
- data/lib/rubocop/cop/migration.rb +13 -0
- data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
- data/lib/rubocop/cop/mixin/hash_subset.rb +8 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +4 -0
- data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +43 -7
- data/lib/rubocop/cop/mixin.rb +78 -77
- data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
- data/lib/rubocop/cop/naming/file_name.rb +4 -3
- data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +9 -0
- data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +9 -3
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/naming.rb +31 -0
- data/lib/rubocop/cop/offense.rb +10 -6
- data/lib/rubocop/cop/registry.rb +153 -43
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/security/marshal_load.rb +1 -1
- data/lib/rubocop/cop/security.rb +19 -0
- data/lib/rubocop/cop/style/accessor_grouping.rb +11 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
- data/lib/rubocop/cop/style/array_intersect.rb +24 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
- data/lib/rubocop/cop/style/collection_compact.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
- data/lib/rubocop/cop/style/copyright.rb +6 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +4 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
- data/lib/rubocop/cop/style/dir_empty.rb +4 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
- data/lib/rubocop/cop/style/documentation.rb +47 -0
- data/lib/rubocop/cop/style/documentation_method.rb +5 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
- data/lib/rubocop/cop/style/empty_class_definition.rb +8 -1
- data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
- data/lib/rubocop/cop/style/empty_literal.rb +7 -2
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
- data/lib/rubocop/cop/style/endless_method.rb +0 -10
- data/lib/rubocop/cop/style/env_home.rb +4 -0
- data/lib/rubocop/cop/style/even_odd.rb +11 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
- data/lib/rubocop/cop/style/file_null.rb +4 -2
- data/lib/rubocop/cop/style/format_string.rb +13 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +14 -6
- data/lib/rubocop/cop/style/hash_lookup_method.rb +2 -6
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -0
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -4
- data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -52
- data/lib/rubocop/cop/style/if_with_semicolon.rb +9 -1
- data/lib/rubocop/cop/style/inline_comment.rb +1 -1
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
- data/lib/rubocop/cop/style/lambda.rb +11 -1
- data/lib/rubocop/cop/style/lambda_call.rb +11 -0
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
- data/lib/rubocop/cop/style/map_into_array.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -5
- data/lib/rubocop/cop/style/method_def_parentheses.rb +5 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +3 -0
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +40 -7
- data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
- data/lib/rubocop/cop/style/module_function.rb +4 -2
- data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
- data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
- data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
- data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
- data/lib/rubocop/cop/style/nil_lambda.rb +8 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
- data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
- data/lib/rubocop/cop/style/option_hash.rb +1 -1
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +11 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
- data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
- data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +40 -3
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +80 -129
- data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
- data/lib/rubocop/cop/style/redundant_self.rb +9 -0
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +23 -4
- data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
- data/lib/rubocop/cop/style/semicolon.rb +4 -4
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +17 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
- data/lib/rubocop/cop/style/static_class.rb +21 -0
- data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/style.rb +310 -0
- data/lib/rubocop/cop/team.rb +66 -7
- data/lib/rubocop/cop/variable_force/variable.rb +18 -6
- data/lib/rubocop/cops_documentation_generator.rb +1 -1
- data/lib/rubocop/directive_comment.rb +2 -0
- data/lib/rubocop/feature_loader.rb +0 -6
- data/lib/rubocop/lsp/routes.rb +41 -3
- data/lib/rubocop/lsp/runtime.rb +4 -0
- data/lib/rubocop/lsp/server.rb +4 -0
- data/lib/rubocop/lsp/stdin_runner.rb +20 -0
- data/lib/rubocop/pending_cops_reporter.rb +1 -6
- data/lib/rubocop/project_index_loader.rb +16 -0
- data/lib/rubocop/result_cache.rb +6 -4
- data/lib/rubocop/rspec/shared_contexts.rb +14 -2
- data/lib/rubocop/runner.rb +100 -33
- data/lib/rubocop/server/cache.rb +10 -5
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +17 -626
- metadata +18 -3
|
@@ -91,6 +91,8 @@ module RuboCop
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def on_new_investigation
|
|
94
|
+
return unless @flagged_terms_regex
|
|
95
|
+
|
|
94
96
|
investigate_filepath if cop_config['CheckFilepaths']
|
|
95
97
|
investigate_tokens
|
|
96
98
|
end
|
|
@@ -144,7 +146,7 @@ module RuboCop
|
|
|
144
146
|
def preprocess_flagged_terms
|
|
145
147
|
allowed_strings = []
|
|
146
148
|
flagged_term_strings = []
|
|
147
|
-
cop_config['FlaggedTerms'].each do |term, term_definition|
|
|
149
|
+
(cop_config['FlaggedTerms'] || {}).each do |term, term_definition|
|
|
148
150
|
next if term_definition.nil?
|
|
149
151
|
|
|
150
152
|
allowed_strings.concat(process_allowed_regex(term_definition['AllowedRegex']))
|
|
@@ -181,7 +183,11 @@ module RuboCop
|
|
|
181
183
|
end
|
|
182
184
|
|
|
183
185
|
def set_regexes(flagged_term_strings, allowed_strings)
|
|
184
|
-
|
|
186
|
+
# With no flagged terms an empty regex would match everything, so leave the
|
|
187
|
+
# regex nil and let `on_new_investigation` treat the cop as a no-op.
|
|
188
|
+
unless flagged_term_strings.empty?
|
|
189
|
+
@flagged_terms_regex = array_to_ignorecase_regex(flagged_term_strings)
|
|
190
|
+
end
|
|
185
191
|
@allowed_regex = array_to_ignorecase_regex(allowed_strings) unless allowed_strings.empty?
|
|
186
192
|
end
|
|
187
193
|
|
|
@@ -174,6 +174,7 @@ module RuboCop
|
|
|
174
174
|
|
|
175
175
|
method_node, method_name = find_definition(node)
|
|
176
176
|
return unless method_node
|
|
177
|
+
return unless nameable_method?(method_name)
|
|
177
178
|
|
|
178
179
|
body = method_node.body
|
|
179
180
|
return unless body == node || body.children.last == node
|
|
@@ -209,6 +210,7 @@ module RuboCop
|
|
|
209
210
|
|
|
210
211
|
method_node, method_name = find_definition(node)
|
|
211
212
|
return false unless method_node
|
|
213
|
+
return false unless nameable_method?(method_name)
|
|
212
214
|
|
|
213
215
|
defined_memoized?(method_node.body, arg.name) do |defined_ivar, return_ivar, ivar_assign|
|
|
214
216
|
return false if matches?(method_name, ivar_assign)
|
|
@@ -250,6 +252,13 @@ module RuboCop
|
|
|
250
252
|
nil
|
|
251
253
|
end
|
|
252
254
|
|
|
255
|
+
# Operator and other non-word method names (e.g. `[]`, `+`, `<=>`) cannot form a
|
|
256
|
+
# valid instance variable name, so there is no matching ivar to enforce and a
|
|
257
|
+
# suggested correction like `@[]` would be invalid Ruby.
|
|
258
|
+
def nameable_method?(method_name)
|
|
259
|
+
/\A[a-zA-Z_]\w*\z/.match?(method_name.to_s.delete('!?='))
|
|
260
|
+
end
|
|
261
|
+
|
|
253
262
|
def matches?(method_name, ivar_assign)
|
|
254
263
|
return true if ivar_assign.nil? || INITIALIZE_METHODS.include?(method_name)
|
|
255
264
|
|
|
@@ -21,6 +21,11 @@ module RuboCop
|
|
|
21
21
|
# offenses if the method has a Sorbet `sig` with a return type of
|
|
22
22
|
# `T::Boolean`. Dynamic methods are not supported with this configuration.
|
|
23
23
|
#
|
|
24
|
+
# When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
|
|
25
|
+
# installed, methods that override a method defined by an ancestor
|
|
26
|
+
# elsewhere in the project are not reported, since renaming an override
|
|
27
|
+
# breaks the inherited contract.
|
|
28
|
+
#
|
|
24
29
|
# @example NamePrefix: ['is_', 'has_', 'have_'] (default)
|
|
25
30
|
# # bad
|
|
26
31
|
# def is_even(value)
|
|
@@ -101,6 +106,7 @@ module RuboCop
|
|
|
101
106
|
# def_node_matcher(:even?) { |value| }
|
|
102
107
|
#
|
|
103
108
|
class PredicatePrefix < Base
|
|
109
|
+
include ProjectIndexHelp
|
|
104
110
|
include AllowedMethods
|
|
105
111
|
|
|
106
112
|
# @!method dynamic_method_define(node)
|
|
@@ -124,18 +130,24 @@ module RuboCop
|
|
|
124
130
|
end
|
|
125
131
|
|
|
126
132
|
def on_def(node)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
next if allowed_method_name?(method_name, prefix)
|
|
131
|
-
next if use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean')
|
|
133
|
+
method_name = node.method_name.to_s
|
|
134
|
+
offending_prefixes = offending_prefixes_for(node, method_name)
|
|
135
|
+
return if offending_prefixes.empty? || overrides_inherited_method?(node)
|
|
132
136
|
|
|
137
|
+
offending_prefixes.each do |prefix|
|
|
133
138
|
add_offense(
|
|
134
139
|
node.loc.name,
|
|
135
140
|
message: message(method_name, expected_name(method_name, prefix))
|
|
136
141
|
)
|
|
137
142
|
end
|
|
138
143
|
end
|
|
144
|
+
|
|
145
|
+
def offending_prefixes_for(node, method_name)
|
|
146
|
+
predicate_prefixes.reject do |prefix|
|
|
147
|
+
allowed_method_name?(method_name, prefix) ||
|
|
148
|
+
(use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean'))
|
|
149
|
+
end
|
|
150
|
+
end
|
|
139
151
|
alias on_defs on_def
|
|
140
152
|
|
|
141
153
|
def validate_config
|
|
@@ -198,6 +210,22 @@ module RuboCop
|
|
|
198
210
|
def method_definition_macro?(macro_name)
|
|
199
211
|
cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
|
|
200
212
|
end
|
|
213
|
+
|
|
214
|
+
# When `AllCops/UseProjectIndex` is enabled, methods that override a
|
|
215
|
+
# method defined by an ancestor elsewhere in the project are not
|
|
216
|
+
# reported: renaming an override breaks the inherited contract.
|
|
217
|
+
def overrides_inherited_method?(node)
|
|
218
|
+
return false unless project_index
|
|
219
|
+
return false unless (namespace_node = node.each_ancestor(:class, :module).first)
|
|
220
|
+
|
|
221
|
+
declaration = resolve_constant_in_index(namespace_node.identifier)
|
|
222
|
+
return false unless declaration.is_a?(Rubydex::Namespace)
|
|
223
|
+
|
|
224
|
+
scope = node.defs_type? ? indexed_singleton_of(declaration) : declaration
|
|
225
|
+
!scope.nil? && inherited_index_member?(scope, "#{node.method_name}()")
|
|
226
|
+
rescue StandardError
|
|
227
|
+
false
|
|
228
|
+
end
|
|
201
229
|
end
|
|
202
230
|
end
|
|
203
231
|
end
|
|
@@ -95,11 +95,14 @@ module RuboCop
|
|
|
95
95
|
|
|
96
96
|
def autocorrect(corrector, node, range, offending_name, preferred_name)
|
|
97
97
|
corrector.replace(range, preferred_name)
|
|
98
|
-
|
|
98
|
+
# Once the exception variable is reassigned, later references point to a
|
|
99
|
+
# different value, so stop correcting after the reassignment - both in the
|
|
100
|
+
# body and in the code following the `begin`/`rescue`.
|
|
101
|
+
return if correct_node(corrector, node.body, offending_name, preferred_name)
|
|
99
102
|
return unless (kwbegin_node = node.parent.each_ancestor(:kwbegin).first)
|
|
100
103
|
|
|
101
104
|
kwbegin_node.right_siblings.each do |child_node|
|
|
102
|
-
correct_node(corrector, child_node, offending_name, preferred_name)
|
|
105
|
+
break if correct_node(corrector, child_node, offending_name, preferred_name)
|
|
103
106
|
end
|
|
104
107
|
end
|
|
105
108
|
|
|
@@ -113,6 +116,8 @@ module RuboCop
|
|
|
113
116
|
end
|
|
114
117
|
end
|
|
115
118
|
|
|
119
|
+
# Returns the reassignment node once the exception variable is reassigned (a truthy
|
|
120
|
+
# signal to stop correcting later references), or `nil` when no reassignment is found.
|
|
116
121
|
# rubocop:disable Metrics/MethodLength
|
|
117
122
|
def correct_node(corrector, node, offending_name, preferred_name)
|
|
118
123
|
return unless node
|
|
@@ -131,9 +136,10 @@ module RuboCop
|
|
|
131
136
|
|
|
132
137
|
if child_node.type?(:masgn, :lvasgn)
|
|
133
138
|
correct_reassignment(corrector, child_node, offending_name, preferred_name)
|
|
134
|
-
|
|
139
|
+
return child_node
|
|
135
140
|
end
|
|
136
141
|
end
|
|
142
|
+
nil
|
|
137
143
|
end
|
|
138
144
|
# rubocop:enable Metrics/MethodLength
|
|
139
145
|
|
|
@@ -132,7 +132,9 @@ module RuboCop
|
|
|
132
132
|
|
|
133
133
|
def on_sym(node)
|
|
134
134
|
@node = node
|
|
135
|
-
|
|
135
|
+
|
|
136
|
+
# Prism parses a quoted empty hash key (`{ "": value }`) as an empty symbol node.
|
|
137
|
+
return if node.value.empty? || allowed_identifier?(node.value)
|
|
136
138
|
|
|
137
139
|
check_name(node, node.value, node) if cop_config['CheckSymbols']
|
|
138
140
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Naming` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Naming
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :AccessorMethodName, "#{__dir__}/naming/accessor_method_name"
|
|
11
|
+
register_cop :AsciiIdentifiers, "#{__dir__}/naming/ascii_identifiers"
|
|
12
|
+
register_cop :BlockForwarding, "#{__dir__}/naming/block_forwarding"
|
|
13
|
+
register_cop :BlockParameterName, "#{__dir__}/naming/block_parameter_name"
|
|
14
|
+
register_cop :ClassAndModuleCamelCase, "#{__dir__}/naming/class_and_module_camel_case"
|
|
15
|
+
register_cop :ConstantName, "#{__dir__}/naming/constant_name"
|
|
16
|
+
register_cop :FileName, "#{__dir__}/naming/file_name"
|
|
17
|
+
register_cop :HeredocDelimiterCase, "#{__dir__}/naming/heredoc_delimiter_case"
|
|
18
|
+
register_cop :HeredocDelimiterNaming, "#{__dir__}/naming/heredoc_delimiter_naming"
|
|
19
|
+
register_cop :InclusiveLanguage, "#{__dir__}/naming/inclusive_language"
|
|
20
|
+
register_cop :MemoizedInstanceVariableName, "#{__dir__}/naming/memoized_instance_variable_name"
|
|
21
|
+
register_cop :MethodName, "#{__dir__}/naming/method_name"
|
|
22
|
+
register_cop :MethodParameterName, "#{__dir__}/naming/method_parameter_name"
|
|
23
|
+
register_cop :BinaryOperatorParameterName, "#{__dir__}/naming/binary_operator_parameter_name"
|
|
24
|
+
register_cop :PredicateMethod, "#{__dir__}/naming/predicate_method"
|
|
25
|
+
register_cop :PredicatePrefix, "#{__dir__}/naming/predicate_prefix"
|
|
26
|
+
register_cop :RescuedExceptionsVariableName, "#{__dir__}/naming/rescued_exceptions_variable_name"
|
|
27
|
+
register_cop :VariableName, "#{__dir__}/naming/variable_name"
|
|
28
|
+
register_cop :VariableNumber, "#{__dir__}/naming/variable_number"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/rubocop/cop/offense.rb
CHANGED
|
@@ -91,6 +91,12 @@ module RuboCop
|
|
|
91
91
|
status = :uncorrected, corrector = nil)
|
|
92
92
|
@severity = RuboCop::Cop::Severity.new(severity)
|
|
93
93
|
@location = location
|
|
94
|
+
|
|
95
|
+
# Pre-compute the position eagerly because the offense is frozen and
|
|
96
|
+
# `location.line` / `location.column` are expensive to compute; sorting
|
|
97
|
+
# many offenses calls them repeatedly through `#<=>`.
|
|
98
|
+
@line = location.line
|
|
99
|
+
@column = location.column
|
|
94
100
|
@message = message.freeze
|
|
95
101
|
@cop_name = cop_name.freeze
|
|
96
102
|
@status = status
|
|
@@ -104,6 +110,8 @@ module RuboCop
|
|
|
104
110
|
|
|
105
111
|
def marshal_load(array)
|
|
106
112
|
@severity, @location, @message, @cop_name, @status = array
|
|
113
|
+
@line = @location.line
|
|
114
|
+
@column = @location.column
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
# @api public
|
|
@@ -167,14 +175,10 @@ module RuboCop
|
|
|
167
175
|
end
|
|
168
176
|
|
|
169
177
|
# @api private
|
|
170
|
-
|
|
171
|
-
location.line
|
|
172
|
-
end
|
|
178
|
+
attr_reader :line
|
|
173
179
|
|
|
174
180
|
# @api private
|
|
175
|
-
|
|
176
|
-
location.column
|
|
177
|
-
end
|
|
181
|
+
attr_reader :column
|
|
178
182
|
|
|
179
183
|
# @api private
|
|
180
184
|
def source_line
|
data/lib/rubocop/cop/registry.rb
CHANGED
|
@@ -50,21 +50,37 @@ module RuboCop
|
|
|
50
50
|
|
|
51
51
|
def initialize(cops = [], options = {})
|
|
52
52
|
@departments = Set.new
|
|
53
|
+
# Maps each badge to its cop class, or to the fully qualified constant name (a `String`)
|
|
54
|
+
# when the cop is registered for lazy loading and its file is not loaded yet.
|
|
55
|
+
# A single insertion-ordered map keeps the registration order stable no matter when
|
|
56
|
+
# a lazy-loaded cop's file gets loaded, because reassigning an existing key preserves
|
|
57
|
+
# its position; the cop execution order depends on this order.
|
|
53
58
|
@cops_by_badge = {}
|
|
54
|
-
@lazy_loaded_cops_by_badge = {}
|
|
55
59
|
|
|
56
60
|
@enrollment_queue = cops
|
|
57
61
|
@options = options
|
|
58
62
|
|
|
59
63
|
@enabled_cache = {}.compare_by_identity
|
|
60
64
|
@disabled_cache = {}.compare_by_identity
|
|
65
|
+
@disabled_names_cache = {}.compare_by_identity
|
|
61
66
|
@warnings = {}
|
|
62
67
|
end
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
# Registers a cop by its badge and constant name without loading the class.
|
|
70
|
+
# The constant is resolved (loading the cop's file through `autoload`) only
|
|
71
|
+
# when the cop class itself is needed.
|
|
72
|
+
#
|
|
73
|
+
# @param badge [Badge, String] the badge, or the qualified cop name
|
|
74
|
+
# @param constant_name [String] the fully qualified constant name
|
|
75
|
+
def lazy_load(badge, constant_name)
|
|
76
|
+
badge = Badge.parse(badge) if badge.is_a?(String)
|
|
77
|
+
|
|
78
|
+
# File any cops enlisted earlier first, so that the registration
|
|
79
|
+
# order follows the call order.
|
|
80
|
+
clear_enrollment_queue
|
|
81
|
+
|
|
66
82
|
@departments << badge.department
|
|
67
|
-
@
|
|
83
|
+
@cops_by_badge[badge] = constant_name unless @cops_by_badge[badge].is_a?(Class)
|
|
68
84
|
end
|
|
69
85
|
|
|
70
86
|
def enlist(cop)
|
|
@@ -72,7 +88,16 @@ module RuboCop
|
|
|
72
88
|
end
|
|
73
89
|
|
|
74
90
|
def dismiss(cop)
|
|
75
|
-
|
|
91
|
+
dismissed = !@enrollment_queue.delete(cop).nil?
|
|
92
|
+
|
|
93
|
+
# A lazy-loaded cop that was not enlisted yet is dismissed by removing its registration.
|
|
94
|
+
# Anonymous cop classes cannot have a badge.
|
|
95
|
+
if !cop.name.nil? && @cops_by_badge[cop.badge].is_a?(String)
|
|
96
|
+
@cops_by_badge.delete(cop.badge)
|
|
97
|
+
dismissed = true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
raise "Cop #{cop} could not be dismissed" unless dismissed
|
|
76
101
|
end
|
|
77
102
|
|
|
78
103
|
# @return [Array<Symbol>] list of departments for current cops.
|
|
@@ -83,12 +108,26 @@ module RuboCop
|
|
|
83
108
|
|
|
84
109
|
# @return [Registry] Cops for that specific department.
|
|
85
110
|
def with_department(department)
|
|
86
|
-
|
|
111
|
+
filter_by_badge { |badge| badge.department == department }
|
|
87
112
|
end
|
|
88
113
|
|
|
89
114
|
# @return [Registry] Cops not for a specific department.
|
|
90
115
|
def without_department(department)
|
|
91
|
-
|
|
116
|
+
filter_by_badge { |badge| badge.department != department }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Returns a new registry containing the cops whose badge satisfies the given block,
|
|
120
|
+
# keeping lazy-loaded cops unloaded.
|
|
121
|
+
#
|
|
122
|
+
# @api private
|
|
123
|
+
# @return [Registry]
|
|
124
|
+
def filter_by_badge(options = {})
|
|
125
|
+
clear_enrollment_queue
|
|
126
|
+
copy = self.class.new([], options)
|
|
127
|
+
@cops_by_badge.each do |badge, cop_or_name|
|
|
128
|
+
copy.add_entry(badge, cop_or_name) if yield(badge)
|
|
129
|
+
end
|
|
130
|
+
copy
|
|
92
131
|
end
|
|
93
132
|
|
|
94
133
|
# @return [Boolean] Checks if given name is department
|
|
@@ -97,7 +136,7 @@ module RuboCop
|
|
|
97
136
|
end
|
|
98
137
|
|
|
99
138
|
def contains_cop_matching?(names)
|
|
100
|
-
|
|
139
|
+
registered_badges.any? { |badge| badge.match_name?(names) }
|
|
101
140
|
end
|
|
102
141
|
|
|
103
142
|
# Convert a user provided cop name into a properly namespaced name
|
|
@@ -160,9 +199,10 @@ module RuboCop
|
|
|
160
199
|
|
|
161
200
|
def unqualified_cop_names
|
|
162
201
|
clear_enrollment_queue
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
202
|
+
|
|
203
|
+
@unqualified_cop_names ||= @cops_by_badge.keys.to_set do |badge|
|
|
204
|
+
File.basename(badge.to_s)
|
|
205
|
+
end << 'RedundantCopDisableDirective'
|
|
166
206
|
end
|
|
167
207
|
|
|
168
208
|
def qualify_badge(badge)
|
|
@@ -187,51 +227,64 @@ module RuboCop
|
|
|
187
227
|
|
|
188
228
|
def length
|
|
189
229
|
clear_enrollment_queue
|
|
190
|
-
@cops_by_badge.size
|
|
230
|
+
@cops_by_badge.size
|
|
191
231
|
end
|
|
192
232
|
|
|
233
|
+
# Returns the enabled cop classes, loading lazy-loaded cops only when they are enabled.
|
|
234
|
+
#
|
|
235
|
+
# @return [Array<Class>]
|
|
193
236
|
def enabled(config)
|
|
194
|
-
@enabled_cache[config] ||=
|
|
237
|
+
@enabled_cache[config] ||= registered_badges.filter_map do |badge|
|
|
238
|
+
next unless enabled_cop_name?(badge.to_s, config)
|
|
239
|
+
|
|
240
|
+
cop_or_name = @cops_by_badge[badge]
|
|
241
|
+
cop_or_name.is_a?(String) ? load_lazy_cop(badge) : cop_or_name
|
|
242
|
+
end
|
|
195
243
|
end
|
|
196
244
|
|
|
197
245
|
def disabled(config)
|
|
198
246
|
@disabled_cache[config] ||= reject { |cop| enabled?(cop, config) }
|
|
199
247
|
end
|
|
200
248
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if options.fetch(:safe, false)
|
|
211
|
-
cop_enabled && cfg.fetch('Safe', true)
|
|
212
|
-
else
|
|
213
|
-
cop_enabled
|
|
249
|
+
# Returns the names of the disabled cops without loading any of them.
|
|
250
|
+
#
|
|
251
|
+
# @api private
|
|
252
|
+
# @return [Array<String>]
|
|
253
|
+
def disabled_names(config)
|
|
254
|
+
@disabled_names_cache[config] ||= registered_badges.filter_map do |badge|
|
|
255
|
+
name = badge.to_s
|
|
256
|
+
name unless enabled_cop_name?(name, config)
|
|
214
257
|
end
|
|
215
258
|
end
|
|
216
259
|
|
|
217
|
-
def
|
|
260
|
+
def enabled?(cop, config)
|
|
261
|
+
enabled_cop_name?(cop.cop_name, config)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# @param cop [Class, String, nil] the cop class or the qualified cop name
|
|
265
|
+
def enabled_pending_cop?(cop_cfg, config, cop = nil)
|
|
218
266
|
return false if @options[:disable_pending_cops]
|
|
267
|
+
return false unless cop_cfg.fetch('Enabled') == 'pending'
|
|
268
|
+
return true if @options[:enable_pending_cops]
|
|
269
|
+
return config.enabled_new_cops? unless cop
|
|
219
270
|
|
|
220
|
-
|
|
221
|
-
|
|
271
|
+
cop_name = cop.is_a?(String) ? cop : cop.cop_name
|
|
272
|
+
config.enabled_new_cop?(cop_name)
|
|
222
273
|
end
|
|
223
274
|
|
|
224
275
|
def names
|
|
225
276
|
clear_enrollment_queue
|
|
226
|
-
@cops_by_badge.keys.map(&:to_s)
|
|
277
|
+
@cops_by_badge.keys.map(&:to_s)
|
|
227
278
|
end
|
|
228
279
|
|
|
229
280
|
def cops_for_department(department)
|
|
230
|
-
|
|
281
|
+
with_department(department.to_sym).cops
|
|
231
282
|
end
|
|
232
283
|
|
|
233
284
|
def names_for_department(department)
|
|
234
|
-
|
|
285
|
+
department = department.to_sym
|
|
286
|
+
|
|
287
|
+
registered_badges.filter_map { |badge| badge.to_s if badge.department == department }
|
|
235
288
|
end
|
|
236
289
|
|
|
237
290
|
def ==(other)
|
|
@@ -259,7 +312,9 @@ module RuboCop
|
|
|
259
312
|
def find_by_cop_name(cop_name)
|
|
260
313
|
clear_enrollment_queue
|
|
261
314
|
badge = Badge.parse(cop_name)
|
|
262
|
-
@cops_by_badge[badge]
|
|
315
|
+
cop_or_name = @cops_by_badge[badge]
|
|
316
|
+
|
|
317
|
+
cop_or_name.is_a?(String) ? load_lazy_cop(badge) : cop_or_name
|
|
263
318
|
end
|
|
264
319
|
|
|
265
320
|
# When a cop name is given returns a single-element array with the cop class.
|
|
@@ -287,35 +342,90 @@ module RuboCop
|
|
|
287
342
|
@warnings[path]
|
|
288
343
|
end
|
|
289
344
|
|
|
345
|
+
protected
|
|
346
|
+
|
|
347
|
+
# Adds an already-loaded cop class or a lazy-load constant name under the given badge,
|
|
348
|
+
# used to build filtered copies.
|
|
349
|
+
def add_entry(badge, cop_or_name)
|
|
350
|
+
@departments << badge.department
|
|
351
|
+
@cops_by_badge[badge] = cop_or_name
|
|
352
|
+
end
|
|
353
|
+
|
|
290
354
|
private
|
|
291
355
|
|
|
292
356
|
def initialize_copy(reg)
|
|
293
|
-
|
|
357
|
+
super
|
|
358
|
+
@cops_by_badge = @cops_by_badge.dup
|
|
359
|
+
@enrollment_queue = @enrollment_queue.dup
|
|
360
|
+
@departments = @departments.dup
|
|
361
|
+
@enabled_cache = {}.compare_by_identity
|
|
362
|
+
@disabled_cache = {}.compare_by_identity
|
|
363
|
+
@disabled_names_cache = {}.compare_by_identity
|
|
364
|
+
@warnings = {}
|
|
365
|
+
@unqualified_cop_names = nil
|
|
294
366
|
end
|
|
295
367
|
|
|
296
368
|
def clear_enrollment_queue
|
|
297
369
|
return if @enrollment_queue.empty?
|
|
298
370
|
|
|
299
371
|
@enrollment_queue.each do |cop|
|
|
372
|
+
# Reassigning a badge that was registered for lazy loading keeps
|
|
373
|
+
# its original position in the insertion-ordered map.
|
|
300
374
|
@cops_by_badge[cop.badge] = cop
|
|
301
375
|
@departments << cop.department
|
|
302
376
|
end
|
|
303
377
|
@enrollment_queue = []
|
|
304
378
|
end
|
|
305
379
|
|
|
306
|
-
def
|
|
307
|
-
|
|
380
|
+
def registered_badges
|
|
381
|
+
clear_enrollment_queue
|
|
382
|
+
@cops_by_badge.keys
|
|
308
383
|
end
|
|
309
384
|
|
|
310
|
-
def
|
|
311
|
-
|
|
312
|
-
return unless constant_name
|
|
385
|
+
def enabled_cop_name?(cop_name, config)
|
|
386
|
+
return true if options[:only]&.include?(cop_name)
|
|
313
387
|
|
|
314
|
-
|
|
388
|
+
# We need to use the cop name in this case, because `for_cop` uses
|
|
389
|
+
# caching which expects cop names or cop classes as keys.
|
|
390
|
+
cfg = config.for_cop(cop_name)
|
|
391
|
+
|
|
392
|
+
cop_enabled = cfg.fetch('Enabled') == true || enabled_pending_cop?(cfg, config, cop_name)
|
|
393
|
+
|
|
394
|
+
if options.fetch(:safe, false)
|
|
395
|
+
cop_enabled && cfg.fetch('Safe', true)
|
|
396
|
+
else
|
|
397
|
+
cop_enabled
|
|
398
|
+
end
|
|
315
399
|
end
|
|
316
400
|
|
|
317
|
-
def
|
|
318
|
-
|
|
401
|
+
def load_all_lazy_cops
|
|
402
|
+
# Take a snapshot because loading a cop can load (and thereby register)
|
|
403
|
+
# another lazy-loaded cop, e.g. its superclass.
|
|
404
|
+
badges = @cops_by_badge.keys
|
|
405
|
+
badges.each { |badge| load_lazy_cop(badge) if @cops_by_badge[badge].is_a?(String) }
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# @return [Class, nil] the loaded cop class, or nil if the cop excluded
|
|
409
|
+
# itself from the registry while being loaded
|
|
410
|
+
def load_lazy_cop(badge)
|
|
411
|
+
constant_name = @cops_by_badge[badge]
|
|
412
|
+
return constant_name unless constant_name.is_a?(String)
|
|
413
|
+
|
|
414
|
+
cop = Kernel.const_get(constant_name)
|
|
415
|
+
if equal?(self.class.global)
|
|
416
|
+
# Resolving the constant fires the autoload, which makes
|
|
417
|
+
# `Base.inherited` enlist the class into the global registry.
|
|
418
|
+
# Enrolling through the queue keeps `exclude_from_registry` honored:
|
|
419
|
+
# a cop that dismissed itself leaves a dangling constant name behind
|
|
420
|
+
# and is removed from the registry.
|
|
421
|
+
clear_enrollment_queue
|
|
422
|
+
cop_or_name = @cops_by_badge[badge]
|
|
423
|
+
@cops_by_badge.delete(badge) if cop_or_name.is_a?(String)
|
|
424
|
+
cop_or_name.is_a?(Class) ? cop_or_name : nil
|
|
425
|
+
else
|
|
426
|
+
# `Base.inherited` enlists into the global registry, not this copy.
|
|
427
|
+
@cops_by_badge[badge] = cop
|
|
428
|
+
end
|
|
319
429
|
end
|
|
320
430
|
|
|
321
431
|
def resolve_badge(given_badge, real_badge, source_path, warn: true)
|
|
@@ -335,7 +445,7 @@ module RuboCop
|
|
|
335
445
|
|
|
336
446
|
def registered?(badge)
|
|
337
447
|
clear_enrollment_queue
|
|
338
|
-
@cops_by_badge.key?(badge)
|
|
448
|
+
@cops_by_badge.key?(badge)
|
|
339
449
|
end
|
|
340
450
|
end
|
|
341
451
|
end
|
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
|
37
37
|
return unless (receiver = node.receiver) && receiver.source == 'IO'
|
|
38
38
|
|
|
39
39
|
argument = node.first_argument
|
|
40
|
-
return if argument
|
|
40
|
+
return if argument&.str_type? && argument.value.strip.start_with?('|')
|
|
41
41
|
|
|
42
42
|
add_offense(node, message: format(MSG, method_name: node.method_name)) do |corrector|
|
|
43
43
|
corrector.replace(receiver, 'File')
|
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
|
25
25
|
# @!method marshal_load(node)
|
|
26
26
|
def_node_matcher :marshal_load, <<~PATTERN
|
|
27
27
|
(send (const {nil? cbase} :Marshal) ${:load :restore}
|
|
28
|
-
!(send (const {nil? cbase} :Marshal) :dump ...))
|
|
28
|
+
!(send (const {nil? cbase} :Marshal) :dump ...) _?)
|
|
29
29
|
PATTERN
|
|
30
30
|
|
|
31
31
|
def on_send(node)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Security` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Security
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :CompoundHash, "#{__dir__}/security/compound_hash"
|
|
11
|
+
register_cop :Eval, "#{__dir__}/security/eval"
|
|
12
|
+
register_cop :IoMethods, "#{__dir__}/security/io_methods"
|
|
13
|
+
register_cop :JSONLoad, "#{__dir__}/security/json_load"
|
|
14
|
+
register_cop :MarshalLoad, "#{__dir__}/security/marshal_load"
|
|
15
|
+
register_cop :Open, "#{__dir__}/security/open"
|
|
16
|
+
register_cop :YAMLLoad, "#{__dir__}/security/yaml_load"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -203,13 +203,23 @@ module RuboCop
|
|
|
203
203
|
end
|
|
204
204
|
|
|
205
205
|
def range_with_trailing_argument_comment(node)
|
|
206
|
-
comment =
|
|
206
|
+
comment = trailing_argument_comment(node)
|
|
207
207
|
if comment
|
|
208
208
|
add_range(node.source_range, comment.source_range)
|
|
209
209
|
else
|
|
210
210
|
node
|
|
211
211
|
end
|
|
212
212
|
end
|
|
213
|
+
|
|
214
|
+
# For a single-line declaration the parser associates the trailing
|
|
215
|
+
# comment with the first argument, not `last_argument`, so look through
|
|
216
|
+
# all arguments for a comment that trails the whole node.
|
|
217
|
+
def trailing_argument_comment(node)
|
|
218
|
+
comments = node.arguments.filter_map do |argument|
|
|
219
|
+
processed_source.ast_with_comments[argument].last
|
|
220
|
+
end
|
|
221
|
+
comments.find { |comment| comment.source_range.begin_pos >= node.source_range.end_pos }
|
|
222
|
+
end
|
|
213
223
|
end
|
|
214
224
|
end
|
|
215
225
|
end
|