rubocop 0.41.2 → 0.42.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/config/default.yml +13 -0
  4. data/config/disabled.yml +5 -0
  5. data/config/enabled.yml +20 -0
  6. data/lib/rubocop.rb +4 -0
  7. data/lib/rubocop/ast_node.rb +4 -3
  8. data/lib/rubocop/config.rb +1 -1
  9. data/lib/rubocop/config_loader.rb +2 -7
  10. data/lib/rubocop/cop/cop.rb +3 -3
  11. data/lib/rubocop/cop/lint/block_alignment.rb +18 -16
  12. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +34 -19
  13. data/lib/rubocop/cop/lint/next_without_accumulator.rb +9 -1
  14. data/lib/rubocop/cop/lint/shadowed_exception.rb +23 -3
  15. data/lib/rubocop/cop/lint/unneeded_disable.rb +1 -1
  16. data/lib/rubocop/cop/lint/useless_access_modifier.rb +41 -4
  17. data/lib/rubocop/cop/mixin/array_hash_indentation.rb +1 -1
  18. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  19. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +3 -5
  20. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +4 -4
  21. data/lib/rubocop/cop/mixin/space_inside.rb +23 -8
  22. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  23. data/lib/rubocop/cop/offense.rb +33 -10
  24. data/lib/rubocop/cop/performance/case_when_splat.rb +16 -14
  25. data/lib/rubocop/cop/performance/sample.rb +0 -1
  26. data/lib/rubocop/cop/rails/save_bang.rb +77 -0
  27. data/lib/rubocop/cop/rails/validation.rb +15 -15
  28. data/lib/rubocop/cop/style/access_modifier_indentation.rb +1 -1
  29. data/lib/rubocop/cop/style/align_hash.rb +1 -1
  30. data/lib/rubocop/cop/style/block_comments.rb +1 -3
  31. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +19 -10
  32. data/lib/rubocop/cop/style/closing_parenthesis_indentation.rb +1 -1
  33. data/lib/rubocop/cop/style/comment_annotation.rb +14 -14
  34. data/lib/rubocop/cop/style/comment_indentation.rb +1 -1
  35. data/lib/rubocop/cop/style/conditional_assignment.rb +25 -26
  36. data/lib/rubocop/cop/style/dot_position.rb +24 -19
  37. data/lib/rubocop/cop/style/each_for_simple_loop.rb +22 -8
  38. data/lib/rubocop/cop/style/each_with_object.rb +11 -1
  39. data/lib/rubocop/cop/style/else_alignment.rb +1 -1
  40. data/lib/rubocop/cop/style/empty_lines.rb +22 -11
  41. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +9 -6
  42. data/lib/rubocop/cop/style/empty_lines_around_block_body.rb +14 -14
  43. data/lib/rubocop/cop/style/empty_lines_around_class_body.rb +8 -5
  44. data/lib/rubocop/cop/style/empty_lines_around_method_body.rb +12 -8
  45. data/lib/rubocop/cop/style/empty_lines_around_module_body.rb +17 -4
  46. data/lib/rubocop/cop/style/empty_literal.rb +12 -7
  47. data/lib/rubocop/cop/style/for.rb +1 -1
  48. data/lib/rubocop/cop/style/indent_array.rb +1 -1
  49. data/lib/rubocop/cop/style/indent_hash.rb +1 -1
  50. data/lib/rubocop/cop/style/indentation_width.rb +1 -1
  51. data/lib/rubocop/cop/style/initial_indentation.rb +1 -1
  52. data/lib/rubocop/cop/style/lambda.rb +2 -3
  53. data/lib/rubocop/cop/style/method_call_parentheses.rb +6 -4
  54. data/lib/rubocop/cop/style/method_missing.rb +74 -0
  55. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +1 -1
  56. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +1 -1
  57. data/lib/rubocop/cop/style/nested_modifier.rb +23 -13
  58. data/lib/rubocop/cop/style/next.rb +1 -1
  59. data/lib/rubocop/cop/style/numeric_predicate.rb +142 -0
  60. data/lib/rubocop/cop/style/op_method.rb +12 -4
  61. data/lib/rubocop/cop/style/parallel_assignment.rb +11 -3
  62. data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +9 -6
  63. data/lib/rubocop/cop/style/single_line_block_params.rb +3 -2
  64. data/lib/rubocop/cop/style/space_inside_block_braces.rb +1 -1
  65. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +1 -1
  66. data/lib/rubocop/cop/style/symbol_array.rb +18 -10
  67. data/lib/rubocop/cop/style/ternary_parentheses.rb +94 -0
  68. data/lib/rubocop/cop/style/trailing_blank_lines.rb +1 -1
  69. data/lib/rubocop/cop/style/unneeded_percent_q.rb +12 -6
  70. data/lib/rubocop/cop/util.rb +1 -1
  71. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  72. data/lib/rubocop/cop/variable_force/locatable.rb +1 -1
  73. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -7
  74. data/lib/rubocop/formatter/html_formatter.rb +25 -11
  75. data/lib/rubocop/formatter/text_util.rb +1 -1
  76. data/lib/rubocop/node_pattern.rb +2 -0
  77. data/lib/rubocop/processed_source.rb +3 -0
  78. data/lib/rubocop/rake_task.rb +1 -1
  79. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  80. data/lib/rubocop/runner.rb +18 -7
  81. data/lib/rubocop/string_util.rb +2 -5
  82. data/lib/rubocop/version.rb +1 -1
  83. metadata +7 -3
@@ -4,31 +4,35 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
7
- # This cops checks if empty lines around the bodies of methods match
8
- # the configuration.
7
+ # This cops checks if empty lines exist around the bodies of methods.
9
8
  #
10
9
  # @example
11
10
  #
12
- # def something(arg)
11
+ # # good
13
12
  #
13
+ # def foo
14
14
  # ...
15
15
  # end
16
16
  #
17
+ # # bad
18
+ #
19
+ # def bar
20
+ #
21
+ # ...
22
+ #
23
+ # end
17
24
  class EmptyLinesAroundMethodBody < Cop
18
25
  include EmptyLinesAroundBody
19
26
  include OnMethodDef
20
27
 
21
28
  KIND = 'method'.freeze
22
29
 
23
- private
24
-
25
30
  def on_method_def(node, _method_name, _args, body)
26
31
  check(node, body)
27
32
  end
28
33
 
29
- # Override ConfigurableEnforcedStyle#style and hard-code
30
- # configuration. It's difficult to imagine that anybody would want
31
- # empty lines around a method body, so we don't make it configurable.
34
+ private
35
+
32
36
  def style
33
37
  :no_empty_lines
34
38
  end
@@ -9,14 +9,27 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  #
12
- # module Test
12
+ # EnforcedStyle: empty_lines
13
13
  #
14
- # def something
15
- # ...
16
- # end
14
+ # # good
15
+ #
16
+ # module Foo
17
+ #
18
+ # def bar
19
+ # ...
20
+ # end
17
21
  #
18
22
  # end
19
23
  #
24
+ # EnforcedStyle: no_empty_lines
25
+ #
26
+ # # good
27
+ #
28
+ # module Foo
29
+ # def bar
30
+ # ...
31
+ # end
32
+ # end
20
33
  class EmptyLinesAroundModuleBody < Cop
21
34
  include EmptyLinesAroundBody
22
35
 
@@ -7,9 +7,11 @@ module RuboCop
7
7
  # This cop checks for the use of a method, the result of which
8
8
  # would be a literal, like an empty array, hash or string.
9
9
  class EmptyLiteral < Cop
10
+ include FrozenStringLiteral
11
+
10
12
  ARR_MSG = 'Use array literal `[]` instead of `Array.new`.'.freeze
11
13
  HASH_MSG = 'Use hash literal `{}` instead of `Hash.new`.'.freeze
12
- STR_MSG = "Use string literal `''` instead of `String.new`.".freeze
14
+ STR_MSG = 'Use string literal `%s` instead of `String.new`.'.freeze
13
15
 
14
16
  # Empty array node
15
17
  #
@@ -39,7 +41,10 @@ module RuboCop
39
41
 
40
42
  add_offense(node, :expression, HASH_MSG)
41
43
  when STR_NODE
42
- add_offense(node, :expression, STR_MSG)
44
+ return if frozen_string_literals_enabled?(processed_source)
45
+
46
+ add_offense(node, :expression,
47
+ format(STR_MSG, preferred_string_literal))
43
48
  end
44
49
  end
45
50
 
@@ -55,17 +60,17 @@ module RuboCop
55
60
  return if first_arg_in_method_call_without_parentheses?(node)
56
61
  '{}'
57
62
  when STR_NODE
58
- if enforce_double_quotes?
59
- '""'
60
- else
61
- "''"
62
- end
63
+ preferred_string_literal
63
64
  end
64
65
  ->(corrector) { corrector.replace(node.source_range, name) }
65
66
  end
66
67
 
67
68
  private
68
69
 
70
+ def preferred_string_literal
71
+ enforce_double_quotes? ? '""' : "''"
72
+ end
73
+
69
74
  def enforce_double_quotes?
70
75
  string_literals_config['EnforcedStyle'] == 'double_quotes'
71
76
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  end
24
24
 
25
25
  def on_block(node)
26
- return if block_length(node) == 0
26
+ return if block_length(node).zero?
27
27
 
28
28
  method, _args, _body = *node
29
29
  return unless method.type == :send
@@ -80,7 +80,7 @@ module RuboCop
80
80
 
81
81
  expected_column = base_column(left_bracket, left_parenthesis)
82
82
  @column_delta = expected_column - right_bracket.column
83
- return if @column_delta == 0
83
+ return if @column_delta.zero?
84
84
 
85
85
  msg = if style == :align_brackets
86
86
  'Indent the right bracket the same as the left bracket.'
@@ -85,7 +85,7 @@ module RuboCop
85
85
 
86
86
  expected_column = base_column(left_brace, left_parenthesis)
87
87
  @column_delta = expected_column - right_brace.column
88
- return if @column_delta == 0
88
+ return if @column_delta.zero?
89
89
 
90
90
  msg = if style == :align_braces
91
91
  'Indent the right brace the same as the left brace.'
@@ -200,7 +200,7 @@ module RuboCop
200
200
 
201
201
  indentation = body_node.loc.column - effective_column(base_loc)
202
202
  @column_delta = configured_indentation_width - indentation
203
- return if @column_delta == 0
203
+ return if @column_delta.zero?
204
204
 
205
205
  offense(body_node, indentation, style)
206
206
  end
@@ -14,7 +14,7 @@ module RuboCop
14
14
  !t.text.start_with?('#')
15
15
  end
16
16
  return unless first_token
17
- return if first_token.pos.column == 0
17
+ return if first_token.pos.column.zero?
18
18
 
19
19
  with_space = range_with_surrounding_space(first_token.pos, :left,
20
20
  !:with_newline)
@@ -105,7 +105,7 @@ module RuboCop
105
105
  end
106
106
 
107
107
  def message(node, selector)
108
- message = (selector == '->') ? METHOD_MESSAGE : LITERAL_MESSAGE
108
+ message = selector == '->' ? METHOD_MESSAGE : LITERAL_MESSAGE
109
109
 
110
110
  format(message, message_line_modifier(node))
111
111
  end
@@ -182,8 +182,7 @@ module RuboCop
182
182
  return false unless parent && parent.send_type?
183
183
  return false if parenthesized_call?(parent)
184
184
 
185
- index = parent.children.index { |c| c.equal?(node) }
186
- index >= 2
185
+ node.sibling_index > 1
187
186
  end
188
187
 
189
188
  def remove_unparenthesized_whitespaces(corrector, node)
@@ -38,10 +38,12 @@ module RuboCop
38
38
 
39
39
  node.each_ancestor(*ASGN_NODES).any? do |asgn_node|
40
40
  # `obj.method = value` parses as (send ... :method= ...), and will
41
- # not be returned as an `asgn_node` here
42
- # however, `obj.method ||= value` parses as (or-asgn (send ...) ...)
43
- # which IS an `asgn_node`
44
- if asgn_node.or_asgn_type? || asgn_node.and_asgn_type?
41
+ # not be returned as an `asgn_node` here, however,
42
+ # `obj.method ||= value` parses as (or-asgn (send ...) ...)
43
+ # which IS an `asgn_node`. Similarly, `obj.method += value` parses
44
+ # as (op-asgn (send ...) ...), which is also an `asgn_node`.
45
+ if asgn_node.or_asgn_type? || asgn_node.and_asgn_type? ||
46
+ asgn_node.op_asgn_type?
45
47
  asgn_node, _value = *asgn_node
46
48
  return false if asgn_node.send_type?
47
49
  end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Style
7
+ # This cop checks for the presence of `method_missing` without also
8
+ # defining `respond_to_missing?` and falling back on `super`.
9
+ #
10
+ # @example
11
+ # #bad
12
+ # def method_missing(...)
13
+ # ...
14
+ # end
15
+ #
16
+ # #good
17
+ # def respond_to_missing?(...)
18
+ # ...
19
+ # end
20
+ #
21
+ # def method_missing(...)
22
+ # ...
23
+ # super
24
+ # end
25
+ class MethodMissing < Cop
26
+ include OnMethodDef
27
+
28
+ MSG = 'When using `method_missing`, %s.'.freeze
29
+
30
+ def on_method_def(node, method_name, _args, _body)
31
+ return unless method_name == :method_missing
32
+
33
+ check(node)
34
+ end
35
+
36
+ private
37
+
38
+ def check(node)
39
+ return if calls_super?(node) && implements_respond_to_missing?(node)
40
+
41
+ add_offense(node, :expression)
42
+ end
43
+
44
+ def message(node)
45
+ instructions = []
46
+
47
+ unless implements_respond_to_missing?(node)
48
+ instructions << 'define `respond_to_missing?`'.freeze
49
+ end
50
+
51
+ unless calls_super?(node)
52
+ instructions << 'fall back on `super`'.freeze
53
+ end
54
+
55
+ format(MSG, instructions.join(' and '))
56
+ end
57
+
58
+ def calls_super?(node)
59
+ node.descendants.any?(&:zsuper_type?)
60
+ end
61
+
62
+ def implements_respond_to_missing?(node)
63
+ node.parent.children.any? do |sibling|
64
+ respond_to_missing_def?(sibling)
65
+ end
66
+ end
67
+
68
+ def_node_matcher :respond_to_missing_def?, <<-PATTERN
69
+ (def :respond_to_missing? (...) ...)
70
+ PATTERN
71
+ end
72
+ end
73
+ end
74
+ end
@@ -62,7 +62,7 @@ module RuboCop
62
62
  indentation(lhs) + correct_indentation(node)
63
63
  end
64
64
  @column_delta = correct_column - rhs.column
65
- rhs if @column_delta != 0
65
+ rhs if @column_delta.nonzero?
66
66
  end
67
67
 
68
68
  def extra_indentation(given_style)
@@ -58,7 +58,7 @@ module RuboCop
58
58
  indentation(lhs) + correct_indentation(node)
59
59
  end
60
60
  @column_delta = correct_column - rhs.column
61
- rhs if @column_delta != 0
61
+ rhs if @column_delta.nonzero?
62
62
  end
63
63
 
64
64
  def should_align?(node, rhs, given_style)
@@ -77,24 +77,34 @@ module RuboCop
77
77
  end
78
78
 
79
79
  def new_expression(outer_node, inner_node)
80
- outer_cond, = *outer_node
81
- inner_cond, = *inner_node
82
-
83
80
  outer_keyword = outer_node.loc.keyword.source
84
- inner_keyword = inner_node.loc.keyword.source
81
+ operator = replacement_operator(outer_keyword)
82
+ lh_operand = left_hand_operand(outer_node, operator)
83
+ rh_operand = right_hand_operand(inner_node, outer_keyword)
85
84
 
86
- operator = outer_keyword == 'if'.freeze ? '&&'.freeze : '||'.freeze
85
+ "#{outer_keyword} #{lh_operand} #{operator} #{rh_operand}"
86
+ end
87
87
 
88
- outer_expr = outer_cond.source
89
- outer_expr = "(#{outer_expr})" if outer_cond.or_type? &&
90
- operator == '&&'.freeze
91
- inner_expr = inner_cond.source
88
+ def replacement_operator(keyword)
89
+ keyword == 'if'.freeze ? '&&'.freeze : '||'.freeze
90
+ end
91
+
92
+ def left_hand_operand(node, operator)
93
+ cond, = *node
94
+
95
+ expr = cond.source
96
+ expr = "(#{expr})" if cond.or_type? && operator == '&&'.freeze
97
+ expr
98
+ end
92
99
 
93
- inner_expr = "(#{inner_expr})" if requires_parens?(inner_cond)
94
- inner_expr = "!#{inner_expr}" unless outer_keyword == inner_keyword
100
+ def right_hand_operand(node, left_hand_keyword)
101
+ cond, = *node
102
+ keyword = node.loc.keyword.source
95
103
 
96
- "#{outer_node.loc.keyword.source} " \
97
- "#{outer_expr} #{operator} #{inner_expr}"
104
+ expr = cond.source
105
+ expr = "(#{expr})" if requires_parens?(cond)
106
+ expr = "!#{expr}" unless left_hand_keyword == keyword
107
+ expr
98
108
  end
99
109
 
100
110
  def requires_parens?(node)
@@ -101,7 +101,7 @@ module RuboCop
101
101
 
102
102
  def offense_node(body)
103
103
  *_, condition = *body
104
- (condition && condition.if_type?) ? condition : body
104
+ condition && condition.if_type? ? condition : body
105
105
  end
106
106
 
107
107
  def offense_location(offense_node)
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Style
7
+ # This cop checks for using of comparison operators (`==`, `!=`,
8
+ # `>`, `<`), to test numbers as zero, nonzero, positive, or negative.
9
+ # These can be replaced by their respective predicate methods.
10
+ # The cop can also be configured to do the reverse.
11
+ #
12
+ # @example
13
+ #
14
+ # # EnforcedStyle: predicate (default)
15
+ #
16
+ # # bad
17
+ #
18
+ # foo == 0
19
+ # 0 != bar.baz
20
+ # 0 > foo
21
+ # bar.baz > 0
22
+ #
23
+ # # good
24
+ #
25
+ # foo.zero?
26
+ # bar.baz.nonzero?
27
+ # foo.negative?
28
+ # bar.baz.positive?
29
+ #
30
+ # @example
31
+ #
32
+ # # EnforcedStyle: comparison
33
+ #
34
+ # # bad
35
+ #
36
+ # foo.zero?
37
+ # bar.baz.nonzero?
38
+ # foo.negative?
39
+ # bar.baz.positive?
40
+ #
41
+ # # good
42
+ #
43
+ # foo == 0
44
+ # 0 != bar.baz
45
+ # 0 > foo
46
+ # bar.baz > 0
47
+ class NumericPredicate < Cop
48
+ include ConfigurableEnforcedStyle
49
+
50
+ MSG = 'Use `%s` instead of `%s`.'.freeze
51
+
52
+ REPLACEMENTS = {
53
+ 'zero?' => '==',
54
+ 'nonzero?' => '!=',
55
+ 'positive?' => '>',
56
+ 'negative?' => '<'
57
+ }.freeze
58
+
59
+ def on_send(node)
60
+ numeric, replacement = check(node)
61
+
62
+ if numeric
63
+ add_offense(node, node.loc.expression,
64
+ format(MSG, replacement, node.source))
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def check(node)
71
+ numeric, operator =
72
+ if style == :predicate
73
+ comparison(node) || inverted_comparison(node, &invert)
74
+ else
75
+ predicate(node)
76
+ end
77
+
78
+ if numeric && operator && replacement_supported?(operator)
79
+ return numeric, replacement(numeric, operator)
80
+ end
81
+ end
82
+
83
+ def autocorrect(node)
84
+ _, replacement = check(node)
85
+
86
+ lambda do |corrector|
87
+ corrector.replace(node.loc.expression, replacement)
88
+ end
89
+ end
90
+
91
+ def replacement(numeric, operation)
92
+ if style == :predicate
93
+ [parenthesized_source(numeric),
94
+ REPLACEMENTS.invert[operation.to_s]].join('.')
95
+ else
96
+ [numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
97
+ end
98
+ end
99
+
100
+ def parenthesized_source(node)
101
+ if require_parentheses?(node)
102
+ "(#{node.source})"
103
+ else
104
+ node.source
105
+ end
106
+ end
107
+
108
+ def require_parentheses?(node)
109
+ node.binary_operation? && node.source !~ /^\(.*\)$/
110
+ end
111
+
112
+ def replacement_supported?(operator)
113
+ if [:>, :<].include?(operator)
114
+ target_ruby_version >= 2.3
115
+ else
116
+ true
117
+ end
118
+ end
119
+
120
+ def invert
121
+ lambda do |comparison, numeric|
122
+ comparison = { :> => :<, :< => :> }[comparison] || comparison
123
+
124
+ [numeric, comparison]
125
+ end
126
+ end
127
+
128
+ def_node_matcher :predicate, <<-PATTERN
129
+ (send $(...) ${:zero? :nonzero? :positive? :negative?})
130
+ PATTERN
131
+
132
+ def_node_matcher :comparison, <<-PATTERN
133
+ (send $(...) ${:== :!= :> :<} (int 0))
134
+ PATTERN
135
+
136
+ def_node_matcher :inverted_comparison, <<-PATTERN
137
+ (send (int 0) ${:== :!= :> :<} $(...))
138
+ PATTERN
139
+ end
140
+ end
141
+ end
142
+ end