rubocop 0.39.0 → 0.40.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/config/default.yml +65 -2
  4. data/config/disabled.yml +0 -28
  5. data/config/enabled.yml +40 -0
  6. data/lib/rubocop.rb +3 -0
  7. data/lib/rubocop/ast_node.rb +28 -13
  8. data/lib/rubocop/cached_data.rb +15 -2
  9. data/lib/rubocop/cli.rb +24 -8
  10. data/lib/rubocop/config.rb +3 -3
  11. data/lib/rubocop/config_loader.rb +0 -7
  12. data/lib/rubocop/cop/cop.rb +2 -2
  13. data/lib/rubocop/cop/lint/condition_position.rb +3 -1
  14. data/lib/rubocop/cop/lint/else_layout.rb +3 -2
  15. data/lib/rubocop/cop/lint/end_alignment.rb +2 -2
  16. data/lib/rubocop/cop/lint/nested_method_definition.rb +15 -9
  17. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  18. data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
  19. data/lib/rubocop/cop/lint/useless_access_modifier.rb +86 -20
  20. data/lib/rubocop/cop/lint/useless_array_splat.rb +56 -0
  21. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  22. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +11 -25
  23. data/lib/rubocop/cop/mixin/if_node.rb +4 -0
  24. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +14 -12
  25. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +75 -9
  26. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -1
  27. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -2
  28. data/lib/rubocop/cop/mixin/trailing_comma.rb +15 -5
  29. data/lib/rubocop/cop/performance/case_when_splat.rb +71 -44
  30. data/lib/rubocop/cop/performance/detect.rb +28 -18
  31. data/lib/rubocop/cop/performance/end_with.rb +1 -1
  32. data/lib/rubocop/cop/performance/redundant_merge.rb +29 -11
  33. data/lib/rubocop/cop/performance/start_with.rb +1 -1
  34. data/lib/rubocop/cop/performance/string_replacement.rb +39 -20
  35. data/lib/rubocop/cop/rails/action_filter.rb +1 -2
  36. data/lib/rubocop/cop/rails/date.rb +2 -5
  37. data/lib/rubocop/cop/rails/time_zone.rb +3 -6
  38. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +57 -0
  39. data/lib/rubocop/cop/style/alias.rb +10 -3
  40. data/lib/rubocop/cop/style/align_parameters.rb +8 -2
  41. data/lib/rubocop/cop/style/and_or.rb +29 -21
  42. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  43. data/lib/rubocop/cop/style/collection_methods.rb +1 -2
  44. data/lib/rubocop/cop/style/comment_indentation.rb +1 -1
  45. data/lib/rubocop/cop/style/conditional_assignment.rb +13 -7
  46. data/lib/rubocop/cop/style/empty_case_condition.rb +96 -0
  47. data/lib/rubocop/cop/style/encoding.rb +9 -5
  48. data/lib/rubocop/cop/style/extra_spacing.rb +22 -7
  49. data/lib/rubocop/cop/style/file_name.rb +7 -2
  50. data/lib/rubocop/cop/style/guard_clause.rb +18 -10
  51. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  52. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -1
  53. data/lib/rubocop/cop/style/indentation_width.rb +7 -4
  54. data/lib/rubocop/cop/style/lambda.rb +98 -30
  55. data/lib/rubocop/cop/style/line_end_concatenation.rb +5 -0
  56. data/lib/rubocop/cop/style/multiline_array_brace_layout.rb +34 -9
  57. data/lib/rubocop/cop/style/multiline_assignment_layout.rb +2 -1
  58. data/lib/rubocop/cop/style/multiline_hash_brace_layout.rb +42 -17
  59. data/lib/rubocop/cop/style/multiline_method_call_brace_layout.rb +39 -14
  60. data/lib/rubocop/cop/style/multiline_method_definition_brace_layout.rb +36 -15
  61. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +8 -6
  62. data/lib/rubocop/cop/style/negated_while.rb +2 -1
  63. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +15 -0
  64. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -8
  65. data/lib/rubocop/cop/style/next.rb +1 -1
  66. data/lib/rubocop/cop/style/not.rb +5 -2
  67. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  68. data/lib/rubocop/cop/style/raise_args.rb +70 -7
  69. data/lib/rubocop/cop/style/redundant_exception.rb +34 -20
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -1
  71. data/lib/rubocop/cop/style/space_after_colon.rb +14 -10
  72. data/lib/rubocop/cop/style/space_after_comma.rb +5 -0
  73. data/lib/rubocop/cop/style/space_after_not.rb +3 -4
  74. data/lib/rubocop/cop/style/space_after_semicolon.rb +5 -0
  75. data/lib/rubocop/cop/style/space_around_operators.rb +2 -1
  76. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  77. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  78. data/lib/rubocop/cop/style/string_methods.rb +1 -2
  79. data/lib/rubocop/cop/style/symbol_proc.rb +7 -1
  80. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +25 -0
  81. data/lib/rubocop/cop/style/word_array.rb +50 -22
  82. data/lib/rubocop/cop/util.rb +0 -4
  83. data/lib/rubocop/formatter/clang_style_formatter.rb +38 -22
  84. data/lib/rubocop/options.rb +45 -10
  85. data/lib/rubocop/path_util.rb +2 -34
  86. data/lib/rubocop/result_cache.rb +10 -4
  87. data/lib/rubocop/runner.rb +5 -3
  88. data/lib/rubocop/version.rb +1 -1
  89. metadata +7 -4
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Style
7
+ # This cop checks for case statements with an empty condition.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad:
12
+ # case
13
+ # when x == 0
14
+ # puts 'x is 0'
15
+ # when y == 0
16
+ # puts 'y is 0'
17
+ # else
18
+ # puts 'neither is 0'
19
+ # end
20
+ #
21
+ # # good:
22
+ # if x == 0
23
+ # puts 'x is 0'
24
+ # elsif y == 0
25
+ # puts 'y is 0'
26
+ # else
27
+ # puts 'neither is 0'
28
+ # end
29
+ #
30
+ # # good: (the case condition node is not empty)
31
+ # case n
32
+ # when 0
33
+ # puts 'zero'
34
+ # when 1
35
+ # puts 'one'
36
+ # else
37
+ # puts 'more'
38
+ # end
39
+ class EmptyCaseCondition < Cop
40
+ MSG = 'Do not use empty `case` condition, instead use an `if` '\
41
+ 'expression.'.freeze
42
+
43
+ def on_case(case_node)
44
+ condition_node = case_node.children.first
45
+
46
+ add_offense(case_node, :keyword, MSG) if condition_node.nil?
47
+ end
48
+
49
+ private
50
+
51
+ def autocorrect(case_node)
52
+ lambda do |corrector|
53
+ _cond_node, *when_nodes, _else_node = *case_node
54
+
55
+ correct_case_whens(corrector, case_node, when_nodes)
56
+
57
+ correct_multiple_alternative_whens(corrector, when_nodes)
58
+ end
59
+ end
60
+
61
+ def correct_case_whens(corrector, case_node, when_nodes)
62
+ case_to_first_when =
63
+ case_node.loc.keyword.join(when_nodes.first.loc.keyword)
64
+
65
+ corrector.replace(case_to_first_when, 'if')
66
+
67
+ when_nodes.drop(1).each do |when_node|
68
+ corrector.replace(when_node.loc.keyword, 'elsif')
69
+ end
70
+ end
71
+
72
+ # Since an if condition containing commas is not syntactically valid, we
73
+ # correct `when x, y` to `if x || y`.
74
+ def correct_multiple_alternative_whens(corrector, when_nodes)
75
+ when_nodes.each do |when_node|
76
+ # In `when a; r` we have two children: [a, r].
77
+ # In `when a, b, c; r` we have 4.
78
+ # In `when a, b then r` we have 3.
79
+ *children, _ = when_node.children
80
+
81
+ next unless children.size > 1
82
+
83
+ first = children.first
84
+ last = children.last
85
+ range =
86
+ Parser::Source::Range.new(when_node.loc.expression.source_buffer,
87
+ first.loc.expression.begin_pos,
88
+ last.loc.expression.end_pos)
89
+
90
+ corrector.replace(range, children.map(&:source).join(' || '))
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,13 +5,15 @@ module RuboCop
5
5
  module Cop
6
6
  module Style
7
7
  # This cop checks whether the source file has a utf-8 encoding
8
- # comment or not. This check makes sense only for code that
9
- # should support Ruby 1.9, since in 2.0+ utf-8 is the default
10
- # source file encoding. There are two styles:
8
+ # comment or not.
9
+ # Setting this check to "always" and "when_needed" makes sense only
10
+ # for code that should support Ruby 1.9, since in 2.0+ utf-8 is the
11
+ # default source file encoding. There are three styles:
11
12
  #
12
13
  # when_needed - only enforce an encoding comment if there are non ASCII
13
14
  # characters, otherwise report an offense
14
15
  # always - enforce encoding comment in all files
16
+ # never - enforce no encoding comment in all files
15
17
  class Encoding < Cop
16
18
  include ConfigurableEnforcedStyle
17
19
 
@@ -56,10 +58,12 @@ module RuboCop
56
58
  encoding_present = line =~ ENCODING_PATTERN
57
59
  ascii_only = processed_source.buffer.source.ascii_only?
58
60
  always_encode = style == :always
61
+ never_encode = style == :never
62
+ encoding_omitable = never_encode || (!always_encode && ascii_only)
59
63
 
60
- if !encoding_present && (always_encode || !ascii_only)
64
+ if !encoding_present && !encoding_omitable
61
65
  MSG_MISSING
62
- elsif !always_encode && ascii_only && encoding_present
66
+ elsif encoding_present && encoding_omitable
63
67
  MSG_UNNECESSARY
64
68
  end
65
69
  end
@@ -67,16 +67,31 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def check_assignment(token)
70
- # minus 2 is because pos.line is zero-based
71
- line = processed_source.lines[token.pos.line - 2]
72
- return if aligned_assignment?(token.pos, line)
73
-
74
- preceding = @asgn_lines.include?(token.pos.line - 1)
75
- align_with = preceding ? 'preceding' : 'following'
76
- message = format(MSG_UNALIGNED_ASGN, align_with)
70
+ assignment_line = ''
71
+ message = ''
72
+ if should_aligned_with_preceding_line?(token)
73
+ assignment_line = preceding_line(token)
74
+ message = format(MSG_UNALIGNED_ASGN, 'preceding')
75
+ else
76
+ assignment_line = following_line(token)
77
+ message = format(MSG_UNALIGNED_ASGN, 'following')
78
+ end
79
+ return if aligned_assignment?(token.pos, assignment_line)
77
80
  add_offense(token.pos, token.pos, message)
78
81
  end
79
82
 
83
+ def should_aligned_with_preceding_line?(token)
84
+ @asgn_lines.include?(token.pos.line - 1)
85
+ end
86
+
87
+ def preceding_line(token)
88
+ processed_source.lines[token.pos.line - 2]
89
+ end
90
+
91
+ def following_line(token)
92
+ processed_source.lines[token.pos.line]
93
+ end
94
+
80
95
  def check_other(t1, t2, ast)
81
96
  return if t1.pos.line != t2.pos.line
82
97
  return if t2.pos.begin_pos - 1 <= t1.pos.end_pos
@@ -10,7 +10,8 @@ module RuboCop
10
10
  # names. Ruby scripts (i.e. source files with a shebang in the
11
11
  # first line) are ignored.
12
12
  class FileName < Cop
13
- MSG_SNAKE_CASE = 'Use snake_case for source file names.'.freeze
13
+ MSG_SNAKE_CASE = 'The name of this source file (`%s`) ' \
14
+ 'should use snake_case.'.freeze
14
15
  MSG_NO_DEFINITION = '%s should define a class or module ' \
15
16
  'called `%s`.'.freeze
16
17
  MSG_REGEX = '`%s` should match `%s`.'.freeze
@@ -55,7 +56,11 @@ module RuboCop
55
56
  end
56
57
 
57
58
  def other_message(basename)
58
- regex ? format(MSG_REGEX, basename, regex) : MSG_SNAKE_CASE
59
+ if regex
60
+ format(MSG_REGEX, basename, regex)
61
+ else
62
+ format(MSG_SNAKE_CASE, basename)
63
+ end
59
64
  end
60
65
 
61
66
  def shebang?(line)
@@ -61,15 +61,8 @@ module RuboCop
61
61
  end
62
62
 
63
63
  def on_if(node)
64
- cond, body, else_body = *node
65
-
66
- return unless body && else_body
67
- # discard modifier ifs and ternary_ops
68
- return if modifier_if?(node) || ternary_op?(node) || elsif?(node)
69
-
70
- return unless single_line_control_flow_exit?(body) ||
71
- single_line_control_flow_exit?(else_body)
72
- return if cond.multiline?
64
+ return if accept_form?(node)
65
+ return unless any_single_line_control_flow_exit?(node)
73
66
  return if line_too_long_when_corrected?(node)
74
67
 
75
68
  add_offense(node, :keyword, MSG)
@@ -77,6 +70,21 @@ module RuboCop
77
70
 
78
71
  private
79
72
 
73
+ def accept_form?(node)
74
+ cond, body, else_body = *node
75
+ return true unless body && else_body
76
+ return true if modifier_if?(node) || ternary?(node) || elsif?(node)
77
+
78
+ cond.multiline?
79
+ end
80
+
81
+ def any_single_line_control_flow_exit?(node)
82
+ _cond, body, else_body = *node
83
+
84
+ single_line_control_flow_exit?(body) ||
85
+ single_line_control_flow_exit?(else_body)
86
+ end
87
+
80
88
  def if?(node)
81
89
  node && node.if_type?
82
90
  end
@@ -92,7 +100,7 @@ module RuboCop
92
100
 
93
101
  return if body && else_body
94
102
  # discard modifier ifs and ternary_ops
95
- return if modifier_if?(node) || ternary_op?(node)
103
+ return if modifier_if?(node) || ternary?(node)
96
104
  return if cond.multiline?
97
105
  # discard short ifs
98
106
  return unless min_body_length?(node)
@@ -37,7 +37,7 @@ module RuboCop
37
37
  _cond, _if_branch, else_branch = *node
38
38
  return unless else_branch
39
39
  return unless else_branch.if_type?
40
- return if ternary_op?(node) || ternary_op?(else_branch)
40
+ return if ternary?(node) || ternary?(else_branch)
41
41
  return unless else_branch.loc.keyword.is?('if')
42
42
  return if node.loc.keyword.is?('unless')
43
43
 
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # if written as a modifier if/unless.
9
9
  # The maximum line length is configurable.
10
10
  class IfUnlessModifier < Cop
11
+ include IfNode
11
12
  include StatementModifier
12
13
 
13
14
  ASSIGNMENT_TYPES = [:lvasgn, :casgn, :cvasgn,
@@ -20,7 +21,7 @@ module RuboCop
20
21
 
21
22
  def on_if(node)
22
23
  # discard ternary ops, if/else and modifier if/unless nodes
23
- return if ternary_op?(node)
24
+ return if ternary?(node)
24
25
  return if modifier_if?(node)
25
26
  return if elsif?(node)
26
27
  return if if_else?(node)
@@ -152,7 +152,7 @@ module RuboCop
152
152
 
153
153
  def on_if(node, base = node)
154
154
  return if ignored_node?(node)
155
- return if ternary_op?(node)
155
+ return if ternary?(node)
156
156
  return if modifier_if?(node)
157
157
 
158
158
  _condition, body, else_clause = if_node_parts(node)
@@ -183,7 +183,7 @@ module RuboCop
183
183
  end
184
184
 
185
185
  def check_if(node, body, else_clause, base_loc)
186
- return if ternary_op?(node)
186
+ return if ternary?(node)
187
187
 
188
188
  check_indentation(base_loc, body)
189
189
  return unless else_clause
@@ -202,10 +202,13 @@ module RuboCop
202
202
  @column_delta = configured_indentation_width - indentation
203
203
  return if @column_delta == 0
204
204
 
205
+ offense(body_node, indentation, style)
206
+ end
207
+
208
+ def offense(body_node, indentation, style)
205
209
  # This cop only auto-corrects the first statement in a def body, for
206
210
  # example.
207
- if body_node.type == :begin && !(body_node.loc.begin &&
208
- body_node.loc.begin.is?('('))
211
+ if body_node.type == :begin && !parentheses?(body_node)
209
212
  body_node = body_node.children.first
210
213
  end
211
214
 
@@ -4,15 +4,72 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
7
- # This cop checks for uses of the pre 1.9 lambda syntax for one-line
8
- # anonymous functions and uses of the 1.9 lambda syntax for multi-line
9
- # anonymous functions.
7
+ # This cop (by default) checks for uses of the lambda literal syntax for
8
+ # single line lambdas, and the method call syntax for multiline lambdas.
9
+ # It is configurable to enforce one of the styles for both single line
10
+ # and multiline lambdas as well.
11
+ #
12
+ # @example
13
+ #
14
+ # # EnforcedStyle: line_count_dependent (default)
15
+ #
16
+ # # bad
17
+ # f = lambda { |x| x }
18
+ # f = ->(x) do
19
+ # x
20
+ # end
21
+ #
22
+ # # good
23
+ # f = ->(x) { x }
24
+ # f = lambda do |x|
25
+ # x
26
+ # end
27
+ #
28
+ # @example
29
+ #
30
+ # # EnforcedStyle: lambda
31
+ #
32
+ # # bad
33
+ # f = ->(x) { x }
34
+ # f = ->(x) do
35
+ # x
36
+ # end
37
+ #
38
+ # # good
39
+ # f = lambda { |x| x }
40
+ # f = lambda do |x|
41
+ # x
42
+ # end
43
+ #
44
+ # @example
45
+ #
46
+ # # EnforcedStyle: literal
47
+ #
48
+ # # bad
49
+ # f = lambda { |x| x }
50
+ # f = lambda do |x|
51
+ # x
52
+ # end
53
+ #
54
+ # # good
55
+ # f = ->(x) { x }
56
+ # f = ->(x) do
57
+ # x
58
+ # end
10
59
  class Lambda < Cop
11
- SINGLE_MSG = 'Use the new lambda literal syntax ' \
12
- '`->(params) {...}`.'.freeze
13
- SINGLE_NO_ARG_MSG = 'Use the new lambda literal syntax ' \
14
- '`-> {...}`.'.freeze
15
- MULTI_MSG = 'Use the `lambda` method for multi-line lambdas.'.freeze
60
+ include ConfigurableEnforcedStyle
61
+
62
+ LITERAL_MESSAGE = 'Use the `-> { ... }` lambda literal syntax for ' \
63
+ '%s lambdas.'.freeze
64
+ METHOD_MESSAGE = 'Use the `lambda` method for %s lambdas.'.freeze
65
+
66
+ OFFENDING_SELECTORS = {
67
+ style: {
68
+ lambda: { single_line: '->', multiline: '->' },
69
+ literal: { single_line: 'lambda', multiline: 'lambda' },
70
+ line_count_dependent: { single_line: 'lambda', multiline: '->' }
71
+ }
72
+ }.freeze
16
73
 
17
74
  TARGET = s(:send, nil, :lambda)
18
75
 
@@ -21,34 +78,45 @@ module RuboCop
21
78
  # (block
22
79
  # (send nil :lambda)
23
80
  # ...)
24
- block_method, args, = *node
81
+ block_method, _args, = *node
25
82
 
26
83
  return unless block_method == TARGET
27
- selector = block_method.source
28
- length = lambda_length(node)
29
84
 
30
- if selector != '->' && length == 1
31
- add_offense_for_single_line(node, block_method.source_range, args)
32
- elsif selector == '->' && length > 1
33
- add_offense(node, block_method.source_range, MULTI_MSG)
34
- end
85
+ check(node)
35
86
  end
36
87
 
37
88
  private
38
89
 
39
- def add_offense_for_single_line(block_node, location, args)
40
- if args.children.empty?
41
- add_offense(block_node, location, SINGLE_NO_ARG_MSG)
42
- else
43
- add_offense(block_node, location, SINGLE_MSG)
90
+ def check(node)
91
+ block_method, _args, = *node
92
+
93
+ selector = block_method.source
94
+
95
+ if offending_selector?(node, selector)
96
+ add_offense(node, block_method.source_range,
97
+ message(node, selector))
44
98
  end
45
99
  end
46
100
 
47
- def lambda_length(block_node)
48
- start_line = block_node.loc.begin.line
49
- end_line = block_node.loc.end.line
101
+ def offending_selector?(node, selector)
102
+ lines = node.multiline? ? :multiline : :single_line
50
103
 
51
- end_line - start_line + 1
104
+ selector == OFFENDING_SELECTORS[:style][style][lines]
105
+ end
106
+
107
+ def message(node, selector)
108
+ message = (selector == '->') ? METHOD_MESSAGE : LITERAL_MESSAGE
109
+
110
+ format(message, message_line_modifier(node))
111
+ end
112
+
113
+ def message_line_modifier(node)
114
+ case style
115
+ when :line_count_dependent
116
+ node.multiline? ? 'multiline' : 'single line'
117
+ else
118
+ 'all'
119
+ end
52
120
  end
53
121
 
54
122
  def autocorrect(node)
@@ -59,15 +127,15 @@ module RuboCop
59
127
  return if selector == '->' && arg_to_unparenthesized_call?(node)
60
128
 
61
129
  lambda do |corrector|
62
- if block_method.source == 'lambda'
63
- autocorrect_old_to_new(corrector, node)
130
+ if selector == 'lambda'
131
+ autocorrect_method_to_literal(corrector, node)
64
132
  else
65
- autocorrect_new_to_old(corrector, node)
133
+ autocorrect_literal_to_method(corrector, node)
66
134
  end
67
135
  end
68
136
  end
69
137
 
70
- def autocorrect_new_to_old(corrector, node)
138
+ def autocorrect_literal_to_method(corrector, node)
71
139
  block_method, args = *node
72
140
  # Avoid correcting to `lambdado` by inserting whitespace
73
141
  # if none exists before or after the lambda arguments.
@@ -81,7 +149,7 @@ module RuboCop
81
149
  corrector.insert_after(node.loc.begin, arg_str)
82
150
  end
83
151
 
84
- def autocorrect_old_to_new(corrector, node)
152
+ def autocorrect_method_to_literal(corrector, node)
85
153
  block_method, args = *node
86
154
  corrector.replace(block_method.source_range, '->')
87
155
  return if args.children.empty?