rubocop 1.11.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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