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
@@ -20,30 +20,10 @@ module RuboCop
20
20
 
21
21
  def check(offenses, cop_disabled_line_ranges, comments)
22
22
  unneeded_cops = Hash.new { |h, k| h[k] = Set.new }
23
- disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
24
23
 
25
- cop_disabled_line_ranges.each do |cop, line_ranges|
26
- line_ranges.each_cons(2) do |previous_range, range|
27
- next if previous_range.end != range.begin
28
-
29
- # If a cop is disabled in a range that begins on the same line as
30
- # the end of the previous range, it means that the cop was
31
- # already disabled by an earlier comment. So it's unneeded
32
- # whether there are offenses or not.
33
- comment = comments.find { |c| c.loc.line == range.begin }
34
- unneeded_cops[comment].add(cop)
35
- end
36
-
37
- line_ranges.each do |line_range|
38
- comment = comments.find { |c| c.loc.line == line_range.begin }
39
-
40
- unless all_disabled?(comment)
41
- next if ignore_offense?(disabled_ranges, line_range)
42
- end
43
-
44
- unneeded_cop = find_unneeded(comment, offenses, cop, line_range)
45
- unneeded_cops[comment].add(unneeded_cop) if unneeded_cop
46
- end
24
+ each_unneeded_disable(cop_disabled_line_ranges,
25
+ offenses, comments) do |comment, unneeded_cop|
26
+ unneeded_cops[comment].add(unneeded_cop)
47
27
  end
48
28
 
49
29
  add_offenses(unneeded_cops)
@@ -77,6 +57,39 @@ module RuboCop
77
57
 
78
58
  private
79
59
 
60
+ def each_unneeded_disable(cop_disabled_line_ranges, offenses, comments)
61
+ disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
62
+
63
+ cop_disabled_line_ranges.each do |cop, line_ranges|
64
+ each_already_disabled(line_ranges, comments) do |comment|
65
+ yield comment, cop
66
+ end
67
+
68
+ line_ranges.each do |line_range|
69
+ comment = comments.find { |c| c.loc.line == line_range.begin }
70
+
71
+ unless all_disabled?(comment)
72
+ next if ignore_offense?(disabled_ranges, line_range)
73
+ end
74
+
75
+ unneeded_cop = find_unneeded(comment, offenses, cop, line_range)
76
+ yield comment, unneeded_cop if unneeded_cop
77
+ end
78
+ end
79
+ end
80
+
81
+ def each_already_disabled(line_ranges, comments)
82
+ line_ranges.each_cons(2) do |previous_range, range|
83
+ next if previous_range.end != range.begin
84
+
85
+ # If a cop is disabled in a range that begins on the same line as
86
+ # the end of the previous range, it means that the cop was
87
+ # already disabled by an earlier comment. So it's unneeded
88
+ # whether there are offenses or not.
89
+ yield comments.find { |c| c.loc.line == range.begin }
90
+ end
91
+ end
92
+
80
93
  def find_unneeded(comment, offenses, cop, line_range)
81
94
  if all_disabled?(comment)
82
95
  'all' if offenses.none? { |o| line_range.cover?(o.line) }
@@ -13,7 +13,6 @@ module RuboCop
13
13
  # end
14
14
  class UnusedBlockArgument < Cop
15
15
  include UnusedArgument
16
- include Util
17
16
 
18
17
  def check_argument(variable)
19
18
  return unless variable.block_argument?
@@ -55,7 +55,7 @@ module RuboCop
55
55
  def check_scope(node, cur_vis = :public)
56
56
  unused = nil
57
57
 
58
- node.children.each do |child|
58
+ node.child_nodes.each do |child|
59
59
  if (new_vis = access_modifier(child))
60
60
  # does this modifier just repeat the existing visibility?
61
61
  if new_vis == cur_vis
@@ -70,14 +70,14 @@ module RuboCop
70
70
  cur_vis = new_vis
71
71
  elsif method_definition?(child)
72
72
  unused = nil
73
- elsif child.kwbegin_type?
74
- cur_vis = check_scope(child, cur_vis)
73
+ elsif child.kwbegin_type? || child.send_type?
74
+ cur_vis, unused = check_scope(child, cur_vis)
75
75
  end
76
76
  end
77
77
 
78
78
  add_offense(unused, :expression, format(MSG, cur_vis)) if unused
79
79
 
80
- cur_vis
80
+ [cur_vis, unused]
81
81
  end
82
82
  end
83
83
  end
@@ -16,30 +16,37 @@ module RuboCop
16
16
  def investigate(processed_source)
17
17
  heredocs = extract_heredocs(processed_source.ast) if allow_heredoc?
18
18
  processed_source.lines.each_with_index do |line, index|
19
- next unless line.length > max
19
+ check_line(line, index, heredocs)
20
+ end
21
+ end
20
22
 
21
- if allow_heredoc?
22
- next if line_in_whitelisted_heredoc?(heredocs, index.succ)
23
- end
23
+ def check_line(line, index, heredocs)
24
+ return unless line.length > max
25
+ return if heredocs &&
26
+ line_in_whitelisted_heredoc?(heredocs, index.succ)
24
27
 
25
- if allow_uri?
26
- uri_range = find_excessive_uri_range(line)
27
- next if uri_range && allowed_uri_position?(line, uri_range)
28
- end
28
+ if allow_uri?
29
+ uri_range = find_excessive_uri_range(line)
30
+ return if uri_range && allowed_uri_position?(line, uri_range)
31
+ end
29
32
 
30
- message = format(MSG, line.length, max)
33
+ offense(excess_range(uri_range, line, index), line)
34
+ end
31
35
 
32
- excessive_position = if uri_range && uri_range.begin < max
33
- uri_range.end
34
- else
35
- max
36
- end
36
+ def offense(loc, line)
37
+ message = format(MSG, line.length, max)
38
+ add_offense(nil, loc, message) { self.max = line.length }
39
+ end
37
40
 
38
- range = source_range(processed_source.buffer, index + 1,
39
- excessive_position...(line.length))
41
+ def excess_range(uri_range, line, index)
42
+ excessive_position = if uri_range && uri_range.begin < max
43
+ uri_range.end
44
+ else
45
+ max
46
+ end
40
47
 
41
- add_offense(nil, range, message) { self.max = line.length }
42
- end
48
+ source_range(processed_source.buffer, index + 1,
49
+ excessive_position...(line.length))
43
50
  end
44
51
 
45
52
  def max
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  def display_column(range)
26
26
  line = processed_source.lines[range.line - 1]
27
- line[0, range.column].display_width
27
+ Unicode::DisplayWidth.of(line[0, range.column])
28
28
  end
29
29
 
30
30
  def check_alignment(items, base_column = nil)
@@ -19,7 +19,7 @@ module RuboCop
19
19
  length = code_length(node)
20
20
  return unless length > max_length
21
21
 
22
- add_offense(node, :keyword, message(length, max_length)) do
22
+ add_offense(node, :expression, message(length, max_length)) do
23
23
  self.max = length
24
24
  end
25
25
  end
@@ -11,10 +11,6 @@ module RuboCop
11
11
  node.loc.respond_to?(:end) && node.loc.end.nil?
12
12
  end
13
13
 
14
- def ternary_op?(node)
15
- node.loc.respond_to?(:question)
16
- end
17
-
18
14
  def elsif?(node)
19
15
  node.loc.respond_to?(:keyword) && node.loc.keyword &&
20
16
  node.loc.keyword.is?('elsif')
@@ -109,7 +109,11 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def argument_in_method_call(node)
112
- node.each_ancestor(:send).find do |a|
112
+ node.each_ancestor(:send, :block).find do |a|
113
+ # If the node is inside a block, it makes no difference if that block
114
+ # is an argument in a method call. It doesn't count.
115
+ break false if a.block_type?
116
+
113
117
  _, method_name, *args = *a
114
118
  next if assignment_call?(method_name)
115
119
  args.any? { |arg| within_node?(node, arg) }
@@ -27,12 +27,12 @@ module RuboCop
27
27
  corrector.insert_before(node.loc.end, "\n".freeze)
28
28
  end
29
29
  else
30
- range = Parser::Source::Range.new(
31
- node.source_range.source_buffer,
32
- children(node).last.source_range.end_pos,
33
- node.loc.end.begin_pos)
34
-
35
- ->(corrector) { corrector.remove(range) }
30
+ lambda do |corrector|
31
+ corrector.remove(range_with_surrounding_space(node.loc.end,
32
+ :left))
33
+ corrector.insert_after(children(node).last.source_range,
34
+ node.loc.end.source)
35
+ end
36
36
  end
37
37
  end
38
38
 
@@ -65,34 +65,36 @@ module RuboCop
65
65
  # on different lines, and each item within is on its own line, and the
66
66
  # closing bracket is on its own line.
67
67
  def multiline?(node)
68
- elements = if node.type == :send
69
- _receiver, _method_name, *args = *node
70
- args.flat_map do |a|
71
- # For each argument, if it is a multi-line hash,
72
- # then promote the hash elements to method arguments
73
- # for the purpose of determining multi-line-ness.
74
- if a.hash_type? && a.loc.first_line != a.loc.last_line
75
- a.children
76
- else
77
- a
78
- end
79
- end
80
- else
81
- node.children
82
- end
83
-
84
68
  # No need to process anything if the whole node is not multiline
85
69
  # Without the 2nd check, Foo.new({}) is considered multiline, which
86
70
  # it should not be. Essentially, if there are no elements, the
87
71
  # expression can not be multiline.
88
- return if !node.multiline? || elements.empty?
89
-
90
- items = elements.map(&:source_range)
91
- if style == :consistent_comma
92
- items.one? || items.each_cons(2).any? { |a, b| !on_same_line?(a, b) }
93
- else
94
- items << node.loc.end
95
- items.each_cons(2).all? { |a, b| !on_same_line?(a, b) }
72
+ return false unless node.multiline?
73
+
74
+ items = elements(node).map(&:source_range)
75
+ return false if items.empty?
76
+
77
+ # If brackets are on different lines and there is one item at least,
78
+ # then comma is needed anytime for consistent_comma.
79
+ return true if style == :consistent_comma
80
+
81
+ items << node.loc.end
82
+ items.each_cons(2).all? { |a, b| !on_same_line?(a, b) }
83
+ end
84
+
85
+ def elements(node)
86
+ return node.children unless node.send_type?
87
+
88
+ _receiver, _method_name, *args = *node
89
+ args.flat_map do |a|
90
+ # For each argument, if it is a multi-line hash,
91
+ # then promote the hash elements to method arguments
92
+ # for the purpose of determining multi-line-ness.
93
+ if a.hash_type? && a.loc.first_line != a.loc.last_line
94
+ a.children
95
+ else
96
+ a
97
+ end
96
98
  end
97
99
  end
98
100
 
@@ -27,7 +27,15 @@ module RuboCop
27
27
  def autocorrect(node)
28
28
  return if [:kwarg, :kwoptarg].include?(node.type)
29
29
 
30
- ->(corrector) { corrector.insert_before(node.loc.name, '_') }
30
+ if node.blockarg_type?
31
+ lambda do |corrector|
32
+ range = range_with_surrounding_space(node.source_range, :left)
33
+ range = range_with_surrounding_comma(range, :left)
34
+ corrector.remove(range)
35
+ end
36
+ else
37
+ ->(corrector) { corrector.insert_before(node.loc.name, '_') }
38
+ end
31
39
  end
32
40
  end
33
41
  end
@@ -87,6 +87,22 @@ module RuboCop
87
87
  @status == :disabled
88
88
  end
89
89
 
90
+ # @api public
91
+ #
92
+ # @return [Parser::Source::Range]
93
+ # the range of the code that is highlighted
94
+ def highlighted_area
95
+ column_length = if location.first_line == location.last_line
96
+ location.column_range.count
97
+ else
98
+ location.source_line.length - location.column
99
+ end
100
+
101
+ Parser::Source::Range.new(location.source_line,
102
+ location.column,
103
+ location.column + column_length)
104
+ end
105
+
90
106
  # @api private
91
107
  # This is just for debugging purpose.
92
108
  def to_s
@@ -9,26 +9,31 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # @bad
12
- # 'aBc'.downcase == 'abc'
13
- # 'abc'.upcase.eql? 'ABC'
14
- # 'abc' == 'ABC'.downcase
15
- # 'ABC'.eql? 'abc'.upcase
16
- # 'abc'.downcase == 'abc'.downcase
12
+ # str.downcase == 'abc'
13
+ # str.upcase.eql? 'ABC'
14
+ # 'abc' == str.downcase
15
+ # 'ABC'.eql? str.upcase
16
+ # str.downcase == str.downcase
17
17
  #
18
18
  # @good
19
- # 'aBc'.casecmp('ABC').zero?
20
- # 'abc'.casecmp('abc').zero?
21
- # 'abc'.casecmp('ABC'.downcase).zero?
19
+ # str.casecmp('ABC').zero?
20
+ # 'abc'.casecmp(str).zero?
22
21
  class Casecmp < Cop
23
22
  MSG = 'Use `casecmp` instead of `%s %s`.'.freeze
24
23
  CASE_METHODS = [:downcase, :upcase].freeze
25
24
 
26
25
  def_node_matcher :downcase_eq, <<-END
27
- (send $(send _ ${:downcase :upcase}) ${:== :eql? :!=} $_)
26
+ (send
27
+ $(send _ ${:downcase :upcase})
28
+ ${:== :eql? :!=}
29
+ ${str (send _ {:downcase :upcase} ...) (begin str)})
28
30
  END
29
31
 
30
32
  def_node_matcher :eq_downcase, <<-END
31
- (send _ ${:== :eql? :!=} $(send _ ${:downcase :upcase}))
33
+ (send
34
+ {str (send _ {:downcase :upcase} ...) (begin str)}
35
+ ${:== :eql? :!=}
36
+ $(send _ ${:downcase :upcase}))
32
37
  END
33
38
 
34
39
  def on_send(node)
@@ -4,8 +4,9 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Performance
7
- # This cop is used to identify usages of `count` on an
8
- # `Enumerable` and change them to `size`.
7
+ # This cop is used to identify usages of `count` on an `Enumerable` that
8
+ # follow calls to `select` or `reject`. Querying logic can instead be
9
+ # passed to the `count` call.
9
10
  #
10
11
  # @example
11
12
  # # bad
@@ -7,21 +7,22 @@ module RuboCop
7
7
  # Do not compute the size of statically sized objects.
8
8
  class FixedSize < Cop
9
9
  MSG = 'Do not compute the size of statically sized objects.'.freeze
10
- COUNTERS = [:count, :length, :size].freeze
11
- STATIC_SIZED_TYPES = [:array, :hash, :str, :sym].freeze
10
+
11
+ def_node_matcher :counter, <<-MATCHER
12
+ (send ${array hash str sym} {:count :length :size} $...)
13
+ MATCHER
12
14
 
13
15
  def on_send(node)
14
- variable, method, arg = *node
15
- return unless variable
16
- return unless COUNTERS.include?(method)
17
- return unless STATIC_SIZED_TYPES.include?(variable.type)
18
- return if contains_splat?(variable)
19
- return if contains_double_splat?(variable)
20
- return if string_argument?(arg)
21
- if node.parent
22
- return if node.parent.casgn_type? || node.parent.block_type?
16
+ counter(node) do |variable, arg|
17
+ return if contains_splat?(variable)
18
+ return if contains_double_splat?(variable)
19
+ return if !arg.nil? && string_argument?(arg.first)
20
+ if node.parent
21
+ return if node.parent.casgn_type? || node.parent.block_type?
22
+ end
23
+
24
+ add_offense(node, :expression)
23
25
  end
24
- add_offense(node, :expression)
25
26
  end
26
27
 
27
28
  private
@@ -15,26 +15,27 @@ module RuboCop
15
15
  # [].reverse_each
16
16
  class ReverseEach < Cop
17
17
  MSG = 'Use `reverse_each` instead of `reverse.each`.'.freeze
18
+ UNDERSCORE = '_'.freeze
18
19
 
19
- def on_send(node)
20
- receiver, second_method = *node
21
- return unless second_method == :each
22
- return if receiver.nil?
23
- _, first_method = *receiver
24
- return unless first_method == :reverse
20
+ def_node_matcher :reverse_each?, <<-MATCHER
21
+ (send $(send array :reverse) :each)
22
+ MATCHER
25
23
 
26
- source_buffer = node.source_range.source_buffer
27
- location_of_reverse = receiver.loc.selector.begin_pos
28
- end_location = node.loc.selector.end_pos
24
+ def on_send(node)
25
+ reverse_each?(node) do |receiver|
26
+ source_buffer = node.source_range.source_buffer
27
+ location_of_reverse = receiver.loc.selector.begin_pos
28
+ end_location = node.loc.selector.end_pos
29
29
 
30
- range = Parser::Source::Range.new(source_buffer,
31
- location_of_reverse,
32
- end_location)
33
- add_offense(node, range, MSG)
30
+ range = Parser::Source::Range.new(source_buffer,
31
+ location_of_reverse,
32
+ end_location)
33
+ add_offense(node, range, MSG)
34
+ end
34
35
  end
35
36
 
36
37
  def autocorrect(node)
37
- ->(corrector) { corrector.replace(node.loc.dot, '_') }
38
+ ->(corrector) { corrector.replace(node.loc.dot, UNDERSCORE) }
38
39
  end
39
40
  end
40
41
  end