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
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # Place `when` conditions that use splat at the end
7
+ # of the list of `when` branches.
8
+ #
9
+ # Ruby has to allocate memory for the splat expansion every time
10
+ # that the `case` `when` statement is run. Since Ruby does not support
11
+ # fall through inside of `case` `when`, like some other languages do,
12
+ # the order of the `when` branches does not matter. By placing any
13
+ # splat expansions at the end of the list of `when` branches we will
14
+ # reduce the number of times that memory has to be allocated for
15
+ # the expansion.
16
+ #
17
+ # This is not a guaranteed performance improvement. If the data being
18
+ # processed by the `case` condition is normalized in a manner that favors
19
+ # hitting a condition in the splat expansion, it is possible that
20
+ # moving the splat condition to the end will use more memory,
21
+ # and run slightly slower.
22
+ #
23
+ # @example
24
+ # # bad
25
+ # case foo
26
+ # when *condition
27
+ # bar
28
+ # when baz
29
+ # foobar
30
+ # end
31
+ #
32
+ # case foo
33
+ # when *[1, 2, 3, 4]
34
+ # bar
35
+ # when 5
36
+ # baz
37
+ # end
38
+ #
39
+ # # good
40
+ # case foo
41
+ # when baz
42
+ # foobar
43
+ # when *condition
44
+ # bar
45
+ # end
46
+ #
47
+ # case foo
48
+ # when 1, 2, 3, 4
49
+ # bar
50
+ # when 5
51
+ # baz
52
+ # end
53
+ class CaseWhenSplat < Cop
54
+ include AutocorrectAlignment
55
+
56
+ MSG = 'Place `when` conditions with a splat ' \
57
+ 'at the end of the `when` branches.'.freeze
58
+ ARRAY_MSG = 'Do not expand array literals in `when` conditions.'.freeze
59
+
60
+ def on_case(node)
61
+ _case_branch, *when_branches, _else_branch = *node
62
+ when_conditions =
63
+ when_branches.each_with_object([]) do |branch, conditions|
64
+ condition, = *branch
65
+ conditions << condition
66
+ end
67
+
68
+ splat_offenses(when_conditions).reverse_each do |condition|
69
+ range = condition.parent.loc.keyword.join(condition.loc.expression)
70
+ variable, = *condition
71
+ message = variable.array_type? ? ARRAY_MSG : MSG
72
+ add_offense(condition.parent, range, message)
73
+ end
74
+ end
75
+
76
+ def autocorrect(node)
77
+ condition, = *node
78
+ variable, = *condition
79
+ if variable.array_type?
80
+ correct_array_literal(condition, variable)
81
+ else
82
+ reorder_splat_condition(node)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def splat_offenses(when_conditions)
89
+ found_non_splat = false
90
+ when_conditions.reverse.each_with_object([]) do |condition, result|
91
+ found_non_splat ||= true if error_condition?(condition)
92
+
93
+ next unless condition.splat_type?
94
+ result << condition if found_non_splat
95
+ end
96
+ end
97
+
98
+ def error_condition?(condition)
99
+ variable, = *condition
100
+
101
+ (condition.splat_type? && variable.array_type?) ||
102
+ !condition.splat_type?
103
+ end
104
+
105
+ def correct_array_literal(condition, variable)
106
+ lambda do |corrector|
107
+ corrector.remove(condition.loc.operator)
108
+ corrector.remove(variable.loc.begin)
109
+ corrector.remove(variable.loc.end)
110
+ end
111
+ end
112
+
113
+ def reorder_splat_condition(node)
114
+ _case_branch, *when_branches, _else_branch = *node.parent
115
+ current_index = when_branches.index { |branch| branch == node }
116
+ next_branch = when_branches[current_index + 1]
117
+ correction = "\n#{offset(node)}#{node.loc.expression.source}"
118
+ range =
119
+ Parser::Source::Range.new(node.parent,
120
+ node.loc.expression.begin_pos,
121
+ next_branch.loc.expression.begin_pos)
122
+
123
+ lambda do |corrector|
124
+ corrector.remove(range)
125
+ corrector.insert_after(when_branches.last.loc.expression,
126
+ correction)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -20,10 +20,15 @@ module RuboCop
20
20
  # 'a b c'.delete(' ')
21
21
  class StringReplacement < Cop
22
22
  MSG = 'Use `%s` instead of `%s`.'
23
- DETERMINISTIC_REGEX = /^[\w\s\-,."']+$/
24
- REGEXP_CONSTRUCTOR_METHODS = [:new, :compile]
25
- GSUB_METHODS = [:gsub, :gsub!]
26
- DETERMINISTIC_TYPES = [:regexp, :str, :send]
23
+ DETERMINISTIC_REGEX = /^[\w\s\-,."']+$/.freeze
24
+ REGEXP_CONSTRUCTOR_METHODS = [:new, :compile].freeze
25
+ GSUB_METHODS = [:gsub, :gsub!].freeze
26
+ DETERMINISTIC_TYPES = [:regexp, :str, :send].freeze
27
+ DELETE = 'delete'.freeze
28
+ TR = 'tr'.freeze
29
+ BANG = '!'.freeze
30
+ SINGLE_QUOTE = "'".freeze
31
+ CLOSING_PAREN = ')'.freeze
27
32
 
28
33
  def on_send(node)
29
34
  _string, method, first_param, second_param = *node
@@ -51,19 +56,20 @@ module RuboCop
51
56
  _string, method, first_param, second_param = *node
52
57
  first_source = first_source(first_param)
53
58
  second_source, = *second_param
54
- replacement_method = replacement_method(first_source, second_source)
59
+ replacement_method = replacement_method(method,
60
+ first_source,
61
+ second_source)
55
62
 
56
63
  lambda do |corrector|
57
- replacement =
58
- if second_source.empty? && first_source.length == 1
59
- "#{replacement_method}#{'!' if bang_method?(method)}" \
60
- "(#{escape(first_source)})"
61
- else
62
- "#{replacement_method}#{'!' if bang_method?(method)}" \
63
- "(#{escape(first_source)}, #{escape(second_source)})"
64
- end
65
-
66
- corrector.replace(range(node), replacement)
64
+ corrector.replace(node.loc.selector, replacement_method)
65
+ unless first_param.str_type?
66
+ corrector.replace(first_param.loc.expression,
67
+ escape(first_source))
68
+ end
69
+
70
+ if second_source.empty? && first_source.length == 1
71
+ remove_second_param(corrector, node, first_param)
72
+ end
67
73
  end
68
74
  end
69
75
 
@@ -123,24 +129,26 @@ module RuboCop
123
129
  node.loc.expression.end_pos)
124
130
  end
125
131
 
126
- def replacement_method(first_source, second_source)
127
- if second_source.empty? && first_source.length == 1
128
- 'delete'
129
- else
130
- 'tr'
131
- end
132
+ def replacement_method(method, first_source, second_source)
133
+ replacement = if second_source.empty? && first_source.length == 1
134
+ DELETE
135
+ else
136
+ TR
137
+ end
138
+
139
+ "#{replacement}#{BANG if bang_method?(method)}"
132
140
  end
133
141
 
134
142
  def message(method, first_source, second_source)
135
- replacement_method = replacement_method(first_source, second_source)
143
+ replacement_method = replacement_method(method,
144
+ first_source,
145
+ second_source)
136
146
 
137
- format(MSG,
138
- "#{replacement_method}#{'!' if bang_method?(method)}",
139
- method)
147
+ format(MSG, replacement_method, method)
140
148
  end
141
149
 
142
150
  def bang_method?(method)
143
- method.to_s.end_with?('!')
151
+ method.to_s.end_with?(BANG)
144
152
  end
145
153
 
146
154
  def escape(string)
@@ -152,8 +160,17 @@ module RuboCop
152
160
  end
153
161
 
154
162
  def require_double_quotes?(string)
155
- /'/ =~ string.inspect ||
156
- StringHelp::ESCAPED_CHAR_REGEXP =~ string.inspect
163
+ string.inspect.include?(SINGLE_QUOTE) ||
164
+ StringHelp::ESCAPED_CHAR_REGEXP =~ string
165
+ end
166
+
167
+ def remove_second_param(corrector, node, first_param)
168
+ end_range =
169
+ Parser::Source::Range.new(node.loc.expression.source_buffer,
170
+ first_param.loc.expression.end_pos,
171
+ node.loc.expression.end_pos)
172
+
173
+ corrector.replace(end_range, CLOSING_PAREN)
157
174
  end
158
175
  end
159
176
  end
@@ -12,11 +12,37 @@ module RuboCop
12
12
 
13
13
  MSG = 'Prefer `%s` over `%s`.'
14
14
 
15
- FILTER_METHODS = [:before_filter, :skip_before_filter,
16
- :after_filter, :around_filter]
17
-
18
- ACTION_METHODS = [:before_action, :skip_before_action,
19
- :after_action, :around_action]
15
+ FILTER_METHODS = [
16
+ :after_filter,
17
+ :append_after_filter,
18
+ :append_around_filter,
19
+ :append_before_filter,
20
+ :around_filter,
21
+ :before_filter,
22
+ :prepend_after_filter,
23
+ :prepend_around_filter,
24
+ :prepend_before_filter,
25
+ :skip_after_filter,
26
+ :skip_around_filter,
27
+ :skip_before_filter,
28
+ :skip_filter
29
+ ]
30
+
31
+ ACTION_METHODS = [
32
+ :after_action,
33
+ :append_after_action,
34
+ :append_around_action,
35
+ :append_before_action,
36
+ :around_action,
37
+ :before_action,
38
+ :prepend_after_action,
39
+ :prepend_around_action,
40
+ :prepend_before_action,
41
+ :skip_after_action,
42
+ :skip_around_action,
43
+ :skip_before_action,
44
+ :skip_action_callback
45
+ ]
20
46
 
21
47
  def on_block(node)
22
48
  method, _args, _body = *node
@@ -33,15 +33,15 @@ module RuboCop
33
33
  # Date.today
34
34
  # date.to_time
35
35
  #
36
- # # reports offense only when style is 'always'
36
+ # # reports offense only when style is 'strict'
37
37
  # date.to_time_in_current_zone
38
38
  class Date < Cop
39
39
  include ConfigurableEnforcedStyle
40
40
 
41
41
  MSG = 'Do not use `%s` without zone. Use `%s` instead.'
42
42
 
43
- MSG_SEND = 'Do not use `%s` on Date objects,' \
44
- 'because it knows nothing about time zone in use.'
43
+ MSG_SEND = 'Do not use `%s` on Date objects, ' \
44
+ 'because they know nothing about the time zone in use.'
45
45
 
46
46
  BAD_DAYS = [:today, :current, :yesterday, :tomorrow]
47
47
 
@@ -54,8 +54,9 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def on_send(node)
57
- receiver, method_name, *_args = *node
57
+ receiver, method_name, *args = *node
58
58
  return unless receiver && bad_methods.include?(method_name)
59
+ return if method_name == :to_time && args.length == 1
59
60
 
60
61
  add_offense(node, :selector,
61
62
  format(MSG_SEND,
@@ -96,7 +97,7 @@ module RuboCop
96
97
  # checks that parent node of send_type
97
98
  # and receiver is the given node
98
99
  def method_send?(node)
99
- return false unless node.parent.send_type?
100
+ return false unless node.parent && node.parent.send_type?
100
101
 
101
102
  receiver, _method_name, *_args = *node.parent
102
103
 
@@ -33,7 +33,7 @@ module RuboCop
33
33
  if method_name == :read_attribute
34
34
  format(MSG, 'self[:attr]', 'read_attribute(:attr)')
35
35
  else
36
- format(MSG, 'self[:attr] = val', 'write_attribute(:attr, var)')
36
+ format(MSG, 'self[:attr] = val', 'write_attribute(:attr, val)')
37
37
  end
38
38
  end
39
39
 
@@ -66,6 +66,8 @@ module RuboCop
66
66
 
67
67
  method_name = (chain & DANGEROUS_METHODS).join('.')
68
68
 
69
+ return if offset_provided?(node)
70
+
69
71
  message = build_message(klass, method_name, node)
70
72
 
71
73
  add_offense(node, :selector, message)
@@ -107,7 +109,7 @@ module RuboCop
107
109
  # checks that parent node of send_type
108
110
  # and receiver is the given node
109
111
  def method_send?(node)
110
- return false unless node.parent.send_type?
112
+ return false unless node.parent && node.parent.send_type?
111
113
 
112
114
  receiver, _method_name, *_args = *node.parent
113
115
 
@@ -163,6 +165,15 @@ module RuboCop
163
165
 
164
166
  acceptable
165
167
  end
168
+
169
+ # Time.new can be called with a time zone offset
170
+ # When it is, that should be considered safe
171
+ # Example:
172
+ # Time.new(1988, 3, 15, 3, 0, 0, "-05:00")
173
+ def offset_provided?(node)
174
+ _, _, *args = *node
175
+ args.length >= 7
176
+ end
166
177
  end
167
178
  end
168
179
  end
@@ -15,6 +15,7 @@ module RuboCop
15
15
  # using alias is the only option in certain scenarios
16
16
  # in such scenarios we don't want to report an offense
17
17
  return unless method_name == :instance_exec
18
+ return unless body
18
19
 
19
20
  body.each_node(:alias) { |n| ignore_node(n) }
20
21
  end
@@ -59,11 +59,8 @@ module RuboCop
59
59
  b = node.loc.begin
60
60
  e = node.loc.end
61
61
  if b.is?('{')
62
- # If the left brace is not preceded by a whitespace character,
63
- # then we need a space before `do` to get valid Ruby code.
64
- if b.source_buffer.source[b.begin_pos - 1, 1] =~ /\S/
65
- corrector.insert_before(b, ' ')
66
- end
62
+ corrector.insert_before(b, ' ') unless whitespace_before?(b)
63
+ corrector.insert_before(e, ' ') unless whitespace_before?(e)
67
64
  corrector.replace(b, 'do')
68
65
  corrector.replace(e, 'end')
69
66
  else
@@ -73,6 +70,10 @@ module RuboCop
73
70
  end
74
71
  end
75
72
 
73
+ def whitespace_before?(node)
74
+ node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
75
+ end
76
+
76
77
  def get_block(node)
77
78
  case node.type
78
79
  when :block
@@ -10,6 +10,8 @@ module RuboCop
10
10
  # Enumerable or not (static analysis limitation), so this cop
11
11
  # can yield some false positives.
12
12
  class CollectionMethods < Cop
13
+ include MethodPreference
14
+
13
15
  MSG = 'Prefer `%s` over `%s`.'
14
16
 
15
17
  def on_block(node)
@@ -44,26 +46,6 @@ module RuboCop
44
46
  method_name)
45
47
  )
46
48
  end
47
-
48
- def preferred_method(method)
49
- preferred_methods[method.to_sym]
50
- end
51
-
52
- def preferred_methods
53
- @preferred_methods ||=
54
- begin
55
- # Make sure default configuration 'foo' => 'bar' is removed from
56
- # the total configuration if there is a 'bar' => 'foo' override.
57
- default = default_cop_config['PreferredMethods']
58
- merged = cop_config['PreferredMethods']
59
- overrides = merged.values - default.values
60
- merged.reject { |key, _| overrides.include?(key) }.symbolize_keys
61
- end
62
- end
63
-
64
- def default_cop_config
65
- ConfigLoader.default_configuration[cop_name]
66
- end
67
49
  end
68
50
  end
69
51
  end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # are special cases when they should follow the same rules as the
9
9
  # alignment of end.
10
10
  class ElseAlignment < Cop
11
+ include EndKeywordAlignment
11
12
  include AutocorrectAlignment
12
13
  include CheckAssignment
13
14
 
@@ -86,7 +87,7 @@ module RuboCop
86
87
 
87
88
  end_config = config.for_cop('Lint/EndAlignment')
88
89
  style = end_config['Enabled'] ? end_config['AlignWith'] : 'keyword'
89
- base = style == 'variable' ? node : rhs
90
+ base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs
90
91
 
91
92
  return if rhs.type != :if
92
93