rubocop 0.33.0 → 0.34.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -1
  3. data/README.md +65 -6
  4. data/config/default.yml +30 -0
  5. data/config/disabled.yml +5 -0
  6. data/config/enabled.yml +19 -3
  7. data/lib/rubocop.rb +7 -2
  8. data/lib/rubocop/cli.rb +1 -1
  9. data/lib/rubocop/config.rb +11 -6
  10. data/lib/rubocop/config_loader.rb +7 -3
  11. data/lib/rubocop/cop/commissioner.rb +6 -11
  12. data/lib/rubocop/cop/cop.rb +7 -3
  13. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -8
  14. data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
  15. data/lib/rubocop/cop/lint/end_alignment.rb +6 -6
  16. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +57 -14
  17. data/lib/rubocop/cop/metrics/method_length.rb +1 -3
  18. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
  19. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +11 -1
  20. data/lib/rubocop/cop/mixin/configurable_naming.rb +14 -3
  21. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -3
  22. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +9 -0
  23. data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
  24. data/lib/rubocop/cop/mixin/safe_assignment.rb +2 -2
  25. data/lib/rubocop/cop/performance/case_when_splat.rb +132 -0
  26. data/lib/rubocop/cop/performance/string_replacement.rb +45 -28
  27. data/lib/rubocop/cop/rails/action_filter.rb +31 -5
  28. data/lib/rubocop/cop/rails/date.rb +6 -5
  29. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  30. data/lib/rubocop/cop/rails/time_zone.rb +12 -1
  31. data/lib/rubocop/cop/style/alias.rb +1 -0
  32. data/lib/rubocop/cop/style/block_delimiters.rb +6 -5
  33. data/lib/rubocop/cop/style/collection_methods.rb +2 -20
  34. data/lib/rubocop/cop/style/else_alignment.rb +2 -1
  35. data/lib/rubocop/cop/style/empty_line_between_defs.rb +42 -13
  36. data/lib/rubocop/cop/style/extra_spacing.rb +17 -3
  37. data/lib/rubocop/cop/style/first_parameter_indentation.rb +1 -1
  38. data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
  39. data/lib/rubocop/cop/style/indentation_width.rb +7 -1
  40. data/lib/rubocop/cop/style/initial_indentation.rb +5 -0
  41. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +1 -1
  42. data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
  43. data/lib/rubocop/cop/style/next.rb +31 -14
  44. data/lib/rubocop/cop/style/option_hash.rb +9 -1
  45. data/lib/rubocop/cop/style/parallel_assignment.rb +21 -44
  46. data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
  47. data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +2 -1
  48. data/lib/rubocop/cop/style/rescue_modifier.rb +35 -3
  49. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +1 -0
  50. data/lib/rubocop/cop/style/string_methods.rb +32 -0
  51. data/lib/rubocop/cop/style/symbol_proc.rb +54 -12
  52. data/lib/rubocop/cop/style/trailing_blank_lines.rb +1 -1
  53. data/lib/rubocop/cop/team.rb +2 -2
  54. data/lib/rubocop/cop/util.rb +2 -2
  55. data/lib/rubocop/cop/variable_force.rb +10 -10
  56. data/lib/rubocop/cop/variable_force/locatable.rb +5 -5
  57. data/lib/rubocop/formatter/formatter_set.rb +1 -0
  58. data/lib/rubocop/options.rb +24 -1
  59. data/lib/rubocop/result_cache.rb +121 -0
  60. data/lib/rubocop/runner.rb +55 -15
  61. data/lib/rubocop/target_finder.rb +1 -1
  62. data/lib/rubocop/version.rb +1 -1
  63. data/relnotes/v0.34.0.md +182 -0
  64. data/rubocop.gemspec +1 -1
  65. metadata +12 -4
@@ -6,23 +6,46 @@ module RuboCop
6
6
  # This cop checks whether method definitions are
7
7
  # separated by empty lines.
8
8
  class EmptyLineBetweenDefs < Cop
9
- MSG = 'Use empty lines between defs.'
9
+ include OnMethodDef
10
+ MSG = 'Use empty lines between method definitions.'
10
11
 
11
- def on_def(node)
12
- if @prev_def_end && (def_start(node) - @prev_def_end) == 1
13
- unless @prev_was_single_line && singe_line_def?(node) &&
14
- cop_config['AllowAdjacentOneLineDefs']
15
- add_offense(node, :keyword)
16
- end
17
- end
12
+ def on_method_def(node, _method_name, _args, _body)
13
+ return unless node.parent && node.parent.begin_type?
18
14
 
19
- @prev_def_end = def_end(node)
20
- @prev_was_single_line = singe_line_def?(node)
15
+ nodes = [prev_node(node), node]
16
+
17
+ return unless nodes.all?(&method(:def_node?))
18
+ return if blank_lines_between?(*nodes)
19
+ return if nodes.all?(&method(:single_line_def?)) &&
20
+ cop_config['AllowAdjacentOneLineDefs']
21
+
22
+ add_offense(node, :keyword)
21
23
  end
22
24
 
23
25
  private
24
26
 
25
- def singe_line_def?(node)
27
+ def def_node?(node)
28
+ return unless node
29
+ node.def_type? || node.defs_type?
30
+ end
31
+
32
+ def blank_lines_between?(first_def_node, second_def_node)
33
+ lines_between_defs(first_def_node, second_def_node).any?(&:blank?)
34
+ end
35
+
36
+ def prev_node(node)
37
+ return nil unless node.sibling_index > 0
38
+
39
+ node.parent.children[node.sibling_index - 1]
40
+ end
41
+
42
+ def lines_between_defs(first_def_node, second_def_node)
43
+ line_range = def_end(first_def_node)..(def_start(second_def_node) - 2)
44
+
45
+ processed_source.lines[line_range]
46
+ end
47
+
48
+ def single_line_def?(node)
26
49
  def_start(node) == def_end(node)
27
50
  end
28
51
 
@@ -35,8 +58,14 @@ module RuboCop
35
58
  end
36
59
 
37
60
  def autocorrect(node)
38
- range = range_with_surrounding_space(node.loc.expression, :left)
39
- ->(corrector) { corrector.insert_before(range, "\n") }
61
+ prev_def = prev_node(node)
62
+ end_pos = prev_def.loc.end.end_pos
63
+ source_buffer = prev_def.loc.end.source_buffer
64
+ newline_pos = source_buffer.source.index("\n", end_pos)
65
+ newline = Parser::Source::Range.new(source_buffer,
66
+ newline_pos,
67
+ newline_pos + 1)
68
+ ->(corrector) { corrector.insert_after(newline, "\n") }
40
69
  end
41
70
  end
42
71
  end
@@ -49,7 +49,13 @@ module RuboCop
49
49
 
50
50
  pre = (token.pos.line - 2).downto(0)
51
51
  post = token.pos.line.upto(processed_source.lines.size - 1)
52
- aligned_with?(pre, token) || aligned_with?(post, token)
52
+ return true if aligned_with?(pre, token) || aligned_with?(post, token)
53
+
54
+ # If no aligned token was found, search for an aligned token on the
55
+ # nearest line with the same indentation as the checked line.
56
+ base_indentation = processed_source.lines[token.pos.line - 1] =~ /\S/
57
+ aligned_with?(pre, token, base_indentation) ||
58
+ aligned_with?(post, token, base_indentation)
53
59
  end
54
60
 
55
61
  def aligned_comments?(token)
@@ -72,11 +78,19 @@ module RuboCop
72
78
  processed_source.comments[ix].loc.column
73
79
  end
74
80
 
75
- def aligned_with?(indices_to_check, token)
81
+ # Returns true if the previous or next line, not counting empty or
82
+ # comment lines, contains a token that's aligned with the given
83
+ # token. If base_indentation is given, lines with different indentation
84
+ # than the base indentation are also skipped.
85
+ def aligned_with?(indices_to_check, token, base_indentation = nil)
76
86
  indices_to_check.each do |ix|
77
87
  next if comment_lines.include?(ix + 1)
78
88
  line = processed_source.lines[ix]
79
89
  next if line.strip.empty?
90
+ if base_indentation
91
+ indentation = line =~ /\S/
92
+ next if indentation != base_indentation
93
+ end
80
94
  return (aligned_words?(token, line) ||
81
95
  aligned_assignments?(token, line) ||
82
96
  aligned_same_character?(token, line))
@@ -104,7 +118,7 @@ module RuboCop
104
118
  end
105
119
 
106
120
  def aligned_same_character?(token, line)
107
- line[token.pos.column] == token.text[0]
121
+ line[token.pos.column] == token.text.to_s[0]
108
122
  end
109
123
  end
110
124
  end
@@ -99,7 +99,7 @@ module RuboCop
99
99
  .map { |c| c.loc.line }
100
100
 
101
101
  line = ''
102
- while line =~ /^\s*$/ || @comment_lines.include?(line_number)
102
+ while line.blank? || @comment_lines.include?(line_number)
103
103
  line_number -= 1
104
104
  line = processed_source.lines[line_number - 1]
105
105
  end
@@ -101,6 +101,11 @@ module RuboCop
101
101
 
102
102
  def valid_19_syntax_symbol?(sym_name)
103
103
  sym_name.sub!(/\A:/, '')
104
+
105
+ # Most hash keys can be matched against a simple regex.
106
+ return true if sym_name =~ /\A[_a-z]\w*[?!]?\z/i
107
+
108
+ # For more complicated hash keys, let the Parser validate the syntax.
104
109
  RuboCop::ProcessedSource.new("{ #{sym_name}: :foo }").valid_syntax?
105
110
  end
106
111
 
@@ -13,6 +13,7 @@ module RuboCop
13
13
  # end
14
14
  # end
15
15
  class IndentationWidth < Cop # rubocop:disable Metrics/ClassLength
16
+ include EndKeywordAlignment
16
17
  include AutocorrectAlignment
17
18
  include OnMethodDef
18
19
  include CheckAssignment
@@ -174,7 +175,7 @@ module RuboCop
174
175
 
175
176
  end_config = config.for_cop('Lint/EndAlignment')
176
177
  style = end_config['Enabled'] ? end_config['AlignWith'] : 'keyword'
177
- base = style == 'variable' ? node : rhs
178
+ base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs
178
179
 
179
180
  case rhs.type
180
181
  when :if then on_if(rhs, base)
@@ -255,6 +256,11 @@ module RuboCop
255
256
  first_char_pos_on_line = body_node.loc.expression.source_line =~ /\S/
256
257
  return false unless body_node.loc.column == first_char_pos_on_line
257
258
 
259
+ if [:rescue, :ensure].include?(body_node.type)
260
+ block_body, *_ = *body_node
261
+ return unless block_body
262
+ end
263
+
258
264
  true
259
265
  end
260
266
 
@@ -17,6 +17,11 @@ module RuboCop
17
17
 
18
18
  with_space = range_with_surrounding_space(first_token.pos, :left,
19
19
  nil, !:with_newline)
20
+ # If the file starts with a byte order mark (BOM), the column can be
21
+ # non-zero, but then we find out here if there's no space to the left
22
+ # of the first token.
23
+ return if with_space == first_token.pos
24
+
20
25
  space = Parser::Source::Range.new(processed_source.buffer,
21
26
  with_space.begin_pos,
22
27
  first_token.pos.begin_pos)
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # b
13
13
  # something
14
14
  # end
15
- class MultilineOperationIndentation < Cop # rubocop:disable ClassLength
15
+ class MultilineOperationIndentation < Cop
16
16
  include ConfigurableEnforcedStyle
17
17
  include AutocorrectAlignment
18
18
 
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks whether some constant value isn't a
7
+ # mutable literal (e.g. array or hash).
8
+ #
9
+ # @example
10
+ # # bad
11
+ # CONST = [1, 2, 3]
12
+ #
13
+ # # good
14
+ # CONST = [1, 2, 3].freeze
15
+ class MutableConstant < Cop
16
+ MSG = 'Freeze mutable objects assigned to constants.'
17
+
18
+ MUTABLE_TYPES = [:array, :hash, :str, :dstr].freeze
19
+
20
+ def on_casgn(node)
21
+ _scope, _const_name, value = *node
22
+
23
+ return if value && !MUTABLE_TYPES.include?(value.type)
24
+
25
+ add_offense(value, :expression)
26
+ end
27
+
28
+ def autocorrect(node)
29
+ expr = node.loc.expression
30
+ ->(corrector) { corrector.replace(expr, "#{expr.source}.freeze") }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -23,28 +23,32 @@ module RuboCop
23
23
  include ConfigurableEnforcedStyle
24
24
  include MinBodyLength
25
25
 
26
- MSG = 'Use `next` to skip iteration.'
27
- ENUMERATORS = [:collect, :detect, :downto, :each, :find, :find_all,
28
- :inject, :loop, :map!, :map, :reduce, :reverse_each,
29
- :select, :times, :upto]
26
+ MSG = 'Use `next` to skip iteration.'.freeze
27
+ EXIT_TYPES = [:break, :return].freeze
28
+ EACH_ = 'each_'.freeze
29
+ ENUMERATORS = [:collect, :collect_concat, :detect, :downto, :each,
30
+ :find, :find_all, :find_index, :inject, :loop, :map!,
31
+ :map, :reduce, :reject, :reject!, :reverse_each, :select,
32
+ :select!, :times, :upto].freeze
30
33
 
31
34
  def on_block(node)
32
35
  block_owner, _, body = *node
33
- return unless block_owner.type == :send
34
- return if body.nil?
36
+ return unless block_owner.send_type?
37
+ return unless body && ends_with_condition?(body)
35
38
 
36
39
  _, method_name = *block_owner
37
40
  return unless enumerator?(method_name)
38
- return unless ends_with_condition?(body)
39
41
 
40
- add_offense(block_owner, :selector, MSG)
42
+ offense_node = offense_node(body)
43
+ add_offense(offense_node, offense_location(offense_node), MSG)
41
44
  end
42
45
 
43
46
  def on_while(node)
44
47
  _, body = *node
45
48
  return unless body && ends_with_condition?(body)
46
49
 
47
- add_offense(node, :keyword, MSG)
50
+ offense_node = offense_node(body)
51
+ add_offense(offense_node, offense_location(offense_node), MSG)
48
52
  end
49
53
  alias_method :on_until, :on_while
50
54
 
@@ -52,25 +56,27 @@ module RuboCop
52
56
  _, _, body = *node
53
57
  return unless body && ends_with_condition?(body)
54
58
 
55
- add_offense(node, :keyword, MSG)
59
+ offense_node = offense_node(body)
60
+ add_offense(offense_node, offense_location(offense_node), MSG)
56
61
  end
57
62
 
58
63
  private
59
64
 
60
65
  def enumerator?(method_name)
61
- ENUMERATORS.include?(method_name) || /\Aeach_/.match(method_name)
66
+ ENUMERATORS.include?(method_name) ||
67
+ method_name.to_s.start_with?(EACH_)
62
68
  end
63
69
 
64
70
  def ends_with_condition?(body)
65
71
  return true if simple_if_without_break?(body)
66
72
 
67
- body.type == :begin && simple_if_without_break?(body.children.last)
73
+ body.begin_type? && simple_if_without_break?(body.children.last)
68
74
  end
69
75
 
70
76
  def simple_if_without_break?(node)
77
+ return false unless node.if_type?
71
78
  return false if ternary_op?(node)
72
79
  return false if if_else?(node)
73
- return false unless node.type == :if
74
80
  return false if style == :skip_modifier_ifs && modifier_if?(node)
75
81
  return false if !modifier_if?(node) && !min_body_length?(node)
76
82
 
@@ -79,7 +85,18 @@ module RuboCop
79
85
  _conditional, if_body, _else_body = *node
80
86
  return true unless if_body
81
87
 
82
- ![:break, :return].include?(if_body.type)
88
+ !EXIT_TYPES.include?(if_body.type)
89
+ end
90
+
91
+ def offense_node(body)
92
+ *_, condition = *body
93
+ (condition && condition.if_type?) ? condition : body
94
+ end
95
+
96
+ def offense_location(offense_node)
97
+ condition_expression, = *offense_node
98
+ offense_begin_pos = offense_node.loc.expression.begin
99
+ offense_begin_pos.join(condition_expression.loc.expression)
83
100
  end
84
101
  end
85
102
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
  # asserting last argument is an optional argument
34
34
  return unless last_arg.optarg_type?
35
35
 
36
- _, default_value = *last_arg
36
+ arg, default_value = *last_arg
37
37
 
38
38
  # asserting default value is a hash
39
39
  return unless default_value.hash_type?
@@ -42,6 +42,9 @@ module RuboCop
42
42
  *key_value_pairs = *default_value
43
43
  return unless key_value_pairs.empty?
44
44
 
45
+ # Check for suspicious argument names
46
+ return unless name_in_suspicious_param_names?(arg)
47
+
45
48
  add_offense(last_arg, :expression, MSG)
46
49
  end
47
50
 
@@ -50,6 +53,11 @@ module RuboCop
50
53
  def supports_keyword_arguments?
51
54
  RUBY_VERSION >= '2.0.0'
52
55
  end
56
+
57
+ def name_in_suspicious_param_names?(arg_name)
58
+ cop_config.key?('SuspiciousParamNames') &&
59
+ cop_config['SuspiciousParamNames'].include?(arg_name.to_s)
60
+ end
53
61
  end
54
62
  end
55
63
  end
@@ -21,7 +21,6 @@ module RuboCop
21
21
  # b = 2
22
22
  # c = 3
23
23
  class ParallelAssignment < Cop
24
- include AutocorrectAlignment
25
24
  include IfNode
26
25
 
27
26
  MSG = 'Do not use parallel assignment.'
@@ -56,11 +55,11 @@ module RuboCop
56
55
  lambda do |corrector|
57
56
  assignment_corrector =
58
57
  if modifier_statement?(node.parent)
59
- ModifierCorrector.new(node, configured_indentation_width)
58
+ ModifierCorrector.new(node, config)
60
59
  elsif rescue_modifier?(node.parent)
61
- RescueCorrector.new(node, configured_indentation_width)
60
+ RescueCorrector.new(node, config)
62
61
  else
63
- GenericCorrector.new(node, configured_indentation_width)
62
+ GenericCorrector.new(node, config)
64
63
  end
65
64
 
66
65
  corrector.replace(assignment_corrector.correction_range,
@@ -98,35 +97,27 @@ module RuboCop
98
97
 
99
98
  # An internal class for correcting parallel assignment
100
99
  class GenericCorrector
101
- attr_reader :correction, :correction_range
100
+ include AutocorrectAlignment
102
101
 
103
- def initialize(node, indentation_width)
102
+ attr_reader :config, :node, :correction, :correction_range
103
+
104
+ def initialize(node, config)
104
105
  @node = node
105
- @indentation_width = indentation_width
106
+ @config = config
106
107
  end
107
108
 
108
109
  def correction
109
- "#{assignment.join("\n#{indent}")}"
110
+ "#{assignment.join("\n#{offset(node)}")}"
110
111
  end
111
112
 
112
113
  def correction_range
113
- @node.loc.expression
114
+ node.loc.expression
114
115
  end
115
116
 
116
117
  protected
117
118
 
118
- def space_offset
119
- @node.loc.expression.column
120
- end
121
-
122
- def indent
123
- ' ' * space_offset
124
- end
125
-
126
- attr_reader :indentation_width
127
-
128
119
  def assignment
129
- left, right = *@node
120
+ left, right = *node
130
121
  l_vars = extract_sources(left)
131
122
  r_vars = extract_sources(right)
132
123
  groups = l_vars.zip(r_vars)
@@ -144,25 +135,18 @@ module RuboCop
144
135
  # protected by rescue
145
136
  class RescueCorrector < GenericCorrector
146
137
  def correction
147
- _node, rescue_clause = *@node.parent
138
+ _node, rescue_clause = *node.parent
148
139
  _, _, rescue_result = *rescue_clause
149
140
 
150
141
  "begin\n" <<
151
- indent << assignment.join("\n#{indent}") <<
152
- "\nrescue\n" <<
153
- indent << rescue_result.loc.expression.source <<
154
- "\nend"
142
+ indentation(node) << assignment.join("\n#{indentation(node)}") <<
143
+ "\n#{offset(node)}rescue\n" <<
144
+ indentation(node) << rescue_result.loc.expression.source <<
145
+ "\n#{offset(node)}end"
155
146
  end
156
147
 
157
148
  def correction_range
158
- @node.parent.loc.expression
159
- end
160
-
161
- protected
162
-
163
- def space_offset
164
- offset = super
165
- offset + indentation_width
149
+ node.parent.loc.expression
166
150
  end
167
151
  end
168
152
 
@@ -170,7 +154,7 @@ module RuboCop
170
154
  # guarded by if, unless, while, or until
171
155
  class ModifierCorrector < GenericCorrector
172
156
  def correction
173
- parent = @node.parent
157
+ parent = node.parent
174
158
 
175
159
  modifier_range =
176
160
  Parser::Source::Range.new(parent.loc.expression.source_buffer,
@@ -178,19 +162,12 @@ module RuboCop
178
162
  parent.loc.expression.end_pos)
179
163
 
180
164
  "#{modifier_range.source}\n" <<
181
- indent << assignment.join("\n#{indent}") <<
182
- "\nend"
165
+ indentation(node) << assignment.join("\n#{indentation(node)}") <<
166
+ "\n#{offset(node)}end"
183
167
  end
184
168
 
185
169
  def correction_range
186
- @node.parent.loc.expression
187
- end
188
-
189
- protected
190
-
191
- def space_offset
192
- offset = super
193
- offset + indentation_width
170
+ node.parent.loc.expression
194
171
  end
195
172
  end
196
173
  end