rubocop 0.82.0 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +29 -4
  4. data/lib/rubocop.rb +2 -1
  5. data/lib/rubocop/ast/node/send_node.rb +4 -0
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config.rb +5 -1
  8. data/lib/rubocop/config_loader.rb +15 -14
  9. data/lib/rubocop/config_loader_resolver.rb +27 -0
  10. data/lib/rubocop/config_validator.rb +2 -1
  11. data/lib/rubocop/cop/generator.rb +3 -2
  12. data/lib/rubocop/cop/layout/condition_position.rb +12 -2
  13. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +68 -0
  14. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  15. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +13 -4
  16. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -1
  17. data/lib/rubocop/cop/layout/trailing_whitespace.rb +2 -2
  18. data/lib/rubocop/cop/lint/ambiguous_operator.rb +38 -0
  19. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +14 -0
  20. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -5
  21. data/lib/rubocop/cop/lint/empty_when.rb +29 -6
  22. data/lib/rubocop/cop/lint/ensure_return.rb +18 -1
  23. data/lib/rubocop/cop/lint/literal_as_condition.rb +10 -13
  24. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -9
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -6
  26. data/lib/rubocop/cop/lint/suppressed_exception.rb +0 -6
  27. data/lib/rubocop/cop/lint/useless_access_modifier.rb +12 -0
  28. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -2
  29. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +5 -0
  30. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +10 -1
  31. data/lib/rubocop/cop/mixin/hash_transform_method.rb +8 -1
  32. data/lib/rubocop/cop/mixin/line_length_help.rb +2 -1
  33. data/lib/rubocop/cop/mixin/parser_diagnostic.rb +1 -1
  34. data/lib/rubocop/cop/mixin/statement_modifier.rb +7 -23
  35. data/lib/rubocop/cop/mixin/target_ruby_version.rb +5 -1
  36. data/lib/rubocop/cop/naming/method_name.rb +1 -5
  37. data/lib/rubocop/cop/style/case_equality.rb +1 -1
  38. data/lib/rubocop/cop/style/empty_method.rb +0 -4
  39. data/lib/rubocop/cop/style/guard_clause.rb +25 -2
  40. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -0
  41. data/lib/rubocop/cop/style/lambda_call.rb +0 -20
  42. data/lib/rubocop/cop/style/multiline_when_then.rb +16 -1
  43. data/lib/rubocop/cop/style/optional_arguments.rb +1 -1
  44. data/lib/rubocop/cop/style/slicing_with_range.rb +39 -0
  45. data/lib/rubocop/cop/util.rb +24 -0
  46. data/lib/rubocop/cop/variable_force/assignment.rb +1 -0
  47. data/lib/rubocop/cop/variable_force/scope.rb +1 -0
  48. data/lib/rubocop/cop/variable_force/variable.rb +1 -0
  49. data/lib/rubocop/name_similarity.rb +12 -9
  50. data/lib/rubocop/options.rb +11 -4
  51. data/lib/rubocop/runner.rb +6 -1
  52. data/lib/rubocop/target_finder.rb +6 -4
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +4 -17
  55. data/lib/rubocop/string_util.rb +0 -14
@@ -73,12 +73,6 @@ module RuboCop
73
73
 
74
74
  add_offense(node)
75
75
  end
76
-
77
- private
78
-
79
- def comment_lines?(node)
80
- processed_source[line_range(node)].any? { |line| comment_line?(line) }
81
- end
82
76
  end
83
77
  end
84
78
  end
@@ -125,6 +125,8 @@ module RuboCop
125
125
  # delegate :method_a, to: :method_b
126
126
  # end
127
127
  class UselessAccessModifier < Cop
128
+ include RangeHelp
129
+
128
130
  MSG = 'Useless `%<current>s` access modifier.'
129
131
 
130
132
  def on_class(node)
@@ -145,6 +147,16 @@ module RuboCop
145
147
  check_node(node.children[1]) # singleton class body
146
148
  end
147
149
 
150
+ def autocorrect(node)
151
+ lambda do |corrector|
152
+ range = range_by_whole_lines(
153
+ node.source_range, include_final_newline: true
154
+ )
155
+
156
+ corrector.remove(range)
157
+ end
158
+ end
159
+
148
160
  private
149
161
 
150
162
  def_node_matcher :static_method_definition?, <<~PATTERN
@@ -31,7 +31,6 @@ module RuboCop
31
31
  # do_something(some_var)
32
32
  # end
33
33
  class UselessAssignment < Cop
34
- include NameSimilarity
35
34
  MSG = 'Useless assignment to variable - `%<variable>s`.'
36
35
 
37
36
  def join_force?(force_class)
@@ -94,7 +93,9 @@ module RuboCop
94
93
  end
95
94
 
96
95
  def similar_name_message(variable)
97
- similar_name = find_similar_name(variable.name, variable.scope)
96
+ variable_like_names = collect_variable_like_names(variable.scope)
97
+ similar_name = NameSimilarity.find_similar_name(variable.name,
98
+ variable_like_names)
98
99
  " Did you mean `#{similar_name}`?" if similar_name
99
100
  end
100
101
 
@@ -40,6 +40,11 @@ module RuboCop
40
40
  diagnostic.reason == :useless_else
41
41
  end
42
42
 
43
+ def find_offense_node_by(diagnostic)
44
+ # TODO: When implementing auto-correction, this method should return
45
+ # an offense node passed as first argument of `add_offense` method.
46
+ end
47
+
43
48
  def alternative_message(_diagnostic)
44
49
  MSG
45
50
  end
@@ -46,7 +46,16 @@ module RuboCop
46
46
  end
47
47
 
48
48
  def leading_comment_lines
49
- processed_source.comments.first(3).map(&:text)
49
+ first_non_comment_token = processed_source.tokens.find do |token|
50
+ !token.comment?
51
+ end
52
+
53
+ if first_non_comment_token
54
+ # `line` is 1-indexed so we need to subtract 1 to get the array index
55
+ processed_source.lines[0...first_non_comment_token.line - 1]
56
+ else
57
+ processed_source.lines
58
+ end
50
59
  end
51
60
  end
52
61
  end
@@ -132,7 +132,14 @@ module RuboCop
132
132
  end
133
133
 
134
134
  def self.from_map_to_h(node, match)
135
- strip_trailing_chars = node.parent&.block_type? ? 0 : '.to_h'.length
135
+ strip_trailing_chars = 0
136
+
137
+ unless node.parent&.block_type?
138
+ map_range = node.children.first.source_range
139
+ node_range = node.source_range
140
+ strip_trailing_chars = node_range.end_pos - map_range.end_pos
141
+ end
142
+
136
143
  new(match, node.children.first, 0, strip_trailing_chars)
137
144
  end
138
145
 
@@ -63,7 +63,8 @@ module RuboCop
63
63
  end
64
64
 
65
65
  def tab_indentation_width
66
- config.for_cop('Layout/IndentationStyle')['IndentationWidth']
66
+ config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
67
+ config.for_cop('Layout/IndentationWidth')['Width']
67
68
  end
68
69
 
69
70
  def uri_regexp
@@ -26,7 +26,7 @@ module RuboCop
26
26
  d.message.capitalize
27
27
  end
28
28
 
29
- add_offense(nil,
29
+ add_offense(find_offense_node_by(d),
30
30
  location: d.location,
31
31
  message: message,
32
32
  severity: d.level)
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for modifier cops.
6
6
  module StatementModifier
7
+ include LineLengthHelp
8
+
7
9
  private
8
10
 
9
11
  def single_line_as_modifier?(node)
@@ -34,21 +36,14 @@ module RuboCop
34
36
  def modifier_fits_on_single_line?(node)
35
37
  return true unless max_line_length
36
38
 
37
- modifier_length = length_in_modifier_form(node, node.condition,
38
- node.body.source_length)
39
-
40
- modifier_length <= max_line_length
39
+ length_in_modifier_form(node, node.condition) <= max_line_length
41
40
  end
42
41
 
43
- def length_in_modifier_form(node, cond, body_length)
42
+ def length_in_modifier_form(node, cond)
44
43
  keyword = node.loc.keyword
45
-
46
- indentation = keyword.column * indentation_multiplier
47
- kw_length = keyword.size
48
- cond_length = cond.source_range.size
49
- space = 1
50
-
51
- indentation + body_length + space + kw_length + space + cond_length
44
+ indentation = keyword.source_line[/^\s*/]
45
+ line_length("#{indentation}#{node.body.source} #{keyword.source} " \
46
+ "#{cond.source}")
52
47
  end
53
48
 
54
49
  def max_line_length
@@ -56,17 +51,6 @@ module RuboCop
56
51
 
57
52
  config.for_cop('Layout/LineLength')['Max']
58
53
  end
59
-
60
- def indentation_multiplier
61
- return 1 if config.for_cop('Layout/IndentationStyle')['Enabled']
62
-
63
- default_configuration = RuboCop::ConfigLoader.default_configuration
64
- config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
65
- config.for_cop('Layout/IndentationWidth')['Width'] ||
66
- default_configuration
67
- .for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
68
- default_configuration.for_cop('Layout/IndentationWidth')['Width']
69
- end
70
54
  end
71
55
  end
72
56
  end
@@ -4,12 +4,16 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking target ruby version.
6
6
  module TargetRubyVersion
7
+ def required_minimum_ruby_version
8
+ @minimum_target_ruby_version
9
+ end
10
+
7
11
  def minimum_target_ruby_version(version)
8
12
  @minimum_target_ruby_version = version
9
13
  end
10
14
 
11
15
  def support_target_ruby_version?(version)
12
- @minimum_target_ruby_version <= version
16
+ required_minimum_ruby_version <= version
13
17
  end
14
18
  end
15
19
  end
@@ -35,15 +35,11 @@ module RuboCop
35
35
 
36
36
  MSG = 'Use %<style>s for method names.'
37
37
 
38
- def_node_matcher :attr?, <<~PATTERN
39
- (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
40
- PATTERN
41
-
42
38
  def_node_matcher :sym_name, '(sym $_name)'
43
39
  def_node_matcher :str_name, '(str $_name)'
44
40
 
45
41
  def on_send(node)
46
- return unless (attrs = attr?(node))
42
+ return unless (attrs = node.attribute_accessor?)
47
43
 
48
44
  attrs.last.each do |name_item|
49
45
  name = attr_name(name_item)
@@ -42,7 +42,7 @@ module RuboCop
42
42
 
43
43
  def const?(node)
44
44
  if cop_config.fetch('AllowOnConstant', false)
45
- !node.const_type?
45
+ !node&.const_type?
46
46
  else
47
47
  true
48
48
  end
@@ -90,10 +90,6 @@ module RuboCop
90
90
  compact_style? ? '; ' : "\n#{indent}"
91
91
  end
92
92
 
93
- def comment_lines?(node)
94
- processed_source[line_range(node)].any? { |line| comment_line?(line) }
95
- end
96
-
97
93
  def compact?(node)
98
94
  node.single_line?
99
95
  end
@@ -35,6 +35,17 @@ module RuboCop
35
35
  # # good
36
36
  # raise 'exception' if something
37
37
  # ok
38
+ #
39
+ # # bad
40
+ # if something
41
+ # foo || raise('exception')
42
+ # else
43
+ # ok
44
+ # end
45
+ #
46
+ # # good
47
+ # foo || raise('exception') if something
48
+ # ok
38
49
  class GuardClause < Cop
39
50
  include MinBodyLength
40
51
  include StatementModifier
@@ -69,7 +80,8 @@ module RuboCop
69
80
  else
70
81
  opposite_keyword(node)
71
82
  end
72
- register_offense(node, guard_clause.source, kw)
83
+
84
+ register_offense(node, guard_clause_source(guard_clause), kw)
73
85
  end
74
86
 
75
87
  private
@@ -98,13 +110,24 @@ module RuboCop
98
110
  message: format(MSG, example: example))
99
111
  end
100
112
 
113
+ def guard_clause_source(guard_clause)
114
+ parent = guard_clause.parent
115
+
116
+ if parent.and_type? || parent.or_type?
117
+ guard_clause.parent.source
118
+ else
119
+ guard_clause.source
120
+ end
121
+ end
122
+
101
123
  def too_long_for_single_line?(node, example)
102
124
  max = max_line_length
103
125
  max && node.source_range.column + example.length > max
104
126
  end
105
127
 
106
128
  def accepted_form?(node, ending = false)
107
- accepted_if?(node, ending) || node.condition.multiline?
129
+ accepted_if?(node, ending) || node.condition.multiline? ||
130
+ node.parent&.assignment?
108
131
  end
109
132
 
110
133
  def accepted_if?(node, ending)
@@ -19,11 +19,27 @@ module RuboCop
19
19
  MSG = 'Do not use if x; Use the ternary operator instead.'
20
20
 
21
21
  def on_normal_if_unless(node)
22
+ return unless node.else_branch
23
+
22
24
  beginning = node.loc.begin
23
25
  return unless beginning&.is?(';')
24
26
 
25
27
  add_offense(node)
26
28
  end
29
+
30
+ def autocorrect(node)
31
+ lambda do |corrector|
32
+ corrector.replace(node, correct_to_ternary(node))
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def correct_to_ternary(node)
39
+ else_code = node.else_branch ? node.else_branch.source : 'nil'
40
+
41
+ "#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
42
+ end
27
43
  end
28
44
  end
29
45
  end
@@ -52,26 +52,6 @@ module RuboCop
52
52
  implicit_style? && !node.implicit_call?
53
53
  end
54
54
 
55
- def add_parentheses(node, corrector)
56
- if node.arguments.empty?
57
- corrector.insert_after(node, '()')
58
- else
59
- corrector.replace(args_begin(node), '(')
60
- corrector.insert_after(args_end(node), ')')
61
- end
62
- end
63
-
64
- def args_begin(node)
65
- loc = node.loc
66
- selector =
67
- node.super_type? || node.yield_type? ? loc.keyword : loc.selector
68
- selector.end.resize(1)
69
- end
70
-
71
- def args_end(node)
72
- node.loc.expression.end
73
- end
74
-
75
55
  def message(_node)
76
56
  if explicit_style?
77
57
  'Prefer the use of `lambda.call(...)` over `lambda.(...)`.'
@@ -22,6 +22,12 @@ module RuboCop
22
22
  # when bar then do_something
23
23
  # end
24
24
  #
25
+ # # good
26
+ # case foo
27
+ # when bar then do_something(arg1,
28
+ # arg2)
29
+ # end
30
+ #
25
31
  class MultilineWhenThen < Cop
26
32
  include RangeHelp
27
33
 
@@ -32,7 +38,10 @@ module RuboCop
32
38
  return unless node.then?
33
39
 
34
40
  # Single line usage of `then` is not an offense
35
- return if !node.children.last.nil? && !node.multiline? && node.then?
41
+ return if !node.children.last.nil? && !node.multiline?
42
+
43
+ # Requires `then` for write `when` and its body on the same line.
44
+ return if require_then?(node)
36
45
 
37
46
  # With more than one statements after then, there's not offense
38
47
  return if accept_node_type?(node.body)
@@ -50,6 +59,12 @@ module RuboCop
50
59
  end
51
60
  end
52
61
 
62
+ def require_then?(when_node)
63
+ return false unless when_node.body
64
+
65
+ when_node.loc.line == when_node.body.loc.line
66
+ end
67
+
53
68
  def accept_node_type?(node)
54
69
  node&.begin_type? || node&.array_type? || node&.hash_type?
55
70
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks for optional arguments to methods
7
- # that do not come at the end of the argument list
7
+ # that do not come at the end of the argument list.
8
8
  #
9
9
  # @example
10
10
  # # bad
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks that arrays are sliced with endless ranges instead of
7
+ # `ary[start..-1]` on Ruby 2.6+.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # items[1..-1]
12
+ #
13
+ # # good
14
+ # items[1..]
15
+ class SlicingWithRange < Cop
16
+ extend TargetRubyVersion
17
+
18
+ minimum_target_ruby_version 2.6
19
+
20
+ MSG = 'Prefer ary[n..] over ary[n..-1].'
21
+
22
+ def_node_matcher :range_till_minus_one?, '(irange (int _) (int -1))'
23
+
24
+ def on_send(node)
25
+ return unless node.method?(:[]) && node.arguments.count == 1
26
+ return unless range_till_minus_one?(node.arguments.first)
27
+
28
+ add_offense(node.arguments.first)
29
+ end
30
+
31
+ def autocorrect(node)
32
+ lambda do |corrector|
33
+ corrector.remove(node.end)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -17,6 +17,10 @@ module RuboCop
17
17
  line_source =~ /^\s*#/
18
18
  end
19
19
 
20
+ def comment_lines?(node)
21
+ processed_source[line_range(node)].any? { |line| comment_line?(line) }
22
+ end
23
+
20
24
  def line_range(node)
21
25
  node.first_line..node.last_line
22
26
  end
@@ -26,6 +30,26 @@ module RuboCop
26
30
  node.loc.end.is?(')')
27
31
  end
28
32
 
33
+ def add_parentheses(node, corrector)
34
+ if node.arguments.empty?
35
+ corrector.insert_after(node, '()')
36
+ else
37
+ corrector.replace(args_begin(node), '(')
38
+ corrector.insert_after(args_end(node), ')')
39
+ end
40
+ end
41
+
42
+ def args_begin(node)
43
+ loc = node.loc
44
+ selector =
45
+ node.super_type? || node.yield_type? ? loc.keyword : loc.selector
46
+ selector.end.resize(1)
47
+ end
48
+
49
+ def args_end(node)
50
+ node.loc.expression.end
51
+ end
52
+
29
53
  def on_node(syms, sexp, excludes = [], &block)
30
54
  return to_enum(:on_node, syms, sexp, excludes) unless block_given?
31
55