rubocop 0.37.2 → 0.38.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/assets/output.html.erb +3 -0
  4. data/config/default.yml +14 -1
  5. data/config/disabled.yml +0 -4
  6. data/lib/rubocop.rb +0 -1
  7. data/lib/rubocop/ast_node.rb +6 -0
  8. data/lib/rubocop/ast_node/traversal.rb +1 -1
  9. data/lib/rubocop/cached_data.rb +6 -2
  10. data/lib/rubocop/config_loader.rb +7 -3
  11. data/lib/rubocop/cop/cop.rb +1 -1
  12. data/lib/rubocop/cop/lint/block_alignment.rb +91 -17
  13. data/lib/rubocop/cop/lint/else_layout.rb +24 -14
  14. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +6 -12
  15. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +16 -10
  16. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  17. data/lib/rubocop/cop/lint/unneeded_disable.rb +36 -23
  18. data/lib/rubocop/cop/lint/unused_block_argument.rb +0 -1
  19. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
  20. data/lib/rubocop/cop/metrics/line_length.rb +25 -18
  21. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  22. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  23. data/lib/rubocop/cop/mixin/if_node.rb +0 -4
  24. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -1
  25. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +6 -6
  26. data/lib/rubocop/cop/mixin/trailing_comma.rb +26 -24
  27. data/lib/rubocop/cop/mixin/unused_argument.rb +9 -1
  28. data/lib/rubocop/cop/offense.rb +16 -0
  29. data/lib/rubocop/cop/performance/casecmp.rb +15 -10
  30. data/lib/rubocop/cop/performance/count.rb +3 -2
  31. data/lib/rubocop/cop/performance/fixed_size.rb +13 -12
  32. data/lib/rubocop/cop/performance/reverse_each.rb +15 -14
  33. data/lib/rubocop/cop/rails/date.rb +13 -1
  34. data/lib/rubocop/cop/rails/output.rb +2 -1
  35. data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -1
  36. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  37. data/lib/rubocop/cop/style/and_or.rb +1 -2
  38. data/lib/rubocop/cop/style/block_delimiters.rb +41 -48
  39. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +4 -2
  40. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -3
  41. data/lib/rubocop/cop/style/comment_annotation.rb +8 -10
  42. data/lib/rubocop/cop/style/conditional_assignment.rb +7 -2
  43. data/lib/rubocop/cop/style/encoding.rb +15 -10
  44. data/lib/rubocop/cop/style/extra_spacing.rb +14 -12
  45. data/lib/rubocop/cop/style/file_name.rb +3 -7
  46. data/lib/rubocop/cop/style/lambda.rb +17 -6
  47. data/lib/rubocop/cop/style/method_call_parentheses.rb +1 -1
  48. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  49. data/lib/rubocop/cop/style/nested_modifier.rb +7 -1
  50. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +0 -4
  51. data/lib/rubocop/cop/style/not.rb +27 -5
  52. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -0
  53. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -1
  54. data/lib/rubocop/cop/style/signal_exception.rb +28 -26
  55. data/lib/rubocop/cop/style/space_around_keyword.rb +10 -6
  56. data/lib/rubocop/cop/style/string_literals.rb +1 -0
  57. data/lib/rubocop/cop/style/unless_else.rb +24 -0
  58. data/lib/rubocop/cop/style/unneeded_interpolation.rb +26 -13
  59. data/lib/rubocop/cop/style/zero_length_predicate.rb +5 -3
  60. data/lib/rubocop/cop/team.rb +6 -1
  61. data/lib/rubocop/cop/util.rb +13 -6
  62. data/lib/rubocop/formatter/clang_style_formatter.rb +18 -14
  63. data/lib/rubocop/formatter/html_formatter.rb +9 -10
  64. data/lib/rubocop/rake_task.rb +4 -4
  65. data/lib/rubocop/remote_config.rb +4 -2
  66. data/lib/rubocop/result_cache.rb +14 -8
  67. data/lib/rubocop/runner.rb +9 -0
  68. data/lib/rubocop/version.rb +1 -1
  69. data/spec/support/cop_helper.rb +81 -0
  70. metadata +13 -7
  71. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +0 -57
@@ -11,7 +11,7 @@ module RuboCop
11
11
  # Rails time zone. You must use Time.zone.today instead.
12
12
  #
13
13
  # The cop also reports warnings when you are using 'to_time' method,
14
- # because it doesn't know about Rails time zone too.
14
+ # because it doesn't know about Rails time zone either.
15
15
  #
16
16
  # Two styles are supported for this cop. When EnforcedStyle is 'strict'
17
17
  # then the Date methods (today, current, yesterday, tomorrow)
@@ -57,6 +57,10 @@ module RuboCop
57
57
  def on_send(node)
58
58
  receiver, method_name, *args = *node
59
59
  return unless receiver && bad_methods.include?(method_name)
60
+
61
+ chain = extract_method_chain(node)
62
+ return if safe_chain?(chain)
63
+
60
64
  return if method_name == :to_time && args.length == 1
61
65
 
62
66
  add_offense(node, :selector,
@@ -105,6 +109,10 @@ module RuboCop
105
109
  receiver == node
106
110
  end
107
111
 
112
+ def safe_chain?(chain)
113
+ (chain & bad_methods).empty? || !(chain & good_methods).empty?
114
+ end
115
+
108
116
  def good_days
109
117
  style == :strict ? [] : [:current, :yesterday, :tomorrow]
110
118
  end
@@ -116,6 +124,10 @@ module RuboCop
116
124
  def bad_methods
117
125
  style == :strict ? [:to_time, :to_time_in_current_zone] : [:to_time]
118
126
  end
127
+
128
+ def good_methods
129
+ style == :strict ? [] : TimeZone::ACCEPTED_METHODS
130
+ end
119
131
  end
120
132
  end
121
133
  end
@@ -13,7 +13,8 @@ module RuboCop
13
13
  :print,
14
14
  :p,
15
15
  :pp,
16
- :pretty_print].freeze
16
+ :pretty_print,
17
+ :ap].freeze
17
18
 
18
19
  def on_send(node)
19
20
  receiver, method_name, *args = *node
@@ -25,7 +25,7 @@ module RuboCop
25
25
  month: :months,
26
26
  year: :years }.freeze
27
27
 
28
- PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert
28
+ PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert.freeze
29
29
 
30
30
  MSG = 'Prefer `%s.%s`.'.freeze
31
31
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # @example
11
11
  #
12
12
  # # bad
13
- # x = read_attributed(:attr)
13
+ # x = read_attribute(:attr)
14
14
  # write_attribute(:attr, val)
15
15
  #
16
16
  # # good
@@ -6,7 +6,6 @@ module RuboCop
6
6
  module Style
7
7
  # This cop checks for uses of *and* and *or*.
8
8
  class AndOr < Cop
9
- include AutocorrectUnlessChangingAST
10
9
  include ConfigurableEnforcedStyle
11
10
 
12
11
  MSG = 'Use `%s` instead of `%s`.'.freeze
@@ -59,7 +58,7 @@ module RuboCop
59
58
  add_offense(node, :operator, format(MSG, OPS[op], op))
60
59
  end
61
60
 
62
- def correction(node)
61
+ def autocorrect(node)
63
62
  expr1, expr2 = *node
64
63
  replacement = (node.type == :and ? '&&' : '||')
65
64
  lambda do |corrector|
@@ -8,24 +8,24 @@ module RuboCop
8
8
  # multi-line blocks.
9
9
  class BlockDelimiters < Cop
10
10
  include ConfigurableEnforcedStyle
11
- include AutocorrectUnlessChangingAST
11
+
12
+ def_node_matcher :block_method_name, '(block (send _ $_ ...) ...)'
12
13
 
13
14
  def on_send(node)
14
15
  _receiver, method_name, *args = *node
15
- return unless args.any?
16
-
17
- block = get_block(args.last)
18
- return unless block && !parentheses?(node) && !operator?(method_name)
19
-
20
- # If there are no parentheses around the arguments, then braces and
21
- # do-end have different meaning due to how they bind, so we allow
22
- # either.
23
- ignore_node(block)
16
+ return if args.empty?
17
+ return if parentheses?(node) || operator?(method_name)
18
+
19
+ get_blocks(args.last) do |block|
20
+ # If there are no parentheses around the arguments, then braces and
21
+ # do-end have different meaning due to how they bind, so we allow
22
+ # either.
23
+ ignore_node(block)
24
+ end
24
25
  end
25
26
 
26
27
  def on_block(node)
27
28
  return if ignored_node?(node)
28
-
29
29
  return if proper_block_style?(node)
30
30
 
31
31
  add_offense(node, :begin)
@@ -34,9 +34,7 @@ module RuboCop
34
34
  private
35
35
 
36
36
  def line_count_based_message(node)
37
- block_length = block_length(node)
38
-
39
- if block_length > 0
37
+ if block_length(node) > 0
40
38
  'Avoid using `{...}` for multi-line blocks.'
41
39
  else
42
40
  'Prefer `{...}` over `do...end` for single-line blocks.'
@@ -54,9 +52,7 @@ module RuboCop
54
52
  end
55
53
 
56
54
  def braces_for_chaining_message(node)
57
- block_length = block_length(node)
58
-
59
- if block_length > 0
55
+ if block_length(node) > 0
60
56
  if return_value_chaining?(node)
61
57
  'Prefer `{...}` over `do...end` for multi-line chained blocks.'
62
58
  else
@@ -78,7 +74,9 @@ module RuboCop
78
74
  end
79
75
  end
80
76
 
81
- def correction(node)
77
+ def autocorrect(node)
78
+ return if correction_would_break_code?(node)
79
+
82
80
  lambda do |corrector|
83
81
  b = node.loc.begin
84
82
  e = node.loc.end
@@ -98,14 +96,23 @@ module RuboCop
98
96
  node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
99
97
  end
100
98
 
101
- def get_block(node)
99
+ def get_blocks(node, &block)
102
100
  case node.type
103
101
  when :block
104
- node
102
+ yield node
105
103
  when :send
106
104
  receiver, _method_name, *_args = *node
107
- get_block(receiver) if receiver
105
+ get_blocks(receiver, &block) if receiver
106
+ when :hash
107
+ # A hash which is passed as method argument may have no braces
108
+ # In that case, one of the K/V pairs could contain a block node
109
+ # which could change in meaning if do...end replaced {...}
110
+ return if node.loc.begin
111
+ node.children.each { |child| get_blocks(child, &block) }
112
+ when :pair
113
+ node.children.each { |child| get_blocks(child, &block) }
108
114
  end
115
+ nil
109
116
  end
110
117
 
111
118
  def proper_block_style?(node)
@@ -120,18 +127,13 @@ module RuboCop
120
127
  end
121
128
 
122
129
  def line_count_based_block_style?(node)
123
- block_length = block_length(node)
124
130
  block_begin = node.loc.begin.source
125
131
 
126
- if block_length > 0
127
- block_begin != '{'
128
- else
129
- block_begin == '{'
130
- end
132
+ (block_length(node) > 0) ^ (block_begin == '{')
131
133
  end
132
134
 
133
135
  def semantic_block_style?(node)
134
- method_name = extract_method_name_from_block(node)
136
+ method_name = block_method_name(node)
135
137
  return true if ignored_method?(method_name)
136
138
 
137
139
  block_begin = node.loc.begin.source
@@ -158,19 +160,22 @@ module RuboCop
158
160
  node.parent && node.parent.send_type? && node.parent.loc.dot
159
161
  end
160
162
 
161
- def extract_method_name_from_block(block)
162
- node, _args, _body = *block
163
- _receiver, method_name, *_args = *node
164
-
165
- method_name
163
+ def correction_would_break_code?(node)
164
+ if node.loc.begin.is?('do')
165
+ # Converting `obj.method arg do |x| end` to use `{}` would cause
166
+ # a syntax error.
167
+ send = node.children.first
168
+ _receiver, _method_name, *args = *send
169
+ !args.empty? && !parentheses?(send)
170
+ end
166
171
  end
167
172
 
168
173
  def ignored_method?(method_name)
169
- ignored_methods.include?(method_name)
174
+ cop_config['IgnoredMethods'].map(&:to_sym).include?(method_name)
170
175
  end
171
176
 
172
177
  def functional_method?(method_name)
173
- functional_methods.include?(method_name)
178
+ cop_config['FunctionalMethods'].map(&:to_sym).include?(method_name)
174
179
  end
175
180
 
176
181
  def functional_block?(node)
@@ -178,7 +183,7 @@ module RuboCop
178
183
  end
179
184
 
180
185
  def procedural_method?(method_name)
181
- procedural_methods.include?(method_name)
186
+ cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name)
182
187
  end
183
188
 
184
189
  def return_value_used?(node)
@@ -200,18 +205,6 @@ module RuboCop
200
205
  node.parent.children.last == node
201
206
  end
202
207
 
203
- def procedural_methods
204
- cop_config['ProceduralMethods'].map(&:to_sym)
205
- end
206
-
207
- def functional_methods
208
- cop_config['FunctionalMethods'].map(&:to_sym)
209
- end
210
-
211
- def ignored_methods
212
- cop_config['IgnoredMethods'].map(&:to_sym)
213
- end
214
-
215
208
  def conditional?(node)
216
209
  node.if_type? || node.or_type? || node.and_type?
217
210
  end
@@ -8,7 +8,6 @@ module RuboCop
8
8
  # if the last parameter is a hash.
9
9
  class BracesAroundHashParameters < Cop
10
10
  include ConfigurableEnforcedStyle
11
- include AutocorrectUnlessChangingAST
12
11
 
13
12
  MSG = '%s curly braces around a hash parameter.'.freeze
14
13
 
@@ -55,7 +54,7 @@ module RuboCop
55
54
  # node, because that context is needed. When parsing the code to see if
56
55
  # the AST has changed, a braceless hash would not be parsed as a hash
57
56
  # otherwise.
58
- def correction(send_node)
57
+ def autocorrect(send_node)
59
58
  _receiver, _method_name, *args = *send_node
60
59
  node = args.last
61
60
  lambda do |corrector|
@@ -71,6 +70,9 @@ module RuboCop
71
70
  comments = processed_source.comments
72
71
  right_brace_and_space = range_with_surrounding_space(node.loc.end,
73
72
  :left)
73
+ right_brace_and_space =
74
+ range_with_surrounding_comma(right_brace_and_space, :left)
75
+
74
76
  if comments.any? { |c| c.loc.line == right_brace_and_space.line }
75
77
  # Removing a line break between a comment and the closing
76
78
  # parenthesis would cause a syntax error, so we only remove the
@@ -17,18 +17,18 @@ module RuboCop
17
17
  # class Foo::Bar
18
18
  # end
19
19
  #
20
- # The compact style is only forced, for classes / modules with one child.
20
+ # The compact style is only forced for classes/modules with one child.
21
21
  class ClassAndModuleChildren < Cop
22
22
  include ConfigurableEnforcedStyle
23
23
 
24
24
  NESTED_MSG = 'Use nested module/class definitions instead of ' \
25
25
  'compact style.'.freeze
26
-
27
26
  COMPACT_MSG = 'Use compact module/class definition instead of ' \
28
27
  'nested style.'.freeze
29
28
 
30
29
  def on_class(node)
31
- _name, _superclass, body = *node
30
+ _name, superclass, body = *node
31
+ return if superclass
32
32
  check_style(node, body)
33
33
  end
34
34
 
@@ -23,17 +23,9 @@ module RuboCop
23
23
  next unless annotation?(comment) &&
24
24
  !correct_annotation?(first_word, colon, space, note)
25
25
 
26
- start = comment.loc.expression.begin_pos + margin.length
27
26
  length = first_word.length + colon.to_s.length + space.to_s.length
28
- source_buffer = comment.loc.expression.source_buffer
29
- range = Parser::Source::Range.new(source_buffer,
30
- start,
31
- start + length)
32
- if note
33
- add_offense(comment, range, format(MSG, first_word))
34
- else
35
- add_offense(comment, range, format(MISSING_NOTE, first_word))
36
- end
27
+ add_offense(comment, annotation_range(comment, margin, length),
28
+ format(note ? MSG : MISSING_NOTE, first_word))
37
29
  end
38
30
  end
39
31
 
@@ -43,6 +35,12 @@ module RuboCop
43
35
  ix == 0 || comments[ix - 1].loc.line < comments[ix].loc.line - 1
44
36
  end
45
37
 
38
+ def annotation_range(comment, margin, length)
39
+ start = comment.loc.expression.begin_pos + margin.length
40
+ source_buffer = comment.loc.expression.source_buffer
41
+ Parser::Source::Range.new(source_buffer, start, start + length)
42
+ end
43
+
46
44
  def autocorrect(comment)
47
45
  margin, first_word, colon, space, note = split_comment(comment)
48
46
  start = comment.loc.expression.begin_pos + margin.length
@@ -252,16 +252,21 @@ module RuboCop
252
252
  def correction_exceeds_line_limit?(node, branches)
253
253
  return false unless config.for_cop(LINE_LENGTH)[ENABLED]
254
254
  assignment = lhs(tail(branches[0]))
255
- assignment_regex = /#{assignment.gsub(' ', '\s*')}/
256
255
  max_line_length = config.for_cop(LINE_LENGTH)[MAX]
257
256
  indentation_width = config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2
258
257
  return true if longest_rhs(branches) + indentation_width +
259
258
  assignment.length > max_line_length
259
+
260
+ longest_line(node, assignment).length > max_line_length
261
+ end
262
+
263
+ def longest_line(node, assignment)
264
+ assignment_regex = /#{Regexp.escape(assignment).gsub(' ', '\s*')}/
260
265
  lines = node.source.lines.map do |line|
261
266
  line.chomp.sub(assignment_regex, '')
262
267
  end
263
268
  longest_line = lines.max_by(&:length)
264
- (longest_line + assignment).length > max_line_length
269
+ longest_line + assignment
265
270
  end
266
271
 
267
272
  def longest_rhs(branches)
@@ -25,22 +25,27 @@ module RuboCop
25
25
  return if processed_source.buffer.source.empty?
26
26
 
27
27
  line_number = encoding_line_number(processed_source)
28
- message = offense(processed_source, line_number)
28
+ return unless (@message = offense(processed_source, line_number))
29
29
 
30
- return unless message
31
-
32
- range = source_range(processed_source.buffer, line_number + 1, 0)
33
- add_offense(range, range, message)
30
+ range = processed_source.buffer.line_range(line_number + 1)
31
+ add_offense(range, range, @message)
34
32
  end
35
33
 
36
34
  def autocorrect(range)
37
- encoding = cop_config[AUTO_CORRECT_ENCODING_COMMENT]
38
- if encoding && encoding =~ ENCODING_PATTERN
39
- lambda do |corrector|
40
- corrector.insert_before(range, "#{encoding}\n")
35
+ if @message == MSG_MISSING
36
+ encoding = cop_config[AUTO_CORRECT_ENCODING_COMMENT]
37
+ if encoding && encoding =~ ENCODING_PATTERN
38
+ lambda do |corrector|
39
+ corrector.insert_before(range, "#{encoding}\n")
40
+ end
41
+ else
42
+ raise "#{encoding} does not match #{ENCODING_PATTERN}"
41
43
  end
42
44
  else
43
- raise "#{encoding} does not match #{ENCODING_PATTERN}"
45
+ # Need to remove unnecessary encoding comment
46
+ lambda do |corrector|
47
+ corrector.remove(range_with_surrounding_space(range, :right))
48
+ end
44
49
  end
45
50
  end
46
51
 
@@ -27,8 +27,6 @@ module RuboCop
27
27
  MSG_UNALIGNED_ASGN = '`=` is not aligned with the %s assignment.'.freeze
28
28
 
29
29
  def investigate(processed_source)
30
- ast = processed_source.ast
31
-
32
30
  if force_equal_sign_alignment?
33
31
  @asgn_tokens = processed_source.tokens.select { |t| equal_sign?(t) }
34
32
  # Only attempt to align the first = on each line
@@ -39,16 +37,7 @@ module RuboCop
39
37
  end
40
38
 
41
39
  processed_source.tokens.each_cons(2) do |t1, t2|
42
- next if t2.type == :tNL
43
-
44
- if force_equal_sign_alignment? &&
45
- @asgn_tokens.include?(t2) &&
46
- (@asgn_lines.include?(t2.pos.line - 1) ||
47
- @asgn_lines.include?(t2.pos.line + 1))
48
- check_assignment(t2)
49
- else
50
- check_other(t1, t2, ast)
51
- end
40
+ check_tokens(processed_source.ast, t1, t2)
52
41
  end
53
42
  end
54
43
 
@@ -64,6 +53,19 @@ module RuboCop
64
53
 
65
54
  private
66
55
 
56
+ def check_tokens(ast, t1, t2)
57
+ return if t2.type == :tNL
58
+
59
+ if force_equal_sign_alignment? &&
60
+ @asgn_tokens.include?(t2) &&
61
+ (@asgn_lines.include?(t2.pos.line - 1) ||
62
+ @asgn_lines.include?(t2.pos.line + 1))
63
+ check_assignment(t2)
64
+ else
65
+ check_other(t1, t2, ast)
66
+ end
67
+ end
68
+
67
69
  def check_assignment(token)
68
70
  # minus 2 is because pos.line is zero-based
69
71
  line = processed_source.lines[token.pos.line - 2]