rubocop 1.79.2 → 1.82.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +35 -7
- data/config/obsoletion.yml +4 -0
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +8 -3
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config_loader.rb +5 -2
- data/lib/rubocop/config_loader_resolver.rb +7 -6
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +8 -7
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +17 -5
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/else_layout.rb +19 -0
- data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +15 -6
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
- data/lib/rubocop/cop/lint/useless_or.rb +15 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
- data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
- data/lib/rubocop/cop/naming/method_name.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_method.rb +19 -6
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/array_intersect.rb +46 -12
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/case_equality.rb +11 -13
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +8 -14
- data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +0 -6
- data/lib/rubocop/cop/style/endless_method.rb +15 -2
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/guard_clause.rb +0 -11
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
- data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +14 -11
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/safe_navigation.rb +18 -1
- data/lib/rubocop/cop/style/semicolon.rb +23 -7
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +9 -7
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/directive_comment.rb +46 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -5
- data/lib/rubocop/lsp/diagnostic.rb +10 -14
- data/lib/rubocop/lsp/routes.rb +31 -2
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- data/lib/rubocop/magic_comment.rb +20 -0
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +7 -8
- data/lib/rubocop/result_cache.rb +39 -28
- data/lib/rubocop/rspec/shared_contexts.rb +2 -2
- data/lib/rubocop/rspec/support.rb +1 -1
- data/lib/rubocop/runner.rb +10 -4
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/target_ruby.rb +11 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +2 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +9 -7
|
@@ -9,6 +9,13 @@ module RuboCop
|
|
|
9
9
|
# on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
|
|
10
10
|
# appended after these methods will never evaluate.
|
|
11
11
|
#
|
|
12
|
+
# @safety
|
|
13
|
+
# As shown in the examples below, there are generally two possible ways to correct the
|
|
14
|
+
# offense, but this cop’s autocorrection always chooses the option that preserves the
|
|
15
|
+
# current behavior. While this does not change how the code behaves, that option is not
|
|
16
|
+
# necessarily the appropriate fix in every situation. For this reason, the autocorrection
|
|
17
|
+
# provided by this cop is considered unsafe.
|
|
18
|
+
#
|
|
12
19
|
# @example
|
|
13
20
|
#
|
|
14
21
|
# # bad
|
|
@@ -64,6 +71,8 @@ module RuboCop
|
|
|
64
71
|
# x&.to_s or fallback
|
|
65
72
|
#
|
|
66
73
|
class UselessOr < Base
|
|
74
|
+
extend AutoCorrector
|
|
75
|
+
|
|
67
76
|
MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
|
|
68
77
|
|
|
69
78
|
TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
|
|
@@ -89,8 +98,12 @@ module RuboCop
|
|
|
89
98
|
private
|
|
90
99
|
|
|
91
100
|
def report_offense(or_node, truthy_node)
|
|
92
|
-
add_offense(
|
|
93
|
-
|
|
101
|
+
add_offense(
|
|
102
|
+
or_node.loc.operator.join(or_node.rhs.source_range),
|
|
103
|
+
message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source)
|
|
104
|
+
) do |corrector|
|
|
105
|
+
corrector.replace(or_node, or_node.lhs.source)
|
|
106
|
+
end
|
|
94
107
|
end
|
|
95
108
|
end
|
|
96
109
|
end
|
|
@@ -16,6 +16,12 @@ module RuboCop
|
|
|
16
16
|
# enumerator.each { |item| item >= 2 } #=> [2, 3]
|
|
17
17
|
# ----
|
|
18
18
|
#
|
|
19
|
+
# NOTE: Return values in assignment method definitions such as `def foo=(arg)` are
|
|
20
|
+
# detected because they are in a void context. However, autocorrection does not remove
|
|
21
|
+
# the return value, as that would change behavior. In such cases, whether to remove
|
|
22
|
+
# the return value or rename the method to something more appropriate should be left to
|
|
23
|
+
# the user.
|
|
24
|
+
#
|
|
19
25
|
# @example CheckForMethodsWithNoSideEffects: false (default)
|
|
20
26
|
# # bad
|
|
21
27
|
# def some_method
|
|
@@ -233,6 +239,7 @@ module RuboCop
|
|
|
233
239
|
|
|
234
240
|
def autocorrect_void_expression(corrector, node)
|
|
235
241
|
return if node.parent.if_type?
|
|
242
|
+
return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
|
|
236
243
|
|
|
237
244
|
corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
|
|
238
245
|
end
|
|
@@ -84,7 +84,10 @@ module RuboCop
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def assignment?(node)
|
|
87
|
-
|
|
87
|
+
if node.masgn_type? || node.shorthand_asgn?
|
|
88
|
+
compound_assignment(node)
|
|
89
|
+
return false
|
|
90
|
+
end
|
|
88
91
|
|
|
89
92
|
node.for_type? ||
|
|
90
93
|
(node.respond_to?(:setter_method?) && node.setter_method?) ||
|
|
@@ -101,8 +104,6 @@ module RuboCop
|
|
|
101
104
|
child.respond_to?(:setter_method?) && !child.setter_method?
|
|
102
105
|
end
|
|
103
106
|
@assignment += will_be_miscounted
|
|
104
|
-
|
|
105
|
-
false
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
def simple_assignment?(node)
|
|
@@ -14,6 +14,8 @@ module RuboCop
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def too_long?(node)
|
|
17
|
+
return false unless max_line_length
|
|
18
|
+
|
|
17
19
|
lines = processed_source.lines[(node.first_line - 1)...node.last_line]
|
|
18
20
|
to_single_line(lines.join("\n")).length > max_line_length
|
|
19
21
|
end
|
|
@@ -27,10 +29,6 @@ module RuboCop
|
|
|
27
29
|
.gsub(/\s*\\?\n\s*/, ' ') # Any other line break, with or without backslash
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
def max_line_length
|
|
31
|
-
config.for_cop('Layout/LineLength')['Max']
|
|
32
|
-
end
|
|
33
|
-
|
|
34
32
|
def comment_within?(node)
|
|
35
33
|
comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line }
|
|
36
34
|
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
return node.loc.name if node.casgn_type?
|
|
60
60
|
|
|
61
61
|
if LSP.enabled?
|
|
62
|
-
end_range = node.loc
|
|
62
|
+
end_range = node.loc?(:name) ? node.loc.name : node.loc.begin
|
|
63
63
|
node.source_range.begin.join(end_range)
|
|
64
64
|
else
|
|
65
65
|
node.source_range
|
|
@@ -19,8 +19,7 @@ module RuboCop
|
|
|
19
19
|
def check_end_kw_alignment(node, align_ranges)
|
|
20
20
|
return if ignored_node?(node)
|
|
21
21
|
|
|
22
|
-
end_loc = node.loc.end
|
|
23
|
-
return if accept_end_kw_alignment?(end_loc)
|
|
22
|
+
return unless (end_loc = node.loc.end)
|
|
24
23
|
|
|
25
24
|
matching = matching_ranges(end_loc, align_ranges)
|
|
26
25
|
|
|
@@ -57,11 +56,6 @@ module RuboCop
|
|
|
57
56
|
add_offense(end_loc, message: msg) { |corrector| autocorrect(corrector, node) }
|
|
58
57
|
end
|
|
59
58
|
|
|
60
|
-
def accept_end_kw_alignment?(end_loc)
|
|
61
|
-
end_loc.nil? || # Discard modifier forms of if/while/until.
|
|
62
|
-
!/\A[ \t]*end/.match?(processed_source.lines[end_loc.line - 1])
|
|
63
|
-
end
|
|
64
|
-
|
|
65
59
|
def style_parameter_name
|
|
66
60
|
'EnforcedStyleAlignWith'
|
|
67
61
|
end
|
|
@@ -8,8 +8,27 @@ module RuboCop
|
|
|
8
8
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
config.for_cop('Layout/LineLength')['
|
|
11
|
+
def allow_rbs_inline_annotation?
|
|
12
|
+
config.for_cop('Layout/LineLength')['AllowRBSInlineAnnotation']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def rbs_inline_annotation_on_source_line?(line_index)
|
|
16
|
+
source_line_number = line_index + processed_source.buffer.first_line
|
|
17
|
+
comment = processed_source.comment_at_line(source_line_number)
|
|
18
|
+
|
|
19
|
+
return false unless comment
|
|
20
|
+
|
|
21
|
+
comment.text.start_with?(/#:|#\[.+\]|#\|/)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def allow_cop_directives?
|
|
25
|
+
# TODO: This logic for backward compatibility with deprecated `IgnoreCopDirectives` option.
|
|
26
|
+
# The following three lines will be removed in RuboCop 2.0.
|
|
27
|
+
ignore_cop_directives = config.for_cop('Layout/LineLength')['IgnoreCopDirectives']
|
|
28
|
+
return true if ignore_cop_directives
|
|
29
|
+
return false if ignore_cop_directives == false
|
|
30
|
+
|
|
31
|
+
config.for_cop('Layout/LineLength')['AllowCopDirectives']
|
|
13
32
|
end
|
|
14
33
|
|
|
15
34
|
def directive_on_source_line?(line_index)
|
|
@@ -73,7 +73,7 @@ module RuboCop
|
|
|
73
73
|
|
|
74
74
|
def location(node)
|
|
75
75
|
if LSP.enabled?
|
|
76
|
-
end_range = node.loc
|
|
76
|
+
end_range = node.loc?(:name) ? node.loc.name : node.loc.begin
|
|
77
77
|
node.source_range.begin.join(end_range)
|
|
78
78
|
else
|
|
79
79
|
node.source_range
|
|
@@ -200,7 +200,7 @@ module RuboCop
|
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def grouped_expression?(node)
|
|
203
|
-
node.begin_type? && node.loc
|
|
203
|
+
node.begin_type? && node.loc?(:begin) && node.loc.begin
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
def inside_arg_list_parentheses?(node, ancestor)
|
|
@@ -8,8 +8,8 @@ module RuboCop
|
|
|
8
8
|
MSG = 'Space missing after %<token>s.'
|
|
9
9
|
|
|
10
10
|
def on_new_investigation
|
|
11
|
-
each_missing_space(processed_source.tokens) do |token|
|
|
12
|
-
add_offense(token.pos, message: format(MSG, token: kind
|
|
11
|
+
each_missing_space(processed_source.tokens) do |token, kind|
|
|
12
|
+
add_offense(token.pos, message: format(MSG, token: kind)) do |corrector|
|
|
13
13
|
PunctuationCorrector.add_space(corrector, token)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -19,11 +19,12 @@ module RuboCop
|
|
|
19
19
|
|
|
20
20
|
def each_missing_space(tokens)
|
|
21
21
|
tokens.each_cons(2) do |token1, token2|
|
|
22
|
-
|
|
22
|
+
kind = kind(token1, token2)
|
|
23
|
+
next unless kind
|
|
23
24
|
next unless space_missing?(token1, token2)
|
|
24
25
|
next unless space_required_before?(token2)
|
|
25
26
|
|
|
26
|
-
yield token1
|
|
27
|
+
yield token1, kind
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -100,12 +100,6 @@ module RuboCop
|
|
|
100
100
|
node.parent.send_type?
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
def max_line_length
|
|
104
|
-
return unless config.cop_enabled?('Layout/LineLength')
|
|
105
|
-
|
|
106
|
-
config.for_cop('Layout/LineLength')['Max']
|
|
107
|
-
end
|
|
108
|
-
|
|
109
103
|
def comment_disables_cop?(comment)
|
|
110
104
|
regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
|
|
111
105
|
Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
|
|
@@ -95,13 +95,16 @@ module RuboCop
|
|
|
95
95
|
node.multiline? && !allowed_multiline_argument?(node)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
+
# rubocop:disable Metrics/AbcSize
|
|
98
99
|
def method_name_and_arguments_on_same_line?(node)
|
|
99
|
-
return false
|
|
100
|
+
return false if !node.call_type? || node.last_line != node.last_argument.last_line
|
|
101
|
+
return true if node.last_argument.hash_type? && node.last_argument.braces?
|
|
100
102
|
|
|
101
|
-
line = node.loc.selector
|
|
103
|
+
line = node.loc.selector&.line || node.loc.line
|
|
102
104
|
|
|
103
|
-
line == node.last_argument.last_line
|
|
105
|
+
line == node.last_argument.last_line
|
|
104
106
|
end
|
|
107
|
+
# rubocop:enable Metrics/AbcSize
|
|
105
108
|
|
|
106
109
|
# A single argument with the closing bracket on the same line as the end
|
|
107
110
|
# of the argument is not considered multiline, even if the argument
|
|
@@ -140,7 +143,7 @@ module RuboCop
|
|
|
140
143
|
end
|
|
141
144
|
|
|
142
145
|
def last_item_precedes_newline?(node)
|
|
143
|
-
after_last_item = node.children.last.source_range.end.join(node.
|
|
146
|
+
after_last_item = node.children.last.source_range.end.join(node.source_range.end)
|
|
144
147
|
|
|
145
148
|
after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
|
|
146
149
|
end
|
|
@@ -185,7 +188,7 @@ module RuboCop
|
|
|
185
188
|
|
|
186
189
|
def heredoc?(node)
|
|
187
190
|
return false unless node.is_a?(RuboCop::AST::Node)
|
|
188
|
-
return true if node.loc
|
|
191
|
+
return true if node.loc?(:heredoc_body)
|
|
189
192
|
|
|
190
193
|
return heredoc_send?(node) if node.send_type?
|
|
191
194
|
|
|
@@ -147,7 +147,9 @@ module RuboCop
|
|
|
147
147
|
alias on_defs on_def
|
|
148
148
|
|
|
149
149
|
def on_alias(node)
|
|
150
|
-
|
|
150
|
+
return unless (new_identifier = node.new_identifier).sym_type?
|
|
151
|
+
|
|
152
|
+
handle_method_name(new_identifier, new_identifier.value)
|
|
151
153
|
end
|
|
152
154
|
|
|
153
155
|
private
|
|
@@ -198,7 +200,7 @@ module RuboCop
|
|
|
198
200
|
|
|
199
201
|
if forbidden_name?(name.to_s)
|
|
200
202
|
register_forbidden_name(node)
|
|
201
|
-
elsif !OPERATOR_METHODS.include?(name)
|
|
203
|
+
elsif !OPERATOR_METHODS.include?(name.to_sym)
|
|
202
204
|
check_name(node, name, range_position(node))
|
|
203
205
|
end
|
|
204
206
|
end
|
|
@@ -232,7 +234,7 @@ module RuboCop
|
|
|
232
234
|
end
|
|
233
235
|
|
|
234
236
|
def range_position(node)
|
|
235
|
-
if node.loc
|
|
237
|
+
if node.loc?(:selector)
|
|
236
238
|
selector_end_pos = node.loc.selector.end_pos + 1
|
|
237
239
|
expr_end_pos = node.source_range.end_pos
|
|
238
240
|
|
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
|
14
14
|
# method calls are assumed to return boolean values. The cop does not make an assessment
|
|
15
15
|
# if the return type is unknown (non-predicate method calls, variables, etc.).
|
|
16
16
|
#
|
|
17
|
-
# NOTE:
|
|
17
|
+
# NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
|
|
18
18
|
#
|
|
19
19
|
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
|
20
20
|
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
|
@@ -113,6 +113,18 @@ module RuboCop
|
|
|
113
113
|
# true
|
|
114
114
|
# end
|
|
115
115
|
#
|
|
116
|
+
# @example AllowedMethods: [call] (default)
|
|
117
|
+
# # good
|
|
118
|
+
# def call
|
|
119
|
+
# foo == bar
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# @example AllowedPatterns: [\Afoo]
|
|
123
|
+
# # good
|
|
124
|
+
# def foo?
|
|
125
|
+
# 'foo'
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
116
128
|
# @example AllowBangMethods: false (default)
|
|
117
129
|
# # bad
|
|
118
130
|
# def save!
|
|
@@ -149,7 +161,8 @@ module RuboCop
|
|
|
149
161
|
private
|
|
150
162
|
|
|
151
163
|
def allowed?(node)
|
|
152
|
-
|
|
164
|
+
node.method?(:initialize) ||
|
|
165
|
+
allowed_method?(node.method_name) ||
|
|
153
166
|
matches_allowed_pattern?(node.method_name) ||
|
|
154
167
|
allowed_bang_method?(node) ||
|
|
155
168
|
node.operator_method? ||
|
|
@@ -180,8 +193,7 @@ module RuboCop
|
|
|
180
193
|
return_values << extract_return_value(return_node)
|
|
181
194
|
end
|
|
182
195
|
|
|
183
|
-
|
|
184
|
-
return_values << last_value if last_value
|
|
196
|
+
return_values << last_value(node)
|
|
185
197
|
|
|
186
198
|
process_return_values(return_values)
|
|
187
199
|
end
|
|
@@ -234,8 +246,9 @@ module RuboCop
|
|
|
234
246
|
end
|
|
235
247
|
|
|
236
248
|
def last_value(node)
|
|
237
|
-
value = node.begin_type? ? node.children.last : node
|
|
238
|
-
|
|
249
|
+
value = node.begin_type? ? node.children.last || s(:nil) : node
|
|
250
|
+
|
|
251
|
+
value.return_type? ? extract_return_value(value) : value
|
|
239
252
|
end
|
|
240
253
|
|
|
241
254
|
def process_return_values(return_values)
|
|
@@ -6,22 +6,40 @@ module RuboCop
|
|
|
6
6
|
# Checks for the use of JSON class methods which have potential
|
|
7
7
|
# security issues.
|
|
8
8
|
#
|
|
9
|
+
# `JSON.load` and similar methods allow deserialization of arbitrary ruby objects:
|
|
10
|
+
#
|
|
11
|
+
# [source,ruby]
|
|
12
|
+
# ----
|
|
13
|
+
# require 'json/add/string'
|
|
14
|
+
# result = JSON.load('{ "json_class": "String", "raw": [72, 101, 108, 108, 111] }')
|
|
15
|
+
# pp result # => "Hello"
|
|
16
|
+
# ----
|
|
17
|
+
#
|
|
18
|
+
# Never use `JSON.load` for untrusted user input. Prefer `JSON.parse` unless you have
|
|
19
|
+
# a concrete use-case for `JSON.load`.
|
|
20
|
+
#
|
|
21
|
+
# NOTE: Starting with `json` gem version 2.8.0, triggering this behavior without explicitly
|
|
22
|
+
# passing the `create_additions` keyword argument emits a deprecation warning, with the
|
|
23
|
+
# goal of being secure by default in the next major version 3.0.0.
|
|
24
|
+
#
|
|
9
25
|
# @safety
|
|
10
26
|
# This cop's autocorrection is unsafe because it's potentially dangerous.
|
|
11
|
-
# If using a stream, like `JSON.load(open('file'))`,
|
|
27
|
+
# If using a stream, like `JSON.load(open('file'))`, you will need to call
|
|
12
28
|
# `#read` manually, like `JSON.parse(open('file').read)`.
|
|
13
|
-
# If reading single values (rather than proper JSON objects), like
|
|
14
|
-
# `JSON.load('false')`, it will need to pass the `quirks_mode: true`
|
|
15
|
-
# option, like `JSON.parse('false', quirks_mode: true)`.
|
|
16
29
|
# Other similar issues may apply.
|
|
17
30
|
#
|
|
18
31
|
# @example
|
|
19
32
|
# # bad
|
|
20
|
-
# JSON.load(
|
|
21
|
-
# JSON.restore(
|
|
33
|
+
# JSON.load('{}')
|
|
34
|
+
# JSON.restore('{}')
|
|
22
35
|
#
|
|
23
36
|
# # good
|
|
24
|
-
# JSON.parse(
|
|
37
|
+
# JSON.parse('{}')
|
|
38
|
+
# JSON.unsafe_load('{}')
|
|
39
|
+
#
|
|
40
|
+
# # good - explicit use of `create_additions` option
|
|
41
|
+
# JSON.load('{}', create_additions: true)
|
|
42
|
+
# JSON.load('{}', create_additions: false)
|
|
25
43
|
#
|
|
26
44
|
class JSONLoad < Base
|
|
27
45
|
extend AutoCorrector
|
|
@@ -29,13 +47,17 @@ module RuboCop
|
|
|
29
47
|
MSG = 'Prefer `JSON.parse` over `JSON.%<method>s`.'
|
|
30
48
|
RESTRICT_ON_SEND = %i[load restore].freeze
|
|
31
49
|
|
|
32
|
-
# @!method
|
|
33
|
-
def_node_matcher :
|
|
34
|
-
(
|
|
50
|
+
# @!method insecure_json_load(node)
|
|
51
|
+
def_node_matcher :insecure_json_load, <<~PATTERN
|
|
52
|
+
(
|
|
53
|
+
send (const {nil? cbase} :JSON) ${:load :restore}
|
|
54
|
+
...
|
|
55
|
+
!(hash `(sym $:create_additions))
|
|
56
|
+
)
|
|
35
57
|
PATTERN
|
|
36
58
|
|
|
37
59
|
def on_send(node)
|
|
38
|
-
|
|
60
|
+
insecure_json_load(node) do |method|
|
|
39
61
|
add_offense(node.loc.selector, message: format(MSG, method: method)) do |corrector|
|
|
40
62
|
corrector.replace(node.loc.selector, 'parse')
|
|
41
63
|
end
|
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
|
10
10
|
# * `(array1 & array2).any?`
|
|
11
11
|
# * `(array1.intersection(array2)).any?`
|
|
12
12
|
# * `array1.any? { |elem| array2.member?(elem) }`
|
|
13
|
+
# * `(array1 & array2).count > 0`
|
|
14
|
+
# * `(array1 & array2).size > 0`
|
|
13
15
|
#
|
|
14
16
|
# can be replaced with `array1.intersect?(array2)`.
|
|
15
17
|
#
|
|
@@ -51,6 +53,19 @@ module RuboCop
|
|
|
51
53
|
# array1.intersect?(array2)
|
|
52
54
|
# !array1.intersect?(array2)
|
|
53
55
|
#
|
|
56
|
+
# # bad
|
|
57
|
+
# (array1 & array2).count > 0
|
|
58
|
+
# (array1 & array2).count.positive?
|
|
59
|
+
# (array1 & array2).count != 0
|
|
60
|
+
#
|
|
61
|
+
# (array1 & array2).count == 0
|
|
62
|
+
# (array1 & array2).count.zero?
|
|
63
|
+
#
|
|
64
|
+
# # good
|
|
65
|
+
# array1.intersect?(array2)
|
|
66
|
+
#
|
|
67
|
+
# !array1.intersect?(array2)
|
|
68
|
+
#
|
|
54
69
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
|
55
70
|
# # good
|
|
56
71
|
# (array1 & array2).present?
|
|
@@ -73,17 +88,33 @@ module RuboCop
|
|
|
73
88
|
PREDICATES = %i[any? empty? none?].to_set.freeze
|
|
74
89
|
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
|
75
90
|
|
|
91
|
+
ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
|
|
92
|
+
|
|
76
93
|
# @!method bad_intersection_check?(node, predicates)
|
|
77
94
|
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
|
78
|
-
(call
|
|
95
|
+
$(call
|
|
79
96
|
{
|
|
80
97
|
(begin (send $_ :& $_))
|
|
81
|
-
(call
|
|
98
|
+
(call $!nil? :intersection $_)
|
|
82
99
|
}
|
|
83
100
|
$%1
|
|
84
101
|
)
|
|
85
102
|
PATTERN
|
|
86
103
|
|
|
104
|
+
# @!method intersection_size_check?(node, predicates)
|
|
105
|
+
def_node_matcher :intersection_size_check?, <<~PATTERN
|
|
106
|
+
(call
|
|
107
|
+
$(call
|
|
108
|
+
{
|
|
109
|
+
(begin (send $_ :& $_))
|
|
110
|
+
(call $!nil? :intersection $_)
|
|
111
|
+
}
|
|
112
|
+
%ARRAY_SIZE_METHODS
|
|
113
|
+
)
|
|
114
|
+
{$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
|
|
115
|
+
)
|
|
116
|
+
PATTERN
|
|
117
|
+
|
|
87
118
|
# @!method any_none_block_intersection(node)
|
|
88
119
|
def_node_matcher :any_none_block_intersection, <<~PATTERN
|
|
89
120
|
{
|
|
@@ -104,15 +135,15 @@ module RuboCop
|
|
|
104
135
|
PATTERN
|
|
105
136
|
|
|
106
137
|
MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
|
|
107
|
-
STRAIGHT_METHODS = %i[present? any?].freeze
|
|
108
|
-
NEGATED_METHODS = %i[blank? empty? none?].freeze
|
|
138
|
+
STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
|
|
139
|
+
NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
|
|
109
140
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
|
110
141
|
|
|
111
142
|
def on_send(node)
|
|
112
143
|
return if node.block_literal?
|
|
113
|
-
return unless (receiver, argument, method_name = bad_intersection?(node))
|
|
144
|
+
return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
|
|
114
145
|
|
|
115
|
-
dot =
|
|
146
|
+
dot = dot_node.loc.dot.source
|
|
116
147
|
bang = straight?(method_name) ? '' : '!'
|
|
117
148
|
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
|
118
149
|
|
|
@@ -135,13 +166,16 @@ module RuboCop
|
|
|
135
166
|
private
|
|
136
167
|
|
|
137
168
|
def bad_intersection?(node)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
PREDICATES
|
|
142
|
-
end
|
|
169
|
+
bad_intersection_check?(node, bad_intersection_predicates) ||
|
|
170
|
+
intersection_size_check?(node)
|
|
171
|
+
end
|
|
143
172
|
|
|
144
|
-
|
|
173
|
+
def bad_intersection_predicates
|
|
174
|
+
if active_support_extensions_enabled?
|
|
175
|
+
ACTIVE_SUPPORT_PREDICATES
|
|
176
|
+
else
|
|
177
|
+
PREDICATES
|
|
178
|
+
end
|
|
145
179
|
end
|
|
146
180
|
|
|
147
181
|
def straight?(method_name)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Use `include?(element)` instead of `intersect?([element])`.
|
|
7
|
+
#
|
|
8
|
+
# @safety
|
|
9
|
+
# The receiver might not be an array.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# array.intersect?([element])
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# array.include?(element)
|
|
17
|
+
class ArrayIntersectWithSingleElement < Base
|
|
18
|
+
extend AutoCorrector
|
|
19
|
+
|
|
20
|
+
MSG = 'Use `include?(element)` instead of `intersect?([element])`.'
|
|
21
|
+
|
|
22
|
+
RESTRICT_ON_SEND = %i[intersect?].freeze
|
|
23
|
+
|
|
24
|
+
# @!method single_element(node)
|
|
25
|
+
def_node_matcher :single_element, <<~PATTERN
|
|
26
|
+
(send _ _ $(array $_))
|
|
27
|
+
PATTERN
|
|
28
|
+
|
|
29
|
+
def on_send(node)
|
|
30
|
+
array, element = single_element(node)
|
|
31
|
+
return unless array
|
|
32
|
+
|
|
33
|
+
add_offense(
|
|
34
|
+
node.source_range.with(begin_pos: node.loc.selector.begin_pos)
|
|
35
|
+
) do |corrector|
|
|
36
|
+
corrector.replace(node.loc.selector, 'include?')
|
|
37
|
+
corrector.replace(
|
|
38
|
+
array,
|
|
39
|
+
array.percent_literal? ? element.value.inspect : element.source
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
alias on_csend on_send
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -70,18 +70,25 @@ module RuboCop
|
|
|
70
70
|
(send _ :& _))
|
|
71
71
|
PATTERN
|
|
72
72
|
|
|
73
|
+
# rubocop:disable Metrics/AbcSize
|
|
73
74
|
def on_send(node)
|
|
74
75
|
return unless node.receiver&.begin_type?
|
|
75
76
|
return unless (preferred_method = preferred_method(node))
|
|
76
77
|
|
|
77
78
|
bit_operation = node.receiver.children.first
|
|
78
79
|
lhs, _operator, rhs = *bit_operation
|
|
79
|
-
|
|
80
|
+
|
|
81
|
+
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
|
82
|
+
"#{rhs.source}.allbits?(#{lhs.source})"
|
|
83
|
+
else
|
|
84
|
+
"#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
|
85
|
+
end
|
|
80
86
|
|
|
81
87
|
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
|
82
88
|
corrector.replace(node, preferred)
|
|
83
89
|
end
|
|
84
90
|
end
|
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
|
85
92
|
|
|
86
93
|
private
|
|
87
94
|
|