rubocop 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +16 -1
  4. data/lib/rubocop.rb +1 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -2
  6. data/lib/rubocop/comment_config.rb +43 -94
  7. data/lib/rubocop/cop/correctors/alignment_corrector.rb +3 -6
  8. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +11 -8
  9. data/lib/rubocop/cop/layout/argument_alignment.rb +6 -5
  10. data/lib/rubocop/cop/layout/array_alignment.rb +7 -6
  11. data/lib/rubocop/cop/layout/assignment_indentation.rb +6 -3
  12. data/lib/rubocop/cop/layout/block_end_newline.rb +4 -8
  13. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +14 -15
  14. data/lib/rubocop/cop/layout/comment_indentation.rb +16 -16
  15. data/lib/rubocop/cop/layout/else_alignment.rb +9 -6
  16. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -5
  17. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +9 -6
  18. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +22 -15
  19. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +6 -5
  20. data/lib/rubocop/cop/layout/indentation_consistency.rb +9 -6
  21. data/lib/rubocop/cop/layout/indentation_style.rb +27 -30
  22. data/lib/rubocop/cop/layout/indentation_width.rb +19 -9
  23. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +6 -5
  24. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -5
  25. data/lib/rubocop/cop/layout/parameter_alignment.rb +6 -5
  26. data/lib/rubocop/cop/lint/number_conversion.rb +7 -0
  27. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -2
  28. data/lib/rubocop/cop/lint/suppressed_exception.rb +44 -1
  29. data/lib/rubocop/cop/lint/symbol_conversion.rb +89 -2
  30. data/lib/rubocop/cop/mixin/alignment.rb +10 -3
  31. data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
  32. data/lib/rubocop/cop/mixin/documentation_comment.rb +1 -1
  33. data/lib/rubocop/cop/mixin/line_length_help.rb +11 -6
  34. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +3 -1
  35. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +4 -3
  36. data/lib/rubocop/cop/mixin/preferred_delimiters.rb +1 -1
  37. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +4 -6
  38. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +4 -0
  39. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +10 -0
  40. data/lib/rubocop/cop/registry.rb +9 -0
  41. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  42. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +59 -71
  43. data/lib/rubocop/cop/style/bisected_attr_accessor/macro.rb +62 -0
  44. data/lib/rubocop/cop/style/case_like_if.rb +15 -4
  45. data/lib/rubocop/cop/style/class_equality_comparison.rb +2 -0
  46. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  47. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +2 -2
  48. data/lib/rubocop/cop/style/documentation.rb +25 -3
  49. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  50. data/lib/rubocop/cop/style/hash_syntax.rb +16 -15
  51. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +40 -0
  52. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -2
  53. data/lib/rubocop/cop/style/negated_if_else_condition.rb +15 -2
  54. data/lib/rubocop/cop/style/redundant_begin.rb +26 -3
  55. data/lib/rubocop/cop/style/redundant_return.rb +4 -0
  56. data/lib/rubocop/cop/style/redundant_self.rb +7 -3
  57. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  58. data/lib/rubocop/cop/style/rescue_modifier.rb +17 -14
  59. data/lib/rubocop/cop/style/sole_nested_conditional.rb +16 -0
  60. data/lib/rubocop/cop/style/string_chars.rb +38 -0
  61. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -0
  62. data/lib/rubocop/cop/style/trailing_body_on_method_definition.rb +5 -1
  63. data/lib/rubocop/cop/style/unless_logical_operators.rb +8 -2
  64. data/lib/rubocop/directive_comment.rb +64 -9
  65. data/lib/rubocop/ext/regexp_parser.rb +3 -6
  66. data/lib/rubocop/magic_comment.rb +1 -1
  67. data/lib/rubocop/target_finder.rb +1 -0
  68. data/lib/rubocop/version.rb +1 -1
  69. metadata +5 -3
@@ -42,12 +42,13 @@ module RuboCop
42
42
  # end
43
43
  # end
44
44
  # end
45
- class IndentationWidth < Cop # rubocop:disable Metrics/ClassLength
45
+ class IndentationWidth < Base # rubocop:disable Metrics/ClassLength
46
46
  include EndKeywordAlignment
47
47
  include Alignment
48
48
  include CheckAssignment
49
49
  include IgnoredPattern
50
50
  include RangeHelp
51
+ extend AutoCorrector
51
52
 
52
53
  MSG = 'Use %<configured_indentation_width>d (not %<indentation>d) ' \
53
54
  'spaces for%<name>s indentation.'
@@ -145,12 +146,12 @@ module RuboCop
145
146
  check_if(node, node.body, node.else_branch, base.loc)
146
147
  end
147
148
 
148
- def autocorrect(node)
149
- AlignmentCorrector.correct(processed_source, node, @column_delta)
150
- end
151
-
152
149
  private
153
150
 
151
+ def autocorrect(corrector, node)
152
+ AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
153
+ end
154
+
154
155
  def check_members(base, members)
155
156
  check_indentation(base, select_check_member(members.first))
156
157
 
@@ -280,8 +281,9 @@ module RuboCop
280
281
  name = style == 'normal' ? '' : " #{style}"
281
282
  message = message(configured_indentation_width, indentation, name)
282
283
 
283
- add_offense(node, location: offending_range(body_node, indentation),
284
- message: message)
284
+ add_offense(offending_range(body_node, indentation), message: message) do |corrector|
285
+ autocorrect(corrector, node)
286
+ end
285
287
  end
286
288
 
287
289
  def message(configured_indentation_width, indentation, name)
@@ -308,12 +310,20 @@ module RuboCop
308
310
  def indentation_to_check?(base_loc, body_node)
309
311
  return false if skip_check?(base_loc, body_node)
310
312
 
311
- if %i[rescue ensure].include?(body_node.type)
313
+ if body_node.rescue_type?
314
+ check_rescue?(body_node)
315
+ elsif body_node.ensure_type?
312
316
  block_body, = *body_node
313
317
  return unless block_body
318
+
319
+ check_rescue?(block_body) if block_body.rescue_type?
320
+ else
321
+ true
314
322
  end
323
+ end
315
324
 
316
- true
325
+ def check_rescue?(rescue_node)
326
+ rescue_node.body
317
327
  end
318
328
 
319
329
  def skip_check?(base_loc, body_node)
@@ -46,10 +46,11 @@ module RuboCop
46
46
  # .a
47
47
  # .b
48
48
  # .c
49
- class MultilineMethodCallIndentation < Cop
49
+ class MultilineMethodCallIndentation < Base
50
50
  include ConfigurableEnforcedStyle
51
51
  include Alignment
52
52
  include MultilineExpressionIndentation
53
+ extend AutoCorrector
53
54
 
54
55
  def validate_config
55
56
  return unless style == :aligned && cop_config['IndentationWidth']
@@ -61,12 +62,12 @@ module RuboCop
61
62
  '`EnforcedStyle` is `indented`.'
62
63
  end
63
64
 
64
- def autocorrect(node)
65
- AlignmentCorrector.correct(processed_source, node, @column_delta)
66
- end
67
-
68
65
  private
69
66
 
67
+ def autocorrect(corrector, node)
68
+ AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
69
+ end
70
+
70
71
  def relevant_node?(send_node)
71
72
  send_node.loc.dot # Only check method calls with dot operator
72
73
  end
@@ -41,10 +41,11 @@ module RuboCop
41
41
  # something_else
42
42
  # end
43
43
  #
44
- class MultilineOperationIndentation < Cop
44
+ class MultilineOperationIndentation < Base
45
45
  include ConfigurableEnforcedStyle
46
46
  include Alignment
47
47
  include MultilineExpressionIndentation
48
+ extend AutoCorrector
48
49
 
49
50
  def on_and(node)
50
51
  check_and_or(node)
@@ -63,12 +64,12 @@ module RuboCop
63
64
  '`EnforcedStyle` is `indented`.'
64
65
  end
65
66
 
66
- def autocorrect(node)
67
- AlignmentCorrector.correct(processed_source, node, @column_delta)
68
- end
69
-
70
67
  private
71
68
 
69
+ def autocorrect(corrector, node)
70
+ AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
71
+ end
72
+
72
73
  def relevant_node?(node)
73
74
  return false if node.send_type? && node.unary_operation?
74
75
 
@@ -68,8 +68,9 @@ module RuboCop
68
68
  # baz)
69
69
  # 123
70
70
  # end
71
- class ParameterAlignment < Cop
71
+ class ParameterAlignment < Base
72
72
  include Alignment
73
+ extend AutoCorrector
73
74
 
74
75
  ALIGN_PARAMS_MSG = 'Align the parameters of a method definition if ' \
75
76
  'they span more than one line.'
@@ -84,12 +85,12 @@ module RuboCop
84
85
  end
85
86
  alias on_defs on_def
86
87
 
87
- def autocorrect(node)
88
- AlignmentCorrector.correct(processed_source, node, column_delta)
89
- end
90
-
91
88
  private
92
89
 
90
+ def autocorrect(corrector, node)
91
+ AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
92
+ end
93
+
93
94
  def message(_node)
94
95
  fixed_indentation? ? FIXED_INDENT_MSG : ALIGN_PARAMS_MSG
95
96
  end
@@ -105,6 +105,8 @@ module RuboCop
105
105
  corrected_method: correct_sym_method(to_method)
106
106
  )
107
107
  add_offense(node, message: message) do |corrector|
108
+ remove_parentheses(corrector, node) if node.parenthesized?
109
+
108
110
  corrector.replace(sym_node, correct_sym_method(to_method))
109
111
  end
110
112
  end
@@ -120,6 +122,11 @@ module RuboCop
120
122
  "{ |i| #{body} }"
121
123
  end
122
124
 
125
+ def remove_parentheses(corrector, node)
126
+ corrector.replace(node.loc.begin, ' ')
127
+ corrector.remove(node.loc.end)
128
+ end
129
+
123
130
  def ignore_receiver?(receiver)
124
131
  if receiver.send_type? && ignored_method?(receiver.method_name)
125
132
  true
@@ -174,8 +174,7 @@ module RuboCop
174
174
  end
175
175
 
176
176
  def directive_count(comment)
177
- match = comment.text.match(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
178
- _, cops_string = match.captures
177
+ _, cops_string = DirectiveComment.new(comment).match_captures
179
178
  cops_string.split(/,\s*/).size
180
179
  end
181
180
 
@@ -64,12 +64,51 @@ module RuboCop
64
64
  # rescue
65
65
  # # do nothing
66
66
  # end
67
+ #
68
+ # @example AllowNil: true (default)
69
+ #
70
+ # # good
71
+ # def some_method
72
+ # do_something
73
+ # rescue
74
+ # nil
75
+ # end
76
+ #
77
+ # # good
78
+ # begin
79
+ # do_something
80
+ # rescue
81
+ # # do nothing
82
+ # end
83
+ #
84
+ # # good
85
+ # do_something rescue nil
86
+ #
87
+ # @example AllowNil: false
88
+ #
89
+ # # bad
90
+ # def some_method
91
+ # do_something
92
+ # rescue
93
+ # nil
94
+ # end
95
+ #
96
+ # # bad
97
+ # begin
98
+ # do_something
99
+ # rescue
100
+ # nil
101
+ # end
102
+ #
103
+ # # bad
104
+ # do_something rescue nil
67
105
  class SuppressedException < Base
68
106
  MSG = 'Do not suppress exceptions.'
69
107
 
70
108
  def on_resbody(node)
71
- return if node.body
109
+ return if node.body && !nil_body?(node)
72
110
  return if cop_config['AllowComments'] && comment_between_rescue_and_end?(node)
111
+ return if cop_config['AllowNil'] && nil_body?(node)
73
112
 
74
113
  add_offense(node)
75
114
  end
@@ -83,6 +122,10 @@ module RuboCop
83
122
  end_line = ancestor.loc.end.line
84
123
  processed_source[node.first_line...end_line].any? { |line| comment_line?(line) }
85
124
  end
125
+
126
+ def nil_body?(node)
127
+ node.body&.nil_type?
128
+ end
86
129
  end
87
130
  end
88
131
  end
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # This cop checks for uses of literal strings converted to
7
7
  # a symbol where a literal symbol could be used instead.
8
8
  #
9
+ # There are two possible styles for this cop.
10
+ # `strict` (default) will register an offense for any incorrect usage.
11
+ # `consistent` additionally requires hashes to use the same style for
12
+ # every symbol key (ie. if any symbol key needs to be quoted it requires
13
+ # all keys to be quoted).
14
+ #
9
15
  # @example
10
16
  # # bad
11
17
  # 'string'.to_sym
@@ -21,10 +27,49 @@ module RuboCop
21
27
  # :underscored_symbol
22
28
  # :'hyphenated-string'
23
29
  #
30
+ # @example EnforcedStyle: strict (default)
31
+ #
32
+ # # bad
33
+ # {
34
+ # 'a': 1,
35
+ # "b": 2,
36
+ # 'c-d': 3
37
+ # }
38
+ #
39
+ # # good (don't quote keys that don't require quoting)
40
+ # {
41
+ # a: 1,
42
+ # b: 2,
43
+ # 'c-d': 3
44
+ # }
45
+ #
46
+ # @example EnforcedStyle: consistent
47
+ #
48
+ # # bad
49
+ # {
50
+ # a: 1,
51
+ # 'b-c': 2
52
+ # }
53
+ #
54
+ # # good (quote all keys if any need quoting)
55
+ # {
56
+ # 'a': 1,
57
+ # 'b-c': 2
58
+ # }
59
+ #
60
+ # # good (no quoting required)
61
+ # {
62
+ # a: 1,
63
+ # b: 2
64
+ # }
65
+ #
24
66
  class SymbolConversion < Base
25
67
  extend AutoCorrector
68
+ include ConfigurableEnforcedStyle
26
69
 
27
70
  MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
71
+ MSG_CONSISTENCY = 'Symbol hash key should be quoted for consistency; ' \
72
+ 'use `%<correction>s` instead.'
28
73
  RESTRICT_ON_SEND = %i[to_sym intern].freeze
29
74
 
30
75
  def on_send(node)
@@ -35,7 +80,7 @@ module RuboCop
35
80
  end
36
81
 
37
82
  def on_sym(node)
38
- return if properly_quoted?(node.source, node.value.inspect)
83
+ return if ignored_node?(node) || properly_quoted?(node.source, node.value.inspect)
39
84
 
40
85
  # `alias` arguments are symbols but since a symbol that requires
41
86
  # being quoted is not a valid method identifier, it can be ignored
@@ -51,6 +96,21 @@ module RuboCop
51
96
  register_offense(node, correction: node.value.inspect)
52
97
  end
53
98
 
99
+ def on_hash(node)
100
+ # For `EnforcedStyle: strict`, hash keys are evaluated in `on_sym`
101
+ return unless style == :consistent
102
+
103
+ keys = node.keys.select(&:sym_type?)
104
+
105
+ if keys.any? { |key| requires_quotes?(key) }
106
+ correct_inconsistent_hash_keys(keys)
107
+ else
108
+ # If there are no symbol keys requiring quoting,
109
+ # treat the hash like `EnforcedStyle: strict`.
110
+ keys.each { |key| correct_hash_key(key) }
111
+ end
112
+ end
113
+
54
114
  private
55
115
 
56
116
  def register_offense(node, correction:, message: format(MSG, correction: correction))
@@ -60,7 +120,7 @@ module RuboCop
60
120
  end
61
121
 
62
122
  def properly_quoted?(source, value)
63
- return true if !source.match?(/['"]/) || value.end_with?('=')
123
+ return true if style == :strict && (!source.match?(/['"]/) || value.end_with?('='))
64
124
 
65
125
  source == value ||
66
126
  # `Symbol#inspect` uses double quotes, but allow single-quoted
@@ -68,6 +128,10 @@ module RuboCop
68
128
  source.tr("'", '"') == value
69
129
  end
70
130
 
131
+ def requires_quotes?(sym_node)
132
+ sym_node.value.inspect.match?(/^:".*?"|=$/)
133
+ end
134
+
71
135
  def in_alias?(node)
72
136
  node.parent&.alias_type?
73
137
  end
@@ -97,6 +161,29 @@ module RuboCop
97
161
  message: format(MSG, correction: "#{correction}:")
98
162
  )
99
163
  end
164
+
165
+ def correct_inconsistent_hash_keys(keys)
166
+ keys.each do |key|
167
+ ignore_node(key)
168
+
169
+ next if requires_quotes?(key)
170
+ next if properly_quoted?(key.source, %("#{key.value}"))
171
+
172
+ correction = "#{quote_type}#{key.value}#{quote_type}"
173
+ register_offense(
174
+ key,
175
+ correction: correction,
176
+ message: format(MSG_CONSISTENCY, correction: "#{correction}:")
177
+ )
178
+ end
179
+ end
180
+
181
+ def quote_type
182
+ # Use the `Style/StringLiterals` configuration for quoting symbols
183
+ return '"' unless config.for_cop('Style/StringLiterals')['Enabled']
184
+
185
+ config.for_cop('Style/StringLiterals')['EnforcedStyle'] == 'single_quotes' ? "'" : '"'
186
+ end
100
187
  end
101
188
  end
102
189
  end
@@ -29,15 +29,15 @@ module RuboCop
29
29
 
30
30
  each_bad_alignment(items, base_column) do |current|
31
31
  expr = current.source_range
32
- if offenses.any? { |o| within?(expr, o.location) }
32
+ if @current_offenses.any? { |o| within?(expr, o.location) }
33
33
  # If this offense is within a line range that is already being
34
34
  # realigned by autocorrect, we report the offense without
35
35
  # autocorrecting it. Two rewrites in the same area by the same
36
36
  # cop cannot be handled. The next iteration will find the
37
37
  # offense again and correct it.
38
- add_offense(nil, location: expr)
38
+ register_offense(expr, nil)
39
39
  else
40
- add_offense(current)
40
+ register_offense(current, current)
41
41
  end
42
42
  end
43
43
  end
@@ -71,6 +71,13 @@ module RuboCop
71
71
  def end_of_line_comment(line)
72
72
  processed_source.line_with_comment?(line)
73
73
  end
74
+
75
+ # @api private
76
+ def register_offense(offense_node, message_node)
77
+ add_offense(offense_node, message: message(message_node)) do |corrector|
78
+ autocorrect(corrector, message_node)
79
+ end
80
+ end
74
81
  end
75
82
  end
76
83
  end
@@ -23,7 +23,11 @@ module RuboCop
23
23
  def begin_pos_with_comment(node)
24
24
  first_comment = processed_source.ast_with_comments[node].first
25
25
 
26
- start_line_position(first_comment || node)
26
+ if first_comment && (first_comment.loc.line < node.loc.line)
27
+ start_line_position(first_comment)
28
+ else
29
+ start_line_position(node)
30
+ end
27
31
  end
28
32
 
29
33
  def start_line_position(node)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  end
46
46
 
47
47
  def rubocop_directive_comment?(comment)
48
- CommentConfig::COMMENT_DIRECTIVE_REGEXP.match?(comment.text)
48
+ !!DirectiveComment.new(comment).match_captures
49
49
  end
50
50
  end
51
51
  end