rubocop 1.39.0 → 1.44.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +149 -11
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +5 -0
  8. data/lib/rubocop/config.rb +39 -15
  9. data/lib/rubocop/config_loader.rb +26 -20
  10. data/lib/rubocop/config_loader_resolver.rb +6 -2
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/badge.rb +9 -4
  13. data/lib/rubocop/cop/base.rb +84 -74
  14. data/lib/rubocop/cop/commissioner.rb +8 -3
  15. data/lib/rubocop/cop/cop.rb +29 -29
  16. data/lib/rubocop/cop/corrector.rb +30 -10
  17. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  18. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
  19. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  20. data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
  21. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  22. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  23. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
  24. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  25. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
  26. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
  28. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  29. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  31. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  32. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  33. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  34. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  35. data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
  36. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  37. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  38. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  39. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  40. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  41. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  42. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  43. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  44. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  45. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  46. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  47. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +0 -2
  48. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  49. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  50. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  51. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
  52. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  54. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  55. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
  56. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  57. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  58. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  59. data/lib/rubocop/cop/lint/else_layout.rb +2 -6
  60. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  61. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  62. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -7
  63. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
  64. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  65. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  66. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  67. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  68. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  69. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +15 -3
  70. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  71. data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
  72. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  73. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  74. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
  75. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  76. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  77. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  78. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
  79. data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
  80. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +14 -4
  81. data/lib/rubocop/cop/lint/void.rb +25 -16
  82. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  83. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  84. data/lib/rubocop/cop/metrics/class_length.rb +10 -5
  85. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  86. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  87. data/lib/rubocop/cop/metrics/module_length.rb +10 -5
  88. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  89. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  90. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +3 -6
  91. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
  92. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  93. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  94. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  95. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  96. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  97. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +59 -6
  98. data/lib/rubocop/cop/mixin/line_length_help.rb +11 -2
  99. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  100. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  101. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  102. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  103. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  104. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  105. data/lib/rubocop/cop/mixin/statement_modifier.rb +16 -1
  106. data/lib/rubocop/cop/naming/block_forwarding.rb +5 -1
  107. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  108. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  109. data/lib/rubocop/cop/registry.rb +63 -43
  110. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  111. data/lib/rubocop/cop/style/access_modifier_declarations.rb +18 -10
  112. data/lib/rubocop/cop/style/alias.rb +9 -1
  113. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  114. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  115. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  116. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -9
  117. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  118. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  119. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  120. data/lib/rubocop/cop/style/documentation.rb +11 -5
  121. data/lib/rubocop/cop/style/guard_clause.rb +44 -9
  122. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  123. data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
  124. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  125. data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
  126. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  127. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  128. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  129. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  130. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  131. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
  132. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  133. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  134. data/lib/rubocop/cop/style/min_max_comparison.rb +83 -0
  135. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  136. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  137. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  138. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  139. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  140. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  141. data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
  142. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  143. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  144. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  145. data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
  146. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +45 -0
  147. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  148. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  149. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  150. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  151. data/lib/rubocop/cop/style/require_order.rb +135 -0
  152. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  153. data/lib/rubocop/cop/style/select_by_regexp.rb +13 -5
  154. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  155. data/lib/rubocop/cop/style/semicolon.rb +26 -3
  156. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  157. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  158. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  159. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  160. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  161. data/lib/rubocop/cop/style/word_array.rb +41 -0
  162. data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
  163. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  164. data/lib/rubocop/cop/team.rb +30 -30
  165. data/lib/rubocop/cop/util.rb +32 -5
  166. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  167. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  168. data/lib/rubocop/cop/variable_force.rb +18 -30
  169. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  170. data/lib/rubocop/directive_comment.rb +1 -1
  171. data/lib/rubocop/file_patterns.rb +43 -0
  172. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  173. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  174. data/lib/rubocop/formatter.rb +4 -1
  175. data/lib/rubocop/options.rb +8 -0
  176. data/lib/rubocop/path_util.rb +50 -22
  177. data/lib/rubocop/result_cache.rb +2 -2
  178. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  179. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  180. data/lib/rubocop/rspec/support.rb +2 -2
  181. data/lib/rubocop/runner.rb +10 -3
  182. data/lib/rubocop/server/cache.rb +3 -1
  183. data/lib/rubocop/server/core.rb +1 -1
  184. data/lib/rubocop/target_finder.rb +1 -1
  185. data/lib/rubocop/target_ruby.rb +1 -2
  186. data/lib/rubocop/version.rb +1 -1
  187. data/lib/rubocop.rb +23 -6
  188. metadata +23 -9
@@ -50,6 +50,11 @@ module RuboCop
50
50
  # foo && foo.bar { |e| e.something }
51
51
  # foo && foo.bar(param) { |e| e.something }
52
52
  #
53
+ # foo ? foo.bar : nil
54
+ # foo.nil? ? nil : foo.bar
55
+ # !foo.nil? ? foo.bar : nil
56
+ # !foo ? nil : foo.bar
57
+ #
53
58
  # # good
54
59
  # foo&.bar
55
60
  # foo&.bar&.baz
@@ -105,6 +110,17 @@ module RuboCop
105
110
  }
106
111
  PATTERN
107
112
 
113
+ # @!method ternary_safe_navigation_candidate(node)
114
+ def_node_matcher :ternary_safe_navigation_candidate, <<~PATTERN
115
+ {
116
+ (if (send $_ {:nil? :!}) nil $_)
117
+
118
+ (if (send (send $_ :nil?) :!) $_ nil)
119
+
120
+ (if $_ $_ nil)
121
+ }
122
+ PATTERN
123
+
108
124
  # @!method not_nil_check?(node)
109
125
  def_node_matcher :not_nil_check?, '(send (send $_ :nil?) :!)'
110
126
 
@@ -118,9 +134,11 @@ module RuboCop
118
134
  check_node(node)
119
135
  end
120
136
 
137
+ private
138
+
121
139
  def check_node(node)
122
140
  checked_variable, receiver, method_chain, method = extract_parts(node)
123
- return unless receiver == checked_variable
141
+ return if receiver != checked_variable || receiver.nil?
124
142
  return if use_var_only_in_unless_modifier?(node, checked_variable)
125
143
  return if chain_length(method_chain, method) > max_chain_length
126
144
  return if unsafe_method_used?(method_chain, method)
@@ -133,10 +151,8 @@ module RuboCop
133
151
  node.if_type? && node.unless? && !method_called?(variable)
134
152
  end
135
153
 
136
- private
137
-
138
154
  def autocorrect(corrector, node)
139
- body = node.node_parts[1]
155
+ body = extract_body(node)
140
156
  method_call = method_call(node)
141
157
 
142
158
  corrector.remove(begin_range(node, body))
@@ -147,6 +163,14 @@ module RuboCop
147
163
  add_safe_nav_to_all_methods_in_chain(corrector, method_call, body)
148
164
  end
149
165
 
166
+ def extract_body(node)
167
+ if node.if_type? && node.ternary?
168
+ node.branches.find { |branch| !branch.nil_type? }
169
+ else
170
+ node.node_parts[1]
171
+ end
172
+ end
173
+
150
174
  def handle_comments(corrector, node, method_call)
151
175
  comments = comments(node)
152
176
  return if comments.empty?
@@ -174,7 +198,7 @@ module RuboCop
174
198
  end
175
199
 
176
200
  def allowed_if_condition?(node)
177
- node.else? || node.elsif? || node.ternary?
201
+ node.else? || node.elsif?
178
202
  end
179
203
 
180
204
  def method_call(node)
@@ -192,7 +216,12 @@ module RuboCop
192
216
  end
193
217
 
194
218
  def extract_parts_from_if(node)
195
- variable, receiver = modifier_if_safe_navigation_candidate(node)
219
+ variable, receiver =
220
+ if node.ternary?
221
+ ternary_safe_navigation_candidate(node)
222
+ else
223
+ modifier_if_safe_navigation_candidate(node)
224
+ end
196
225
 
197
226
  checked_variable, matching_receiver, method = extract_common_parts(receiver, variable)
198
227
 
@@ -49,7 +49,8 @@ module RuboCop
49
49
  MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
50
50
  RESTRICT_ON_SEND = %i[select find_all reject].freeze
51
51
  REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
52
- REGEXP_METHODS = %i[match? =~].to_set.freeze
52
+ OPPOSITE_REPLACEMENTS = { select: 'grep_v', find_all: 'grep_v', reject: 'grep' }.freeze
53
+ REGEXP_METHODS = %i[match? =~ !~].to_set.freeze
53
54
 
54
55
  # @!method regexp_match?(node)
55
56
  def_node_matcher :regexp_match?, <<~PATTERN
@@ -85,13 +86,15 @@ module RuboCop
85
86
 
86
87
  def on_send(node)
87
88
  return unless (block_node = node.block_node)
88
- return if block_node.body.begin_type?
89
+ return if block_node.body&.begin_type?
89
90
  return if receiver_allowed?(block_node.receiver)
90
91
  return unless (regexp_method_send_node = extract_send_node(block_node))
91
92
  return if match_predicate_without_receiver?(regexp_method_send_node)
92
93
 
94
+ opposite = opposite?(regexp_method_send_node)
93
95
  regexp = find_regexp(regexp_method_send_node, block_node)
94
- register_offense(node, block_node, regexp)
96
+
97
+ register_offense(node, block_node, regexp, opposite)
95
98
  end
96
99
 
97
100
  private
@@ -102,8 +105,9 @@ module RuboCop
102
105
  node.hash_type? || creates_hash?(node) || env_const?(node)
103
106
  end
104
107
 
105
- def register_offense(node, block_node, regexp)
106
- replacement = REPLACEMENTS[node.method_name.to_sym]
108
+ def register_offense(node, block_node, regexp, opposite)
109
+ method_name = node.method_name.to_sym
110
+ replacement = opposite ? OPPOSITE_REPLACEMENTS[method_name] : REPLACEMENTS[method_name]
107
111
  message = format(MSG, replacement: replacement, original_method: node.method_name)
108
112
 
109
113
  add_offense(block_node, message: message) do |corrector|
@@ -124,6 +128,10 @@ module RuboCop
124
128
  regexp_method_send_node
125
129
  end
126
130
 
131
+ def opposite?(regexp_method_send_node)
132
+ regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
133
+ end
134
+
127
135
  def find_regexp(node, block)
128
136
  return node.child_nodes.first if node.match_with_lvasgn_type?
129
137
 
@@ -42,7 +42,7 @@ module RuboCop
42
42
 
43
43
  if rhs.send_type?
44
44
  check_send_node(node, rhs, var_name, var_type)
45
- elsif %i[and or].include?(rhs.type)
45
+ elsif rhs.operator_keyword?
46
46
  check_boolean_node(node, rhs, var_name, var_type)
47
47
  end
48
48
  end
@@ -76,7 +76,7 @@ module RuboCop
76
76
 
77
77
  if rhs.send_type?
78
78
  autocorrect_send_node(corrector, node, rhs)
79
- elsif %i[and or].include?(rhs.type)
79
+ elsif rhs.operator_keyword?
80
80
  autocorrect_boolean_node(corrector, node, rhs)
81
81
  end
82
82
  end
@@ -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
 
@@ -71,8 +72,10 @@ module RuboCop
71
72
 
72
73
  def each_semicolon
73
74
  tokens_for_lines.each do |line, tokens|
74
- yield line, tokens.last.column, tokens[-2] if tokens.last.semicolon?
75
- yield line, tokens.first.column if tokens.first.semicolon?
75
+ semicolon_pos = semicolon_position(tokens)
76
+ after_expr_pos = semicolon_pos == -1 ? -2 : semicolon_pos
77
+
78
+ yield line, tokens[semicolon_pos].column, tokens[after_expr_pos] if semicolon_pos
76
79
  end
77
80
  end
78
81
 
@@ -80,6 +83,26 @@ module RuboCop
80
83
  processed_source.tokens.group_by(&:line)
81
84
  end
82
85
 
86
+ def semicolon_position(tokens)
87
+ if tokens.last.semicolon?
88
+ -1
89
+ elsif tokens.first.semicolon?
90
+ 0
91
+ elsif exist_semicolon_before_right_curly_brace?(tokens)
92
+ -3
93
+ elsif exist_semicolon_after_left_curly_brace?(tokens)
94
+ 2
95
+ end
96
+ end
97
+
98
+ def exist_semicolon_before_right_curly_brace?(tokens)
99
+ tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
100
+ end
101
+
102
+ def exist_semicolon_after_left_curly_brace?(tokens)
103
+ tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
+ end
105
+
83
106
  def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
84
107
  range = source_range(processed_source.buffer, line, column)
85
108
 
@@ -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 @custom_fail_defined
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 = node.key.str_content.to_sym.inspect
50
+ symbol_content = key_content.to_sym.inspect
48
51
 
49
52
  corrector.replace(node.key, symbol_content)
50
53
  end
@@ -95,11 +95,7 @@ module RuboCop
95
95
  end
96
96
 
97
97
  def offense?(node)
98
- # If it's a string within an interpolation, then it's not an offense
99
- # for this cop.
100
- return false if inside_interpolation?(node)
101
-
102
- wrong_quotes?(node)
98
+ wrong_quotes?(node) && !inside_interpolation?(node)
103
99
  end
104
100
 
105
101
  def consistent_multiline?
@@ -7,7 +7,7 @@ module RuboCop
7
7
  #
8
8
  # If you prefer a style that allows block for method with arguments,
9
9
  # please set `true` to `AllowMethodsWithArguments`.
10
- # respond_to , and `define_method?` methods are allowed by default.
10
+ # `define_method?` methods are allowed by default.
11
11
  # These are customizable with `AllowedMethods` option.
12
12
  #
13
13
  # @safety
@@ -72,12 +72,10 @@ module RuboCop
72
72
  # # some comment
73
73
  # end
74
74
  #
75
- # @example AllowedMethods: [respond_to, define_method] (default)
75
+ # @example AllowedMethods: [define_method] (default)
76
76
  # # good
77
- # respond_to { |foo| foo.bar }
78
77
  # define_method(:foo) { |foo| foo.bar }
79
78
  #
80
- #
81
79
  # @example AllowedPatterns: [] (default)
82
80
  # # bad
83
81
  # something.map { |s| s.upcase }
@@ -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
@@ -34,43 +34,55 @@ module RuboCop
34
34
  class ZeroLengthPredicate < Base
35
35
  extend AutoCorrector
36
36
 
37
- ZERO_MSG = 'Use `empty?` instead of `%<lhs>s %<opr>s %<rhs>s`.'
38
- NONZERO_MSG = 'Use `!empty?` instead of `%<lhs>s %<opr>s %<rhs>s`.'
37
+ ZERO_MSG = 'Use `empty?` instead of `%<current>s`.'
38
+ NONZERO_MSG = 'Use `!empty?` instead of `%<current>s`.'
39
39
 
40
40
  RESTRICT_ON_SEND = %i[size length].freeze
41
41
 
42
42
  def on_send(node)
43
43
  check_zero_length_predicate(node)
44
- check_nonzero_length_predicate(node)
44
+ check_zero_length_comparison(node)
45
+ check_nonzero_length_comparison(node)
45
46
  end
46
47
 
47
48
  private
48
49
 
49
50
  def check_zero_length_predicate(node)
50
- zero_length_predicate = zero_length_predicate(node.parent)
51
- return unless zero_length_predicate
51
+ return unless (length_method = zero_length_predicate(node.parent))
52
52
 
53
- lhs, opr, rhs = zero_length_predicate
53
+ offense = node.loc.selector.join(node.parent.source_range.end)
54
+ message = format(ZERO_MSG, current: "#{length_method}.zero?")
55
+
56
+ add_offense(offense, message: message) do |corrector|
57
+ corrector.replace(offense, 'empty?')
58
+ end
59
+ end
60
+
61
+ def check_zero_length_comparison(node)
62
+ zero_length_comparison = zero_length_comparison(node.parent)
63
+ return unless zero_length_comparison
64
+
65
+ lhs, opr, rhs = zero_length_comparison
54
66
 
55
67
  return if non_polymorphic_collection?(node.parent)
56
68
 
57
69
  add_offense(
58
- node.parent, message: format(ZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
70
+ node.parent, message: format(ZERO_MSG, current: "#{lhs} #{opr} #{rhs}")
59
71
  ) do |corrector|
60
72
  corrector.replace(node.parent, replacement(node.parent))
61
73
  end
62
74
  end
63
75
 
64
- def check_nonzero_length_predicate(node)
65
- nonzero_length_predicate = nonzero_length_predicate(node.parent)
66
- return unless nonzero_length_predicate
76
+ def check_nonzero_length_comparison(node)
77
+ nonzero_length_comparison = nonzero_length_comparison(node.parent)
78
+ return unless nonzero_length_comparison
67
79
 
68
- lhs, opr, rhs = nonzero_length_predicate
80
+ lhs, opr, rhs = nonzero_length_comparison
69
81
 
70
82
  return if non_polymorphic_collection?(node.parent)
71
83
 
72
84
  add_offense(
73
- node.parent, message: format(NONZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
85
+ node.parent, message: format(NONZERO_MSG, current: "#{lhs} #{opr} #{rhs}")
74
86
  ) do |corrector|
75
87
  corrector.replace(node.parent, replacement(node.parent))
76
88
  end
@@ -78,14 +90,19 @@ module RuboCop
78
90
 
79
91
  # @!method zero_length_predicate(node)
80
92
  def_node_matcher :zero_length_predicate, <<~PATTERN
93
+ (send (send (...) ${:length :size}) :zero?)
94
+ PATTERN
95
+
96
+ # @!method zero_length_comparison(node)
97
+ def_node_matcher :zero_length_comparison, <<~PATTERN
81
98
  {(send (send (...) ${:length :size}) $:== (int $0))
82
99
  (send (int $0) $:== (send (...) ${:length :size}))
83
100
  (send (send (...) ${:length :size}) $:< (int $1))
84
101
  (send (int $1) $:> (send (...) ${:length :size}))}
85
102
  PATTERN
86
103
 
87
- # @!method nonzero_length_predicate(node)
88
- def_node_matcher :nonzero_length_predicate, <<~PATTERN
104
+ # @!method nonzero_length_comparison(node)
105
+ def_node_matcher :nonzero_length_comparison, <<~PATTERN
89
106
  {(send (send (...) ${:length :size}) ${:> :!=} (int $0))
90
107
  (send (int $0) ${:< :!=} (send (...) ${:length :size}))}
91
108
  PATTERN
@@ -10,20 +10,6 @@ module RuboCop
10
10
  # first the ones needed for autocorrection (if any), then the rest
11
11
  # (unless autocorrections happened).
12
12
  class Team
13
- attr_reader :errors, :warnings, :updated_source_file, :cops
14
-
15
- alias updated_source_file? updated_source_file
16
-
17
- def initialize(cops, config = nil, options = {})
18
- @cops = cops
19
- @config = config
20
- @options = options
21
- reset
22
- @ready = true
23
-
24
- validate_config
25
- end
26
-
27
13
  # @return [Team]
28
14
  def self.new(cop_or_classes, config, options = {})
29
15
  # Support v0 api:
@@ -38,7 +24,7 @@ module RuboCop
38
24
  new(cops, config, options)
39
25
  end
40
26
 
41
- # @return [Array<Cop::Cop>]
27
+ # @return [Array<Cop::Base>]
42
28
  def self.mobilize_cops(cop_classes, config, options = {})
43
29
  cop_classes = Registry.new(cop_classes.to_a, options) unless cop_classes.is_a?(Registry)
44
30
 
@@ -47,6 +33,35 @@ module RuboCop
47
33
  end
48
34
  end
49
35
 
36
+ # @return [Array<Force>] needed for the given cops
37
+ def self.forces_for(cops)
38
+ needed = Hash.new { |h, k| h[k] = [] }
39
+ cops.each do |cop|
40
+ forces = cop.class.joining_forces
41
+ if forces.is_a?(Array)
42
+ forces.each { |force| needed[force] << cop }
43
+ elsif forces
44
+ needed[forces] << cop
45
+ end
46
+ end
47
+
48
+ needed.map { |force_class, joining_cops| force_class.new(joining_cops) }
49
+ end
50
+
51
+ attr_reader :errors, :warnings, :updated_source_file, :cops
52
+
53
+ alias updated_source_file? updated_source_file
54
+
55
+ def initialize(cops, config = nil, options = {})
56
+ @cops = cops
57
+ @config = config
58
+ @options = options
59
+ reset
60
+ @ready = true
61
+
62
+ validate_config
63
+ end
64
+
50
65
  def autocorrect?
51
66
  @options[:autocorrect]
52
67
  end
@@ -94,21 +109,6 @@ module RuboCop
94
109
  @forces ||= self.class.forces_for(cops)
95
110
  end
96
111
 
97
- # @return [Array<Force>] needed for the given cops
98
- def self.forces_for(cops)
99
- needed = Hash.new { |h, k| h[k] = [] }
100
- cops.each do |cop|
101
- forces = cop.class.joining_forces
102
- if forces.is_a?(Array)
103
- forces.each { |force| needed[force] << cop }
104
- elsif forces
105
- needed[forces] << cop
106
- end
107
- end
108
-
109
- needed.map { |force_class, joining_cops| force_class.new(joining_cops) }
110
- end
111
-
112
112
  def external_dependency_checksum
113
113
  keys = cops.map(&:external_dependency_checksum).compact
114
114
  Digest::SHA1.hexdigest(keys.join)