rubocop 1.5.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +5 -2
  4. data/config/obsoletion.yml +196 -0
  5. data/lib/rubocop.rb +10 -0
  6. data/lib/rubocop/cli/command/suggest_extensions.rb +16 -44
  7. data/lib/rubocop/config_obsoletion.rb +63 -263
  8. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  9. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  10. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  11. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  12. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  13. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  14. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  15. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  16. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  17. data/lib/rubocop/config_validator.rb +11 -4
  18. data/lib/rubocop/cop/base.rb +17 -15
  19. data/lib/rubocop/cop/cop.rb +2 -2
  20. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  21. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  22. data/lib/rubocop/cop/layout/line_length.rb +6 -16
  23. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  24. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  25. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  26. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  27. data/lib/rubocop/cop/style/float_division.rb +44 -1
  28. data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -0
  29. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  30. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  31. data/lib/rubocop/cop/style/redundant_argument.rb +14 -1
  32. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  33. data/lib/rubocop/cop/style/sole_nested_conditional.rb +12 -6
  34. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  35. data/lib/rubocop/cop/style/string_concatenation.rb +19 -0
  36. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  37. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  38. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  39. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  40. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  41. data/lib/rubocop/lockfile.rb +40 -0
  42. data/lib/rubocop/version.rb +1 -1
  43. metadata +14 -3
@@ -84,7 +84,7 @@ module RuboCop
84
84
  private
85
85
 
86
86
  def next_line_empty?(line)
87
- processed_source[line].blank?
87
+ processed_source[line].nil? || processed_source[line].blank?
88
88
  end
89
89
 
90
90
  def require_empty_line?(node)
@@ -58,12 +58,13 @@ module RuboCop
58
58
  # bar: "0000000000",
59
59
  # baz: "0000000000",
60
60
  # }
61
- class LineLength < Cop
61
+ class LineLength < Base
62
62
  include CheckLineBreakable
63
63
  include ConfigurableMax
64
64
  include IgnoredPattern
65
65
  include RangeHelp
66
66
  include LineLengthHelp
67
+ extend AutoCorrector
67
68
 
68
69
  MSG = 'Line is too long. [%<length>d/%<max>d]'
69
70
 
@@ -78,28 +79,16 @@ module RuboCop
78
79
  alias on_hash on_potential_breakable_node
79
80
  alias on_send on_potential_breakable_node
80
81
 
81
- def investigate(processed_source)
82
+ def on_new_investigation
82
83
  check_for_breakable_semicolons(processed_source)
83
84
  end
84
85
 
85
- def investigate_post_walk(processed_source)
86
+ def on_investigation_end
86
87
  processed_source.lines.each_with_index do |line, line_index|
87
88
  check_line(line, line_index)
88
89
  end
89
90
  end
90
91
 
91
- def correctable?
92
- super && !breakable_range.nil?
93
- end
94
-
95
- def autocorrect(range)
96
- return if range.nil?
97
-
98
- lambda do |corrector|
99
- corrector.insert_before(range, "\n")
100
- end
101
- end
102
-
103
92
  private
104
93
 
105
94
  attr_accessor :breakable_range
@@ -203,8 +192,9 @@ module RuboCop
203
192
 
204
193
  self.breakable_range = breakable_range_by_line_index[line_index]
205
194
 
206
- add_offense(breakable_range, location: loc, message: message) do
195
+ add_offense(loc, message: message) do |corrector|
207
196
  self.max = line_length(line)
197
+ corrector.insert_before(breakable_range, "\n") unless breakable_range.nil?
208
198
  end
209
199
  end
210
200
 
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
 
73
73
  def qualified_legacy_cop_name(cop_name)
74
- legacy_cop_names = RuboCop::ConfigObsoletion::OBSOLETE_COPS.keys
74
+ legacy_cop_names = RuboCop::ConfigObsoletion.legacy_cop_names
75
75
 
76
76
  legacy_cop_names.detect do |legacy_cop_name|
77
77
  legacy_cop_name.split('/')[1] == cop_name
@@ -14,7 +14,10 @@ module RuboCop
14
14
  return if part_of_ignored_node?(node)
15
15
 
16
16
  if offense?(node)
17
- add_offense(node) { opposite_style_detected }
17
+ add_offense(node) do |corrector|
18
+ opposite_style_detected
19
+ autocorrect(corrector, node) if respond_to?(:autocorrect, true)
20
+ end
18
21
  else
19
22
  correct_style_detected
20
23
  end
@@ -3,7 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Naming
6
- # This cop makes sure that accessor methods are named properly.
6
+ # This cop makes sure that accessor methods are named properly. Applies
7
+ # to both instance and class methods.
8
+ #
9
+ # NOTE: Offenses are only registered for methods with the expected
10
+ # arity. Getters (`get_attribute`) must have no arguments to be
11
+ # registered, and setters (`set_attribute(value)`) must have exactly
12
+ # one.
7
13
  #
8
14
  # @example
9
15
  # # bad
@@ -21,6 +27,14 @@ module RuboCop
21
27
  # # good
22
28
  # def attribute
23
29
  # end
30
+ #
31
+ # # accepted, incorrect arity for getter
32
+ # def get_value(attr)
33
+ # end
34
+ #
35
+ # # accepted, incorrect arity for setter
36
+ # def set_value
37
+ # end
24
38
  class AccessorMethodName < Base
25
39
  MSG_READER = 'Do not prefix reader method names with `get_`.'
26
40
  MSG_WRITER = 'Do not prefix writer method names with `set_`.'
@@ -14,8 +14,9 @@ module RuboCop
14
14
  #
15
15
  # # good
16
16
  # ?\C-\M-d
17
- class CharacterLiteral < Cop
17
+ class CharacterLiteral < Base
18
18
  include StringHelp
19
+ extend AutoCorrector
19
20
 
20
21
  MSG = 'Do not use the character literal - ' \
21
22
  'use string literal instead.'
@@ -26,17 +27,15 @@ module RuboCop
26
27
  node.source.size.between?(2, 3)
27
28
  end
28
29
 
29
- def autocorrect(node)
30
- lambda do |corrector|
31
- string = node.source[1..-1]
30
+ def autocorrect(corrector, node)
31
+ string = node.source[1..-1]
32
32
 
33
- # special character like \n
34
- # or ' which needs to use "" or be escaped.
35
- if string.length == 2 || string == "'"
36
- corrector.replace(node, %("#{string}"))
37
- elsif string.length == 1 # normal character
38
- corrector.replace(node, "'#{string}'")
39
- end
33
+ # special character like \n
34
+ # or ' which needs to use "" or be escaped.
35
+ if string.length == 2 || string == "'"
36
+ corrector.replace(node, %("#{string}"))
37
+ elsif string.length == 1 # normal character
38
+ corrector.replace(node, "'#{string}'")
40
39
  end
41
40
  end
42
41
 
@@ -41,6 +41,8 @@ module RuboCop
41
41
  # a.fdiv(b)
42
42
  class FloatDivision < Base
43
43
  include ConfigurableEnforcedStyle
44
+ extend AutoCorrector
45
+
44
46
  MESSAGES = {
45
47
  left_coerce: 'Prefer using `.to_f` on the left side.',
46
48
  right_coerce: 'Prefer using `.to_f` on the right side.',
@@ -64,7 +66,20 @@ module RuboCop
64
66
  PATTERN
65
67
 
66
68
  def on_send(node)
67
- add_offense(node) if offense_condition?(node)
69
+ return unless offense_condition?(node)
70
+
71
+ add_offense(node) do |corrector|
72
+ case style
73
+ when :left_coerce, :single_coerce
74
+ add_to_f_method(corrector, node.receiver)
75
+ remove_to_f_method(corrector, node.first_argument)
76
+ when :right_coerce
77
+ remove_to_f_method(corrector, node.receiver)
78
+ add_to_f_method(corrector, node.first_argument)
79
+ when :fdiv
80
+ correct_from_slash_to_fdiv(corrector, node, node.receiver, node.first_argument)
81
+ end
82
+ end
68
83
  end
69
84
 
70
85
  private
@@ -87,6 +102,34 @@ module RuboCop
87
102
  def message(_node)
88
103
  MESSAGES[style]
89
104
  end
105
+
106
+ def add_to_f_method(corrector, node)
107
+ corrector.insert_after(node, '.to_f') unless node.send_type? && node.method?(:to_f)
108
+ end
109
+
110
+ def remove_to_f_method(corrector, send_node)
111
+ corrector.remove(send_node.loc.dot)
112
+ corrector.remove(send_node.loc.selector)
113
+ end
114
+
115
+ def correct_from_slash_to_fdiv(corrector, node, receiver, argument)
116
+ receiver_source = extract_receiver_source(receiver)
117
+ argument_source = extract_receiver_source(argument)
118
+
119
+ if argument.respond_to?(:parenthesized?) && !argument.parenthesized?
120
+ argument_source = "(#{argument_source})"
121
+ end
122
+
123
+ corrector.replace(node, "#{receiver_source}.fdiv#{argument_source}")
124
+ end
125
+
126
+ def extract_receiver_source(node)
127
+ if node.send_type? && node.method?(:to_f)
128
+ node.receiver.source
129
+ else
130
+ node.source
131
+ end
132
+ end
90
133
  end
91
134
  end
92
135
  end
@@ -46,6 +46,10 @@ module RuboCop
46
46
  MSG_USE_NORMAL =
47
47
  'Modifier form of `%<keyword>s` makes the line too long.'
48
48
 
49
+ def self.autocorrect_incompatible_with
50
+ [Style::SoleNestedConditional]
51
+ end
52
+
49
53
  def on_if(node)
50
54
  msg = if single_line_as_modifier?(node) && !named_capture_in_condition?(node)
51
55
  MSG_USE_MODIFIER
@@ -18,7 +18,7 @@ module RuboCop
18
18
  #
19
19
  # # good
20
20
  # ip_address = ENV['DEPLOYMENT_IP_ADDRESS']
21
- class IpAddresses < Cop
21
+ class IpAddresses < Base
22
22
  include StringHelp
23
23
 
24
24
  IPV6_MAX_SIZE = 45 # IPv4-mapped IPv6 is the longest
@@ -4,7 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop looks for uses of Perl-style regexp match
7
- # backreferences like $1, $2, etc.
7
+ # backreferences and their English versions like
8
+ # $1, $2, $&, &+, $MATCH, $PREMATCH, etc.
8
9
  #
9
10
  # @example
10
11
  # # bad
@@ -15,19 +16,95 @@ module RuboCop
15
16
  class PerlBackrefs < Base
16
17
  extend AutoCorrector
17
18
 
18
- MSG = 'Avoid the use of Perl-style backrefs.'
19
+ MESSAGE_FORMAT = 'Prefer `%<preferred_expression>s` over `%<original_expression>s`.'
20
+
21
+ def on_back_ref(node)
22
+ on_back_ref_or_gvar_or_nth_ref(node)
23
+ end
24
+
25
+ def on_gvar(node)
26
+ on_back_ref_or_gvar_or_nth_ref(node)
27
+ end
19
28
 
20
29
  def on_nth_ref(node)
21
- add_offense(node) do |corrector|
22
- backref, = *node
23
- parent_type = node.parent ? node.parent.type : nil
30
+ on_back_ref_or_gvar_or_nth_ref(node)
31
+ end
24
32
 
25
- if %i[dstr xstr regexp].include?(parent_type)
26
- corrector.replace(node, "{Regexp.last_match(#{backref})}")
33
+ private
34
+
35
+ # @private
36
+ # @param [RuboCop::AST::Node] node
37
+ # @return [Boolean]
38
+ def derived_from_braceless_interpolation?(node)
39
+ %i[
40
+ dstr
41
+ regexp
42
+ xstr
43
+ ].include?(node.parent&.type)
44
+ end
45
+
46
+ # @private
47
+ # @param [RuboCop::AST::Node] node
48
+ # @param [String] preferred_expression
49
+ # @return [String]
50
+ def format_message(node:, preferred_expression:)
51
+ original_expression = original_expression_of(node)
52
+ format(
53
+ MESSAGE_FORMAT,
54
+ original_expression: original_expression,
55
+ preferred_expression: preferred_expression
56
+ )
57
+ end
27
58
 
28
- else
29
- corrector.replace(node, "Regexp.last_match(#{backref})")
59
+ # @private
60
+ # @param [RuboCop::AST::Node] node
61
+ # @return [String]
62
+ def original_expression_of(node)
63
+ first = node.to_a.first
64
+ if first.is_a?(::Integer)
65
+ "$#{first}"
66
+ else
67
+ first.to_s
68
+ end
69
+ end
70
+
71
+ # @private
72
+ # @param [RuboCop::AST::Node] node
73
+ # @return [String, nil]
74
+ def preferred_expression_to(node)
75
+ first = node.to_a.first
76
+ case first
77
+ when ::Integer
78
+ "Regexp.last_match(#{first})"
79
+ when :$&, :$MATCH
80
+ 'Regexp.last_match(0)'
81
+ when :$`, :$PREMATCH
82
+ 'Regexp.last_match.pre_match'
83
+ when :$', :$POSTMATCH
84
+ 'Regexp.last_match.post_match'
85
+ when :$+, :$LAST_PAREN_MATCH
86
+ 'Regexp.last_match(-1)'
87
+ end
88
+ end
89
+
90
+ # @private
91
+ # @param [RuboCop::AST::Node] node
92
+ def on_back_ref_or_gvar_or_nth_ref(node)
93
+ preferred_expression = preferred_expression_to(node)
94
+ return unless preferred_expression
95
+
96
+ add_offense(
97
+ node,
98
+ message: format_message(
99
+ node: node,
100
+ preferred_expression: preferred_expression
101
+ )
102
+ ) do |corrector|
103
+ if derived_from_braceless_interpolation?(node)
104
+ preferred_expression = "{#{preferred_expression}}"
30
105
  end
106
+
107
+ corrector.replace(node, preferred_expression)
31
108
  end
32
109
  end
33
110
  end
@@ -37,6 +37,9 @@ module RuboCop
37
37
  # "first second".split
38
38
  # A.foo
39
39
  class RedundantArgument < Base
40
+ include RangeHelp
41
+ extend AutoCorrector
42
+
40
43
  MSG = 'Argument %<arg>s is redundant because it is implied by default.'
41
44
 
42
45
  def on_send(node)
@@ -44,7 +47,9 @@ module RuboCop
44
47
  return if node.arguments.count != 1
45
48
  return unless redundant_argument?(node)
46
49
 
47
- add_offense(node, message: format(MSG, arg: node.arguments.first.source))
50
+ add_offense(node, message: format(MSG, arg: node.arguments.first.source)) do |corrector|
51
+ corrector.remove(argument_range(node))
52
+ end
48
53
  end
49
54
 
50
55
  private
@@ -69,6 +74,14 @@ module RuboCop
69
74
  Parser::CurrentRuby.new(builder).parse(buffer)
70
75
  end
71
76
  end
77
+
78
+ def argument_range(node)
79
+ if node.parenthesized?
80
+ range_between(node.loc.begin.begin_pos, node.loc.end.end_pos)
81
+ else
82
+ range_with_surrounding_space(range: node.first_argument.source_range, newlines: false)
83
+ end
84
+ end
72
85
  end
73
86
  end
74
87
  end
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # c + d
30
30
  # end
31
31
  class SingleLineBlockParams < Base
32
+ extend AutoCorrector
33
+
32
34
  MSG = 'Name `%<method>s` block params `|%<params>s|`.'
33
35
 
34
36
  def on_block(node)
@@ -37,20 +39,41 @@ module RuboCop
37
39
  return unless eligible_method?(node)
38
40
  return unless eligible_arguments?(node)
39
41
 
40
- return if args_match?(node.send_node.method_name, node.arguments)
42
+ method_name = node.send_node.method_name
43
+ return if args_match?(method_name, node.arguments)
44
+
45
+ preferred_block_arguments = build_preferred_arguments_map(node, target_args(method_name))
46
+ joined_block_arguments = preferred_block_arguments.values.join(', ')
41
47
 
42
- message = message(node.arguments)
48
+ message = format(MSG, method: method_name, params: joined_block_arguments)
43
49
 
44
- add_offense(node.arguments, message: message)
50
+ add_offense(node.arguments, message: message) do |corrector|
51
+ autocorrect(corrector, node, preferred_block_arguments, joined_block_arguments)
52
+ end
45
53
  end
46
54
 
47
55
  private
48
56
 
49
- def message(node)
50
- method_name = node.parent.send_node.method_name
51
- arguments = target_args(method_name).join(', ')
57
+ def build_preferred_arguments_map(node, preferred_arguments)
58
+ preferred_arguments_map = {}
59
+ node.arguments.each_with_index do |current_lvar, index|
60
+ preferred_argument = preferred_arguments[index]
61
+ current_argument = current_lvar.source
62
+ preferred_argument = "_#{preferred_argument}" if current_argument.start_with?('_')
63
+ preferred_arguments_map[current_argument] = preferred_argument
64
+ end
65
+
66
+ preferred_arguments_map
67
+ end
68
+
69
+ def autocorrect(corrector, node, preferred_block_arguments, joined_block_arguments)
70
+ corrector.replace(node.arguments, "|#{joined_block_arguments}|")
52
71
 
53
- format(MSG, method: method_name, params: arguments)
72
+ node.each_descendant(:lvar) do |lvar|
73
+ if (preferred_lvar = preferred_block_arguments[lvar.source])
74
+ corrector.replace(lvar, preferred_lvar)
75
+ end
76
+ end
54
77
  end
55
78
 
56
79
  def eligible_arguments?(node)