rubocop 1.40.0 → 1.43.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +52 -2
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config.rb +34 -11
- data/lib/rubocop/config_loader.rb +9 -0
- data/lib/rubocop/config_loader_resolver.rb +5 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/badge.rb +9 -4
- data/lib/rubocop/cop/base.rb +83 -66
- data/lib/rubocop/cop/commissioner.rb +8 -3
- data/lib/rubocop/cop/cop.rb +29 -29
- data/lib/rubocop/cop/corrector.rb +23 -11
- data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
- data/lib/rubocop/cop/layout/class_structure.rb +32 -11
- data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
- data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
- data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
- data/lib/rubocop/cop/layout/line_length.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
- data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
- data/lib/rubocop/cop/lint/debugger.rb +3 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
- data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -19
- data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
- data/lib/rubocop/cop/lint/useless_rescue.rb +71 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
- data/lib/rubocop/cop/metrics/class_length.rb +1 -1
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -2
- data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
- data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/require_library.rb +2 -0
- data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
- data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
- data/lib/rubocop/cop/registry.rb +28 -25
- data/lib/rubocop/cop/security/compound_hash.rb +2 -1
- data/lib/rubocop/cop/style/alias.rb +9 -1
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
- data/lib/rubocop/cop/style/documentation.rb +11 -5
- data/lib/rubocop/cop/style/guard_clause.rb +17 -9
- data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -3
- data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
- data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
- data/lib/rubocop/cop/style/map_to_set.rb +61 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -9
- data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
- data/lib/rubocop/cop/style/missing_else.rb +13 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
- data/lib/rubocop/cop/style/redundant_constant_base.rb +13 -0
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
- data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
- data/lib/rubocop/cop/style/require_order.rb +63 -9
- data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
- data/lib/rubocop/cop/style/semicolon.rb +2 -1
- data/lib/rubocop/cop/style/signal_exception.rb +8 -6
- data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
- data/lib/rubocop/cop/style/word_array.rb +41 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
- data/lib/rubocop/cop/team.rb +29 -29
- data/lib/rubocop/cop/util.rb +31 -4
- data/lib/rubocop/cop/variable_force.rb +0 -3
- data/lib/rubocop/cops_documentation_generator.rb +33 -11
- data/lib/rubocop/directive_comment.rb +1 -1
- data/lib/rubocop/file_patterns.rb +43 -0
- data/lib/rubocop/formatter.rb +2 -0
- data/lib/rubocop/options.rb +1 -1
- data/lib/rubocop/path_util.rb +38 -22
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/runner.rb +10 -3
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +0 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +7 -1
- metadata +16 -10
- data/lib/rubocop/optimized_patterns.rb +0 -38
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Enforces the use of `max` or `min` instead of comparison for greater or less.
|
7
|
+
#
|
8
|
+
# NOTE: It can be used if you want to present limit or threshold in Ruby 2.7+.
|
9
|
+
# That it is slow though. So autocorrection will apply generic `max` or `min`:
|
10
|
+
#
|
11
|
+
# [source,ruby]
|
12
|
+
# ----
|
13
|
+
# a.clamp(b..) # Same as `[a, b].max`
|
14
|
+
# a.clamp(..b) # Same as `[a, b].min`
|
15
|
+
# ----
|
16
|
+
#
|
17
|
+
# @safety
|
18
|
+
# This cop is unsafe because even if a value has `<` or `>` method,
|
19
|
+
# it is not necessarily `Comparable`.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# a > b ? a : b
|
25
|
+
# a >= b ? a : b
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# [a, b].max
|
29
|
+
#
|
30
|
+
# # bad
|
31
|
+
# a < b ? a : b
|
32
|
+
# a <= b ? a : b
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# [a, b].min
|
36
|
+
#
|
37
|
+
class MinMaxComparison < Base
|
38
|
+
extend AutoCorrector
|
39
|
+
|
40
|
+
MSG = 'Use `%<prefer>s` instead.'
|
41
|
+
GRATER_OPERATORS = %i[> >=].freeze
|
42
|
+
LESS_OPERATORS = %i[< <=].freeze
|
43
|
+
COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
|
44
|
+
|
45
|
+
def on_if(node)
|
46
|
+
lhs, operator, rhs = *node.condition
|
47
|
+
return unless COMPARISON_OPERATORS.include?(operator)
|
48
|
+
|
49
|
+
if_branch = node.if_branch
|
50
|
+
else_branch = node.else_branch
|
51
|
+
preferred_method = preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
52
|
+
return unless preferred_method
|
53
|
+
|
54
|
+
replacement = "[#{lhs.source}, #{rhs.source}].#{preferred_method}"
|
55
|
+
|
56
|
+
add_offense(node, message: format(MSG, prefer: replacement)) do |corrector|
|
57
|
+
corrector.replace(node, replacement)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
64
|
+
if lhs == if_branch && rhs == else_branch
|
65
|
+
GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
|
66
|
+
elsif lhs == else_branch && rhs == if_branch
|
67
|
+
LESS_OPERATORS.include?(operator) ? 'max' : 'min'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -99,6 +99,7 @@ module RuboCop
|
|
99
99
|
class MissingElse < Base
|
100
100
|
include OnNormalIfUnless
|
101
101
|
include ConfigurableEnforcedStyle
|
102
|
+
extend AutoCorrector
|
102
103
|
|
103
104
|
MSG = '`%<type>s` condition requires an `else`-clause.'
|
104
105
|
MSG_NIL = '`%<type>s` condition requires an `else`-clause with `nil` in it.'
|
@@ -126,7 +127,9 @@ module RuboCop
|
|
126
127
|
def check(node)
|
127
128
|
return if node.else?
|
128
129
|
|
129
|
-
add_offense(node, message: format(message_template, type: node.type))
|
130
|
+
add_offense(node, message: format(message_template, type: node.type)) do |corrector|
|
131
|
+
autocorrect(corrector, node)
|
132
|
+
end
|
130
133
|
end
|
131
134
|
|
132
135
|
def message_template
|
@@ -140,6 +143,15 @@ module RuboCop
|
|
140
143
|
end
|
141
144
|
end
|
142
145
|
|
146
|
+
def autocorrect(corrector, node)
|
147
|
+
case empty_else_style
|
148
|
+
when :empty
|
149
|
+
corrector.insert_before(node.loc.end, 'else; nil; ')
|
150
|
+
when :nil
|
151
|
+
corrector.insert_before(node.loc.end, 'else; ')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
143
155
|
def if_style?
|
144
156
|
style == :if
|
145
157
|
end
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
return if node.receiver.const_type?
|
29
29
|
|
30
30
|
_lhs, _op, rhs = *node
|
31
|
-
return if rhs
|
31
|
+
return if !rhs || method_call_with_parenthesized_arg?(rhs) || anonymous_forwarding?(rhs)
|
32
32
|
|
33
33
|
add_offense(dot) do |corrector|
|
34
34
|
wrap_in_parentheses_if_chained(corrector, node)
|
@@ -38,6 +38,20 @@ module RuboCop
|
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
+
# Checks for an acceptable case of `foo.+(bar).baz`.
|
42
|
+
def method_call_with_parenthesized_arg?(argument)
|
43
|
+
return false unless argument.parent.parent&.send_type?
|
44
|
+
|
45
|
+
argument.children.first && argument.parent.parenthesized?
|
46
|
+
end
|
47
|
+
|
48
|
+
def anonymous_forwarding?(argument)
|
49
|
+
return true if argument.forwarded_args_type? || argument.forwarded_restarg_type?
|
50
|
+
return true if argument.children.first&.forwarded_kwrestarg_type?
|
51
|
+
|
52
|
+
argument.block_pass_type? && argument.source == '&'
|
53
|
+
end
|
54
|
+
|
41
55
|
def wrap_in_parentheses_if_chained(corrector, node)
|
42
56
|
return unless node.parent&.call_type?
|
43
57
|
|
@@ -10,6 +10,10 @@ module RuboCop
|
|
10
10
|
# is empty, there is no need to prepend `::`, so it would be nice to consistently
|
11
11
|
# avoid such meaningless `::` prefix to avoid confusion.
|
12
12
|
#
|
13
|
+
# NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled to prevent
|
14
|
+
# conflicting rules. Because it respects user configurations that want to enable
|
15
|
+
# `Lint/ConstantResolution` cop which is disabled by default.
|
16
|
+
#
|
13
17
|
# @example
|
14
18
|
# # bad
|
15
19
|
# ::Const
|
@@ -42,6 +46,7 @@ module RuboCop
|
|
42
46
|
MSG = 'Remove redundant `::`.'
|
43
47
|
|
44
48
|
def on_cbase(node)
|
49
|
+
return if lint_constant_resolution_cop_enabled?
|
45
50
|
return unless bad?(node)
|
46
51
|
|
47
52
|
add_offense(node) do |corrector|
|
@@ -51,6 +56,14 @@ module RuboCop
|
|
51
56
|
|
52
57
|
private
|
53
58
|
|
59
|
+
def lint_constant_resolution_cop_enabled?
|
60
|
+
lint_constant_resolution_config.fetch('Enabled', false)
|
61
|
+
end
|
62
|
+
|
63
|
+
def lint_constant_resolution_config
|
64
|
+
config.for_cop('Lint/ConstantResolution')
|
65
|
+
end
|
66
|
+
|
54
67
|
def bad?(node)
|
55
68
|
module_nesting_ancestors_of(node).none?
|
56
69
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for redundant uses of double splat hash braces.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# do_something(**{foo: bar, baz: qux})
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# do_something(foo: bar, baz: qux)
|
15
|
+
#
|
16
|
+
class RedundantDoubleSplatHashBraces < Base
|
17
|
+
extend AutoCorrector
|
18
|
+
|
19
|
+
MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
|
20
|
+
|
21
|
+
# @!method double_splat_hash_braces?(node)
|
22
|
+
def_node_matcher :double_splat_hash_braces?, <<~PATTERN
|
23
|
+
(hash (kwsplat (hash ...)))
|
24
|
+
PATTERN
|
25
|
+
|
26
|
+
def on_hash(node)
|
27
|
+
return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
|
28
|
+
|
29
|
+
grandparent = node.parent&.parent
|
30
|
+
return unless double_splat_hash_braces?(grandparent)
|
31
|
+
|
32
|
+
add_offense(grandparent) do |corrector|
|
33
|
+
corrector.replace(grandparent, node.children.map(&:source).join(', '))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -77,7 +77,8 @@ module RuboCop
|
|
77
77
|
# but it's not necessry to escape hyphen if it's the first or last character
|
78
78
|
# within the character class. This method checks if that's the case.
|
79
79
|
# e.g. "[0-9\\-]" or "[\\-0-9]" would return true
|
80
|
-
node.source[index] == '[' ||
|
80
|
+
contents_range(node).source[index - 1] == '[' ||
|
81
|
+
contents_range(node).source[index + 2] == ']'
|
81
82
|
end
|
82
83
|
|
83
84
|
def delimiter?(node, char)
|
@@ -58,7 +58,7 @@ module RuboCop
|
|
58
58
|
private
|
59
59
|
|
60
60
|
def message(range)
|
61
|
-
format(MSG, char: range.source
|
61
|
+
format(MSG, char: range.source[-1])
|
62
62
|
end
|
63
63
|
|
64
64
|
def str_contents_range(node)
|
@@ -76,6 +76,7 @@ module RuboCop
|
|
76
76
|
node.loc.to_hash.key?(:begin) && !node.loc.begin.nil?
|
77
77
|
end
|
78
78
|
|
79
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
79
80
|
def allowed_escape?(node, range)
|
80
81
|
escaped = range.source[(1..-1)]
|
81
82
|
|
@@ -88,13 +89,14 @@ module RuboCop
|
|
88
89
|
# with different versions of Ruby so that e.g. /\d/ != /d/
|
89
90
|
return true if /[\n\\[[:alnum:]]]/.match?(escaped[0])
|
90
91
|
|
91
|
-
return true if escaped[0] == ' ' && percent_array_literal?(node)
|
92
|
+
return true if escaped[0] == ' ' && (percent_array_literal?(node) || node.heredoc?)
|
92
93
|
|
93
94
|
return true if disabling_interpolation?(range)
|
94
95
|
return true if delimiter?(node, escaped[0])
|
95
96
|
|
96
97
|
false
|
97
98
|
end
|
99
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
98
100
|
|
99
101
|
def interpolation_not_enabled?(node)
|
100
102
|
single_quoted?(node) ||
|
@@ -169,9 +171,10 @@ module RuboCop
|
|
169
171
|
# Allow \#{foo}, \#$foo, \#@foo, and \#@@foo
|
170
172
|
# for escaping local, global, instance and class variable interpolations
|
171
173
|
return true if range.source.match?(/\A\\#[{$@]/)
|
172
|
-
|
173
174
|
# Also allow #\{foo}, #\$foo, #\@foo and #\@@foo
|
174
175
|
return true if range.adjust(begin_pos: -2).source.match?(/\A[^\\]#\\[{$@]/)
|
176
|
+
# For `\#\{foo} allow `\#` and warn `\{`
|
177
|
+
return true if range.adjust(end_pos: 1).source == '\\#\\{'
|
175
178
|
|
176
179
|
false
|
177
180
|
end
|
@@ -36,6 +36,33 @@ module RuboCop
|
|
36
36
|
# require 'b'
|
37
37
|
# require_relative 'c'
|
38
38
|
# require 'a'
|
39
|
+
#
|
40
|
+
# # bad
|
41
|
+
# require 'a'
|
42
|
+
# require 'c' if foo
|
43
|
+
# require 'b'
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# require 'a'
|
47
|
+
# require 'b'
|
48
|
+
# require 'c' if foo
|
49
|
+
#
|
50
|
+
# # bad
|
51
|
+
# require 'c'
|
52
|
+
# if foo
|
53
|
+
# require 'd'
|
54
|
+
# require 'b'
|
55
|
+
# end
|
56
|
+
# require 'a'
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# require 'c'
|
60
|
+
# if foo
|
61
|
+
# require 'b'
|
62
|
+
# require 'd'
|
63
|
+
# end
|
64
|
+
# require 'a'
|
65
|
+
#
|
39
66
|
class RequireOrder < Base
|
40
67
|
extend AutoCorrector
|
41
68
|
|
@@ -43,17 +70,27 @@ module RuboCop
|
|
43
70
|
|
44
71
|
RESTRICT_ON_SEND = %i[require require_relative].freeze
|
45
72
|
|
73
|
+
MSG = 'Sort `%<name>s` in alphabetical order.'
|
74
|
+
|
75
|
+
# @!method if_inside_only_require(node)
|
76
|
+
def_node_matcher :if_inside_only_require, <<~PATTERN
|
77
|
+
{
|
78
|
+
(if _ _ $(send nil? {:require :require_relative} _))
|
79
|
+
(if _ $(send nil? {:require :require_relative} _) _)
|
80
|
+
}
|
81
|
+
PATTERN
|
82
|
+
|
46
83
|
def on_send(node)
|
84
|
+
return unless node.parent && node.arguments?
|
85
|
+
return if not_modifier_form?(node.parent)
|
86
|
+
|
47
87
|
previous_older_sibling = find_previous_older_sibling(node)
|
48
88
|
return unless previous_older_sibling
|
49
89
|
|
50
|
-
add_offense(
|
51
|
-
node,
|
52
|
-
message: "Sort `#{node.method_name}` in alphabetical order."
|
53
|
-
) do |corrector|
|
90
|
+
add_offense(node, message: format(MSG, name: node.method_name)) do |corrector|
|
54
91
|
swap(
|
55
92
|
range_with_comments_and_lines(previous_older_sibling),
|
56
|
-
range_with_comments_and_lines(node),
|
93
|
+
range_with_comments_and_lines(node.parent.if_type? ? node.parent : node),
|
57
94
|
corrector: corrector
|
58
95
|
)
|
59
96
|
end
|
@@ -61,16 +98,33 @@ module RuboCop
|
|
61
98
|
|
62
99
|
private
|
63
100
|
|
64
|
-
def
|
65
|
-
node.
|
66
|
-
|
67
|
-
|
101
|
+
def not_modifier_form?(node)
|
102
|
+
node.if_type? && !node.modifier_form?
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_previous_older_sibling(node) # rubocop:disable Metrics
|
106
|
+
search_node(node).left_siblings.reverse.find do |sibling|
|
107
|
+
next unless sibling.is_a?(AST::Node)
|
108
|
+
|
109
|
+
sibling = sibling_node(sibling)
|
110
|
+
break unless sibling&.send_type? && sibling&.method?(node.method_name)
|
111
|
+
break unless sibling.arguments? && !sibling.receiver
|
68
112
|
break unless in_same_section?(sibling, node)
|
69
113
|
|
70
114
|
node.first_argument.source < sibling.first_argument.source
|
71
115
|
end
|
72
116
|
end
|
73
117
|
|
118
|
+
def search_node(node)
|
119
|
+
node.parent.if_type? ? node.parent : node
|
120
|
+
end
|
121
|
+
|
122
|
+
def sibling_node(node)
|
123
|
+
return if not_modifier_form?(node)
|
124
|
+
|
125
|
+
node.if_type? ? if_inside_only_require(node) : node
|
126
|
+
end
|
127
|
+
|
74
128
|
def in_same_section?(node1, node2)
|
75
129
|
!node1.location.expression.with(
|
76
130
|
end_pos: node2.location.expression.end_pos
|
@@ -86,12 +86,12 @@ module RuboCop
|
|
86
86
|
|
87
87
|
def on_send(node)
|
88
88
|
return unless (block_node = node.block_node)
|
89
|
-
return if block_node.body
|
89
|
+
return if block_node.body&.begin_type?
|
90
90
|
return if receiver_allowed?(block_node.receiver)
|
91
91
|
return unless (regexp_method_send_node = extract_send_node(block_node))
|
92
92
|
return if match_predicate_without_receiver?(regexp_method_send_node)
|
93
93
|
|
94
|
-
opposite =
|
94
|
+
opposite = opposite?(regexp_method_send_node)
|
95
95
|
regexp = find_regexp(regexp_method_send_node, block_node)
|
96
96
|
|
97
97
|
register_offense(node, block_node, regexp, opposite)
|
@@ -128,6 +128,10 @@ module RuboCop
|
|
128
128
|
regexp_method_send_node
|
129
129
|
end
|
130
130
|
|
131
|
+
def opposite?(regexp_method_send_node)
|
132
|
+
regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
|
133
|
+
end
|
134
|
+
|
131
135
|
def find_regexp(node, block)
|
132
136
|
return node.child_nodes.first if node.match_with_lvasgn_type?
|
133
137
|
|
@@ -37,13 +37,14 @@ module RuboCop
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def on_new_investigation
|
40
|
-
return if processed_source.blank?
|
40
|
+
return if processed_source.blank? || !processed_source.raw_source.include?(';')
|
41
41
|
|
42
42
|
check_for_line_terminator_or_opener
|
43
43
|
end
|
44
44
|
|
45
45
|
def on_begin(node)
|
46
46
|
return if cop_config['AllowAsExpressionSeparator']
|
47
|
+
return unless node.source.include?(';')
|
47
48
|
|
48
49
|
exprs = node.children
|
49
50
|
|
@@ -119,11 +119,6 @@ module RuboCop
|
|
119
119
|
# @!method custom_fail_methods(node)
|
120
120
|
def_node_search :custom_fail_methods, '{(def :fail ...) (defs _ :fail ...)}'
|
121
121
|
|
122
|
-
def on_new_investigation
|
123
|
-
ast = processed_source.ast
|
124
|
-
@custom_fail_defined = ast && custom_fail_methods(ast).any?
|
125
|
-
end
|
126
|
-
|
127
122
|
def on_rescue(node)
|
128
123
|
return unless style == :semantic
|
129
124
|
|
@@ -141,7 +136,7 @@ module RuboCop
|
|
141
136
|
when :semantic
|
142
137
|
check_send(:raise, node) unless ignored_node?(node)
|
143
138
|
when :only_raise
|
144
|
-
return if
|
139
|
+
return if custom_fail_defined?
|
145
140
|
|
146
141
|
check_send(:fail, node)
|
147
142
|
when :only_fail
|
@@ -151,6 +146,13 @@ module RuboCop
|
|
151
146
|
|
152
147
|
private
|
153
148
|
|
149
|
+
def custom_fail_defined?
|
150
|
+
return @custom_fail_defined if defined?(@custom_fail_defined)
|
151
|
+
|
152
|
+
ast = processed_source.ast
|
153
|
+
@custom_fail_defined = ast && custom_fail_methods(ast).any?
|
154
|
+
end
|
155
|
+
|
154
156
|
def message(method_name)
|
155
157
|
case style
|
156
158
|
when :semantic
|
@@ -41,10 +41,13 @@ module RuboCop
|
|
41
41
|
|
42
42
|
def on_pair(node)
|
43
43
|
return unless string_hash_key?(node)
|
44
|
+
|
45
|
+
key_content = node.key.str_content
|
46
|
+
return unless key_content.valid_encoding?
|
44
47
|
return if receive_environments_method?(node)
|
45
48
|
|
46
49
|
add_offense(node.key) do |corrector|
|
47
|
-
symbol_content =
|
50
|
+
symbol_content = key_content.to_sym.inspect
|
48
51
|
|
49
52
|
corrector.replace(node.key, symbol_content)
|
50
53
|
end
|
@@ -88,6 +88,10 @@ module RuboCop
|
|
88
88
|
include TrailingComma
|
89
89
|
extend AutoCorrector
|
90
90
|
|
91
|
+
def self.autocorrect_incompatible_with
|
92
|
+
[Layout::HeredocArgumentClosingParenthesis]
|
93
|
+
end
|
94
|
+
|
91
95
|
def on_send(node)
|
92
96
|
return unless node.arguments? && node.parenthesized?
|
93
97
|
|
@@ -96,10 +100,6 @@ module RuboCop
|
|
96
100
|
node.source_range.end_pos)
|
97
101
|
end
|
98
102
|
alias on_csend on_send
|
99
|
-
|
100
|
-
def self.autocorrect_incompatible_with
|
101
|
-
[Layout::HeredocArgumentClosingParenthesis]
|
102
|
-
end
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -27,6 +27,25 @@ module RuboCop
|
|
27
27
|
# # bad (contains spaces)
|
28
28
|
# %w[foo\ bar baz\ quux]
|
29
29
|
#
|
30
|
+
# # bad
|
31
|
+
# [
|
32
|
+
# ['one', 'One'],
|
33
|
+
# ['two', 'Two']
|
34
|
+
# ]
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# [
|
38
|
+
# %w[one One],
|
39
|
+
# %w[two Two]
|
40
|
+
# ]
|
41
|
+
#
|
42
|
+
# # good (2d array containing spaces)
|
43
|
+
# [
|
44
|
+
# ['one', 'One'],
|
45
|
+
# ['two', 'Two'],
|
46
|
+
# ['forty two', 'Forty Two']
|
47
|
+
# ]
|
48
|
+
#
|
30
49
|
# @example EnforcedStyle: brackets
|
31
50
|
# # good
|
32
51
|
# ['foo', 'bar', 'baz']
|
@@ -36,6 +55,19 @@ module RuboCop
|
|
36
55
|
#
|
37
56
|
# # good (contains spaces)
|
38
57
|
# ['foo bar', 'baz quux']
|
58
|
+
#
|
59
|
+
# # good
|
60
|
+
# [
|
61
|
+
# ['one', 'One'],
|
62
|
+
# ['two', 'Two']
|
63
|
+
# ]
|
64
|
+
#
|
65
|
+
# # bad
|
66
|
+
# [
|
67
|
+
# %w[one One],
|
68
|
+
# %w[two Two]
|
69
|
+
# ]
|
70
|
+
#
|
39
71
|
class WordArray < Base
|
40
72
|
include ArrayMinSize
|
41
73
|
include ArraySyntax
|
@@ -53,6 +85,7 @@ module RuboCop
|
|
53
85
|
def on_array(node)
|
54
86
|
if bracketed_array_of?(:str, node)
|
55
87
|
return if complex_content?(node.values)
|
88
|
+
return if within_2d_array_of_complex_content?(node)
|
56
89
|
|
57
90
|
check_bracketed_array(node, 'w')
|
58
91
|
elsif node.percent_literal?(:string)
|
@@ -62,6 +95,14 @@ module RuboCop
|
|
62
95
|
|
63
96
|
private
|
64
97
|
|
98
|
+
def within_2d_array_of_complex_content?(node)
|
99
|
+
return false unless (parent = node.parent)
|
100
|
+
|
101
|
+
parent.array_type? &&
|
102
|
+
parent.values.all?(&:array_type?) &&
|
103
|
+
parent.values.any? { |subarray| complex_content?(subarray.values) }
|
104
|
+
end
|
105
|
+
|
65
106
|
def complex_content?(strings, complex_regex: word_regex)
|
66
107
|
strings.any? do |s|
|
67
108
|
next unless s.str_content
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Forbids Yoda expressions, i.e. binary operations (using `*`, `+`, `&`, `|`,
|
7
|
+
# and `^` operators) where the order of expression is reversed, eg. `1 + x`.
|
8
|
+
# This cop complements `Style/YodaCondition` cop, which has a similar purpose.
|
9
|
+
#
|
10
|
+
# This cop is disabled by default to respect user intentions such as:
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# config.server_port = 9000 + ENV["TEST_ENV_NUMBER"].to_i
|
15
|
+
# ----
|
16
|
+
#
|
17
|
+
# @safety
|
18
|
+
# This cop is unsafe because binary operators can be defined
|
19
|
+
# differently on different classes, and are not guaranteed to
|
20
|
+
# have the same result if reversed.
|
21
|
+
#
|
22
|
+
# @example SupportedOperators: ['*', '+', '&'']
|
23
|
+
# # bad
|
24
|
+
# 1 + x
|
25
|
+
# 10 * y
|
26
|
+
# 1 & z
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# 60 * 24
|
30
|
+
# x + 1
|
31
|
+
# y * 10
|
32
|
+
# z & 1
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# 1 | x
|
36
|
+
#
|
37
|
+
class YodaExpression < Base
|
38
|
+
extend AutoCorrector
|
39
|
+
|
40
|
+
MSG = 'Non-literal operand (`%<source>s`) should be first.'
|
41
|
+
|
42
|
+
RESTRICT_ON_SEND = %i[* + & | ^].freeze
|
43
|
+
|
44
|
+
def on_new_investigation
|
45
|
+
@offended_nodes = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_send(node)
|
49
|
+
return unless supported_operators.include?(node.method_name.to_s)
|
50
|
+
|
51
|
+
lhs = node.receiver
|
52
|
+
rhs = node.first_argument
|
53
|
+
return if !lhs.numeric_type? || rhs.numeric_type?
|
54
|
+
|
55
|
+
return if offended_ancestor?(node)
|
56
|
+
|
57
|
+
message = format(MSG, source: rhs.source)
|
58
|
+
add_offense(node, message: message) do |corrector|
|
59
|
+
corrector.swap(lhs, rhs)
|
60
|
+
end
|
61
|
+
|
62
|
+
offended_nodes.add(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def supported_operators
|
68
|
+
Array(cop_config['SupportedOperators'])
|
69
|
+
end
|
70
|
+
|
71
|
+
def offended_ancestor?(node)
|
72
|
+
node.each_ancestor(:send).any? { |ancestor| @offended_nodes&.include?(ancestor) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def offended_nodes
|
76
|
+
@offended_nodes ||= Set.new.compare_by_identity
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|