rubocop 1.39.0 → 1.44.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.
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)