rubocop 0.87.1 → 0.88.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/rubocop-profile +31 -0
  4. data/config/default.yml +57 -6
  5. data/lib/rubocop.rb +6 -0
  6. data/lib/rubocop/cli.rb +2 -2
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
  8. data/lib/rubocop/config_loader.rb +20 -7
  9. data/lib/rubocop/config_store.rb +4 -0
  10. data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
  11. data/lib/rubocop/cop/badge.rb +1 -1
  12. data/lib/rubocop/cop/base.rb +12 -4
  13. data/lib/rubocop/cop/cop.rb +1 -1
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +26 -0
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +6 -1
  16. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  17. data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
  18. data/lib/rubocop/cop/layout/multiline_block_layout.rb +16 -5
  19. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +3 -2
  20. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +27 -68
  21. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +3 -2
  22. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +8 -2
  23. data/lib/rubocop/cop/lint/duplicate_elsif_condition.rb +39 -0
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
  25. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +3 -2
  26. data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -1
  27. data/lib/rubocop/cop/lint/nested_method_definition.rb +13 -19
  28. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +67 -0
  29. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -3
  30. data/lib/rubocop/cop/style/accessor_grouping.rb +8 -1
  31. data/lib/rubocop/cop/style/array_coercion.rb +63 -0
  32. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +3 -2
  33. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +5 -4
  34. data/lib/rubocop/cop/style/case_like_if.rb +217 -0
  35. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  36. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  37. data/lib/rubocop/cop/style/exponential_notation.rb +6 -8
  38. data/lib/rubocop/cop/style/float_division.rb +7 -10
  39. data/lib/rubocop/cop/style/format_string_token.rb +5 -5
  40. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +62 -0
  41. data/lib/rubocop/cop/style/hash_like_case.rb +76 -0
  42. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -11
  43. data/lib/rubocop/cop/style/missing_else.rb +1 -11
  44. data/lib/rubocop/cop/style/numeric_predicate.rb +3 -4
  45. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -3
  46. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  47. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +50 -0
  48. data/lib/rubocop/cop/style/redundant_sort.rb +3 -2
  49. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +3 -2
  50. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +9 -32
  51. data/lib/rubocop/cop/variable_force/variable.rb +5 -3
  52. data/lib/rubocop/file_finder.rb +12 -12
  53. data/lib/rubocop/path_util.rb +2 -17
  54. data/lib/rubocop/result_cache.rb +12 -8
  55. data/lib/rubocop/rspec/expect_offense.rb +31 -5
  56. data/lib/rubocop/rspec/shared_contexts.rb +12 -9
  57. data/lib/rubocop/runner.rb +5 -6
  58. data/lib/rubocop/target_finder.rb +2 -2
  59. data/lib/rubocop/version.rb +1 -1
  60. metadata +9 -2
@@ -150,9 +150,10 @@ module RuboCop
150
150
  end
151
151
 
152
152
  def alignment_node(node)
153
- if style == :keyword
153
+ case style
154
+ when :keyword
154
155
  node
155
- elsif style == :variable
156
+ when :variable
156
157
  alignment_node_for_variable_style(node)
157
158
  else
158
159
  start_line_range(node)
@@ -95,11 +95,22 @@ module RuboCop
95
95
  end
96
96
 
97
97
  def line_break_necessary_in_args?(node)
98
- needed_length = node.source_range.column +
99
- node.source.lines.first.length +
100
- block_arg_string(node, node.arguments).length +
101
- PIPE_SIZE
102
- needed_length > max_line_length
98
+ needed_length_for_args(node) > max_line_length
99
+ end
100
+
101
+ def needed_length_for_args(node)
102
+ node.source_range.column +
103
+ characters_needed_for_space_and_pipes(node) +
104
+ node.source.lines.first.chomp.length +
105
+ block_arg_string(node, node.arguments).length
106
+ end
107
+
108
+ def characters_needed_for_space_and_pipes(node)
109
+ if node.source.lines.first.end_with?("|\n")
110
+ PIPE_SIZE
111
+ else
112
+ 1 + PIPE_SIZE * 2
113
+ end
103
114
  end
104
115
 
105
116
  def add_offense_for_expression(node, expr, msg)
@@ -56,11 +56,12 @@ module RuboCop
56
56
  def check_inside_pipes(arguments)
57
57
  opening_pipe, closing_pipe = pipes(arguments)
58
58
 
59
- if style == :no_space
59
+ case style
60
+ when :no_space
60
61
  check_no_space_style_inside_pipes(arguments.children,
61
62
  opening_pipe,
62
63
  closing_pipe)
63
- elsif style == :space
64
+ when :space
64
65
  check_space_style_inside_pipes(arguments.children,
65
66
  opening_pipe,
66
67
  closing_pipe)
@@ -34,96 +34,55 @@ module RuboCop
34
34
  # RuboCop::Cop::Cop
35
35
  # ::RuboCop::Cop
36
36
  #
37
- class SpaceAroundMethodCallOperator < Cop
38
- include SurroundingSpace
37
+ class SpaceAroundMethodCallOperator < Base
38
+ include RangeHelp
39
+ extend AutoCorrector
40
+
41
+ SPACES_REGEXP = /\A[ \t]+\z/.freeze
39
42
 
40
43
  MSG = 'Avoid using spaces around a method call operator.'
41
44
 
42
45
  def on_send(node)
43
- return unless dot_or_safe_navigation_operator?(node)
46
+ return unless node.dot? || node.safe_navigation?
44
47
 
45
- check_and_add_offense(node)
48
+ check_space_before_dot(node)
49
+ check_space_after_dot(node)
46
50
  end
51
+ alias on_csend on_send
47
52
 
48
53
  def on_const(node)
49
54
  return unless node.loc.double_colon
50
55
 
51
- check_and_add_offense(node, false)
52
- end
53
-
54
- def autocorrect(node)
55
- operator = operator_token(node)
56
- left = left_token_for_auto_correction(node, operator)
57
- right = right_token_for_auto_correction(operator)
58
-
59
- lambda do |corrector|
60
- SpaceCorrector.remove_space(
61
- processed_source, corrector, left, right
62
- )
63
- end
56
+ check_space_after_double_colon(node)
64
57
  end
65
58
 
66
- alias on_csend on_send
67
-
68
59
  private
69
60
 
70
- def check_and_add_offense(node, add_left_offense = true)
71
- operator = operator_token(node)
72
- left = previous_token(operator)
73
- right = next_token(operator)
74
-
75
- if !right.comment? && valid_right_token?(right, operator)
76
- no_space_offenses(node, operator, right, MSG)
77
- end
78
- return unless valid_left_token?(left, operator)
79
-
80
- no_space_offenses(node, left, operator, MSG) if add_left_offense
81
- end
82
-
83
- def operator_token(node)
84
- operator_location =
85
- node.const_type? ? node.loc.double_colon : node.loc.dot
86
-
87
- processed_source.find_token do |token|
88
- token.pos == operator_location
89
- end
90
- end
91
-
92
- def previous_token(current_token)
93
- index = processed_source.tokens.index(current_token)
94
- index.zero? ? nil : processed_source.tokens[index - 1]
95
- end
96
-
97
- def next_token(current_token)
98
- index = processed_source.tokens.index(current_token)
99
- processed_source.tokens[index + 1]
100
- end
101
-
102
- def dot_or_safe_navigation_operator?(node)
103
- node.dot? || node.safe_navigation?
61
+ def check_space_before_dot(node)
62
+ receiver_pos = node.receiver.source_range.end_pos
63
+ dot_pos = node.loc.dot.begin_pos
64
+ check_space(receiver_pos, dot_pos)
104
65
  end
105
66
 
106
- def valid_left_token?(left, operator)
107
- left && left.line == operator.line
67
+ def check_space_after_dot(node)
68
+ dot_pos = node.loc.dot.end_pos
69
+ selector_pos = node.loc.selector.begin_pos
70
+ check_space(dot_pos, selector_pos)
108
71
  end
109
72
 
110
- def valid_right_token?(right, operator)
111
- right && right.line == operator.line
73
+ def check_space_after_double_colon(node)
74
+ double_colon_pos = node.loc.double_colon.end_pos
75
+ name_pos = node.loc.name.begin_pos
76
+ check_space(double_colon_pos, name_pos)
112
77
  end
113
78
 
114
- def left_token_for_auto_correction(node, operator)
115
- left_token = previous_token(operator)
116
- return operator if node.const_type?
117
- return left_token if valid_left_token?(left_token, operator)
118
-
119
- operator
120
- end
79
+ def check_space(begin_pos, end_pos)
80
+ return if end_pos <= begin_pos
121
81
 
122
- def right_token_for_auto_correction(operator)
123
- right_token = next_token(operator)
124
- return right_token if !right_token.comment? && valid_right_token?(right_token, operator)
82
+ range = range_between(begin_pos, end_pos)
83
+ return unless range.source.match?(SPACES_REGEXP)
125
84
 
126
- operator
85
+ add_offense(range) { |corrector| corrector.remove(range) }
127
86
  end
128
87
  end
129
88
  end
@@ -142,11 +142,12 @@ module RuboCop
142
142
  end
143
143
 
144
144
  def issue_offenses(node, left, right, start_ok, end_ok)
145
- if style == :no_space
145
+ case style
146
+ when :no_space
146
147
  start_ok = next_to_comment?(node, left)
147
148
  no_space_offenses(node, left, right, MSG, start_ok: start_ok,
148
149
  end_ok: end_ok)
149
- elsif style == :space
150
+ when :space
150
151
  space_offenses(node, left, right, MSG, start_ok: start_ok,
151
152
  end_ok: end_ok)
152
153
  else
@@ -22,7 +22,9 @@ module RuboCop
22
22
  # def initialize
23
23
  # @x = 1
24
24
  # end
25
- class DisjunctiveAssignmentInConstructor < Cop
25
+ class DisjunctiveAssignmentInConstructor < Base
26
+ extend AutoCorrector
27
+
26
28
  MSG = 'Unnecessary disjunctive assignment. Use plain assignment.'
27
29
 
28
30
  def on_def(node)
@@ -73,7 +75,11 @@ module RuboCop
73
75
  # @param [Node] node a disjunctive assignment
74
76
  def check_disjunctive_assignment(node)
75
77
  lhs = node.child_nodes.first
76
- add_offense(node, location: :operator) if lhs.ivasgn_type?
78
+ return unless lhs.ivasgn_type?
79
+
80
+ add_offense(node.loc.operator) do |corrector|
81
+ corrector.replace(node.loc.operator, '=')
82
+ end
77
83
  end
78
84
  end
79
85
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks that there are no repeated conditions used in if 'elsif'.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # if x == 1
11
+ # do_something
12
+ # elsif x == 1
13
+ # do_something_else
14
+ # end
15
+ #
16
+ # # good
17
+ # if x == 1
18
+ # do_something
19
+ # elsif x == 2
20
+ # do_something_else
21
+ # end
22
+ #
23
+ class DuplicateElsifCondition < Base
24
+ MSG = 'Duplicate `elsif` condition detected.'
25
+
26
+ def on_if(node)
27
+ previous = []
28
+ while node.if? || node.elsif?
29
+ condition = node.condition
30
+ add_offense(condition) if previous.include?(condition)
31
+ previous << condition
32
+ node = node.else_branch
33
+ break unless node&.if_type?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -126,7 +126,7 @@ module RuboCop
126
126
  end
127
127
 
128
128
  def message_for_dup(node, method_name)
129
- format(MSG, method: method_name, defined: @definitions[method_name],
129
+ format(MSG, method: method_name, defined: source_location(@definitions[method_name]),
130
130
  current: source_location(node))
131
131
  end
132
132
 
@@ -152,7 +152,7 @@ module RuboCop
152
152
 
153
153
  add_offense(node, location: loc, message: message)
154
154
  else
155
- @definitions[method_name] = source_location(node)
155
+ @definitions[method_name] = node
156
156
  end
157
157
  end
158
158
 
@@ -64,9 +64,10 @@ module RuboCop
64
64
 
65
65
  def ending_delimiter(str)
66
66
  # implicit string concatenation does not work with %{}, etc.
67
- if str.source[0] == "'"
67
+ case str.source[0]
68
+ when "'"
68
69
  "'"
69
- elsif str.source[0] == '"'
70
+ when '"'
70
71
  '"'
71
72
  end
72
73
  end
@@ -30,6 +30,8 @@ module RuboCop
30
30
  # break if condition
31
31
  # end
32
32
  class LiteralAsCondition < Cop
33
+ include RangeHelp
34
+
33
35
  MSG = 'Literal `%<literal>s` appeared as a condition.'
34
36
 
35
37
  def on_if(node)
@@ -57,7 +59,8 @@ module RuboCop
57
59
  case_node.each_when do |when_node|
58
60
  next unless when_node.conditions.all?(&:literal?)
59
61
 
60
- add_offense(when_node)
62
+ range = when_conditions_range(when_node)
63
+ add_offense(when_node, location: range, message: message(range))
61
64
  end
62
65
  end
63
66
  end
@@ -129,6 +132,13 @@ module RuboCop
129
132
  node.condition
130
133
  end
131
134
  end
135
+
136
+ def when_conditions_range(when_node)
137
+ range_between(
138
+ when_node.conditions.first.source_range.begin_pos,
139
+ when_node.conditions.last.source_range.end_pos
140
+ )
141
+ end
132
142
  end
133
143
  end
134
144
  end
@@ -59,31 +59,25 @@ module RuboCop
59
59
  'Use `lambda` instead.'
60
60
 
61
61
  def on_def(node)
62
- find_nested_defs(node) do |nested_def_node|
63
- add_offense(nested_def_node)
64
- end
65
- end
66
- alias on_defs on_def
67
-
68
- private
62
+ subject, = *node
63
+ return if node.defs_type? && subject.lvar_type?
69
64
 
70
- def find_nested_defs(node, &block)
71
- node.each_child_node do |child|
72
- if child.def_type?
73
- yield child
74
- elsif child.defs_type?
75
- subject, = *child
76
- next if subject.lvar_type?
65
+ def_ancestor = node.each_ancestor(:def, :defs).first
66
+ return unless def_ancestor
77
67
 
78
- yield child
79
- elsif !scoping_method_call?(child)
80
- find_nested_defs(child, &block)
68
+ within_scoping_def =
69
+ node.each_ancestor(:block, :sclass).any? do |ancestor|
70
+ scoping_method_call?(ancestor)
81
71
  end
82
- end
72
+
73
+ add_offense(node) if def_ancestor && !within_scoping_def
83
74
  end
75
+ alias on_defs on_def
76
+
77
+ private
84
78
 
85
79
  def scoping_method_call?(child)
86
- eval_call?(child) || exec_call?(child) || child.sclass_type? ||
80
+ child.sclass_type? || eval_call?(child) || exec_call?(child) ||
87
81
  class_or_module_or_struct_new_call?(child)
88
82
  end
89
83
 
@@ -35,6 +35,22 @@ module RuboCop
35
35
  # require file
36
36
  # end
37
37
  #
38
+ # @example
39
+ #
40
+ # # bad
41
+ # Dir['./lib/**/*.rb'].each(&method(:require))
42
+ #
43
+ # # good
44
+ # Dir['./lib/**/*.rb'].sort.each(&method(:require))
45
+ #
46
+ # @example
47
+ #
48
+ # # bad
49
+ # Dir.glob(Rails.root.join('test', '*.rb'), &method(:require))
50
+ #
51
+ # # good
52
+ # Dir.glob(Rails.root.join('test', '*.rb')).sort.each(&method(:require))
53
+ #
38
54
  class NonDeterministicRequireOrder < Cop
39
55
  MSG = 'Sort files before requiring them.'
40
56
 
@@ -49,7 +65,16 @@ module RuboCop
49
65
  end
50
66
  end
51
67
 
68
+ def on_block_pass(node)
69
+ return unless method_require?(node)
70
+ return unless unsorted_dir_pass?(node.parent)
71
+
72
+ add_offense(node.parent)
73
+ end
74
+
52
75
  def autocorrect(node)
76
+ return correct_block_pass(node) if node.arguments.last&.block_pass_type?
77
+
53
78
  if unsorted_dir_block?(node)
54
79
  lambda do |corrector|
55
80
  corrector.replace(node, "#{node.source}.sort.each")
@@ -64,10 +89,38 @@ module RuboCop
64
89
 
65
90
  private
66
91
 
92
+ def correct_block_pass(node)
93
+ if unsorted_dir_glob_pass?(node)
94
+ lambda do |corrector|
95
+ block_arg = node.arguments.last
96
+ corrector.remove(last_arg_range(node))
97
+ corrector.insert_after(node, ".sort.each(#{block_arg.source})")
98
+ end
99
+ else
100
+ lambda do |corrector|
101
+ corrector.replace(node.loc.selector, 'sort.each')
102
+ end
103
+ end
104
+ end
105
+
106
+ # Returns range of last argument including comma and whitespace.
107
+ #
108
+ # @return [Parser::Source::Range]
109
+ #
110
+ def last_arg_range(node)
111
+ node.arguments.last.source_range.with(
112
+ begin_pos: node.arguments[-2].source_range.end_pos
113
+ )
114
+ end
115
+
67
116
  def unsorted_dir_loop?(node)
68
117
  unsorted_dir_block?(node) || unsorted_dir_each?(node)
69
118
  end
70
119
 
120
+ def unsorted_dir_pass?(node)
121
+ unsorted_dir_glob_pass?(node) || unsorted_dir_each_pass?(node)
122
+ end
123
+
71
124
  def_node_matcher :unsorted_dir_block?, <<~PATTERN
72
125
  (send (const {nil? cbase} :Dir) :glob ...)
73
126
  PATTERN
@@ -76,6 +129,20 @@ module RuboCop
76
129
  (send (send (const {nil? cbase} :Dir) {:[] :glob} ...) :each)
77
130
  PATTERN
78
131
 
132
+ def_node_matcher :method_require?, <<~PATTERN
133
+ (block-pass (send nil? :method (sym :require)))
134
+ PATTERN
135
+
136
+ def_node_matcher :unsorted_dir_glob_pass?, <<~PATTERN
137
+ (send (const {nil? cbase} :Dir) :glob ...
138
+ (block-pass (send nil? :method (sym :require))))
139
+ PATTERN
140
+
141
+ def_node_matcher :unsorted_dir_each_pass?, <<~PATTERN
142
+ (send (send (const {nil? cbase} :Dir) {:[] :glob} ...) :each
143
+ (block-pass (send nil? :method (sym :require))))
144
+ PATTERN
145
+
79
146
  def_node_matcher :loop_variable, <<~PATTERN
80
147
  (args (arg $_))
81
148
  PATTERN