rubocop 1.13.0 → 1.17.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +68 -8
  4. data/lib/rubocop.rb +9 -0
  5. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -3
  6. data/lib/rubocop/cop/bundler/gem_version.rb +99 -0
  7. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  8. data/lib/rubocop/cop/layout/argument_alignment.rb +29 -11
  9. data/lib/rubocop/cop/layout/case_indentation.rb +57 -9
  10. data/lib/rubocop/cop/layout/dot_position.rb +7 -1
  11. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +13 -15
  12. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +12 -0
  13. data/lib/rubocop/cop/layout/hash_alignment.rb +34 -9
  14. data/lib/rubocop/cop/layout/indentation_width.rb +13 -2
  15. data/lib/rubocop/cop/layout/redundant_line_break.rb +24 -10
  16. data/lib/rubocop/cop/layout/single_line_block_chain.rb +53 -0
  17. data/lib/rubocop/cop/layout/space_around_keyword.rb +28 -0
  18. data/lib/rubocop/cop/layout/space_around_operators.rb +6 -0
  19. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +83 -39
  20. data/lib/rubocop/cop/lint/empty_block.rb +18 -2
  21. data/lib/rubocop/cop/lint/empty_in_pattern.rb +62 -0
  22. data/lib/rubocop/cop/lint/literal_as_condition.rb +13 -1
  23. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +32 -17
  24. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +105 -74
  26. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -0
  27. data/lib/rubocop/cop/lint/symbol_conversion.rb +2 -12
  28. data/lib/rubocop/cop/lint/unreachable_loop.rb +12 -2
  29. data/lib/rubocop/cop/lint/unused_block_argument.rb +7 -1
  30. data/lib/rubocop/cop/lint/void.rb +1 -1
  31. data/lib/rubocop/cop/migration/department_name.rb +3 -1
  32. data/lib/rubocop/cop/mixin/check_line_breakable.rb +19 -3
  33. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
  34. data/lib/rubocop/cop/mixin/gem_declaration.rb +13 -0
  35. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +14 -3
  36. data/lib/rubocop/cop/mixin/string_literals_help.rb +3 -5
  37. data/lib/rubocop/cop/mixin/symbol_help.rb +13 -0
  38. data/lib/rubocop/cop/style/class_and_module_children.rb +17 -5
  39. data/lib/rubocop/cop/style/empty_literal.rb +8 -1
  40. data/lib/rubocop/cop/style/hash_each_methods.rb +18 -1
  41. data/lib/rubocop/cop/style/identical_conditional_branches.rb +58 -8
  42. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -4
  43. data/lib/rubocop/cop/style/in_pattern_then.rb +56 -0
  44. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -1
  45. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +62 -0
  46. data/lib/rubocop/cop/style/multiline_when_then.rb +2 -11
  47. data/lib/rubocop/cop/style/negated_if_else_condition.rb +17 -9
  48. data/lib/rubocop/cop/style/nil_lambda.rb +29 -12
  49. data/lib/rubocop/cop/style/quoted_symbols.rb +110 -0
  50. data/lib/rubocop/cop/style/raise_args.rb +2 -0
  51. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  52. data/lib/rubocop/cop/style/redundant_self.rb +24 -2
  53. data/lib/rubocop/cop/style/regexp_literal.rb +9 -1
  54. data/lib/rubocop/cop/style/single_line_methods.rb +8 -3
  55. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -5
  56. data/lib/rubocop/cop/style/string_literals.rb +1 -0
  57. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +1 -0
  58. data/lib/rubocop/cop/style/top_level_method_definition.rb +83 -0
  59. data/lib/rubocop/cop/style/trivial_accessors.rb +65 -0
  60. data/lib/rubocop/cop/style/when_then.rb +6 -2
  61. data/lib/rubocop/cop/variable_force/branch.rb +15 -0
  62. data/lib/rubocop/directive_comment.rb +58 -6
  63. data/lib/rubocop/formatter/junit_formatter.rb +21 -6
  64. data/lib/rubocop/options.rb +14 -20
  65. data/lib/rubocop/rake_task.rb +1 -1
  66. data/lib/rubocop/remote_config.rb +10 -2
  67. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  68. data/lib/rubocop/target_finder.rb +9 -2
  69. data/lib/rubocop/target_ruby.rb +1 -1
  70. data/lib/rubocop/version.rb +1 -1
  71. metadata +18 -9
@@ -35,6 +35,12 @@ module RuboCop
35
35
  leading_comment_lines.any? { |line| MagicComment.parse(line).frozen_string_literal? }
36
36
  end
37
37
 
38
+ def frozen_string_literals_disabled?
39
+ leading_comment_lines.any? do |line|
40
+ MagicComment.parse(line).frozen_string_literal == false
41
+ end
42
+ end
43
+
38
44
  def frozen_string_literal_specified?
39
45
  leading_comment_lines.any? do |line|
40
46
  MagicComment.parse(line).frozen_string_literal_specified?
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for checking gem declarations.
6
+ module GemDeclaration
7
+ extend NodePattern::Macros
8
+
9
+ # @!method gem_declaration?(node)
10
+ def_node_matcher :gem_declaration?, '(send nil? :gem str ...)'
11
+ end
12
+ end
13
+ end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  end
44
44
 
45
45
  def value_delta(pair)
46
- return 0 if pair.kwsplat_type? || pair.value_on_new_line?
46
+ return 0 if pair.value_on_new_line?
47
47
 
48
48
  correct_value_column = pair.loc.operator.end.column + 1
49
49
  actual_value_column = pair.value.loc.column
@@ -108,8 +108,6 @@ module RuboCop
108
108
  end
109
109
 
110
110
  def value_delta(first_pair, current_pair)
111
- return 0 if current_pair.kwsplat_type?
112
-
113
111
  correct_value_column = first_pair.key.loc.column +
114
112
  current_pair.delimiter(true).length +
115
113
  max_key_width
@@ -139,6 +137,19 @@ module RuboCop
139
137
  first_pair.value_delta(current_pair)
140
138
  end
141
139
  end
140
+
141
+ # Handles calculation of deltas for `kwsplat` nodes.
142
+ # This is a special case that just ensures the kwsplat is aligned with the rest of the hash
143
+ # since a `kwsplat` does not have a key, separator or value.
144
+ class KeywordSplatAlignment
145
+ def deltas(first_pair, current_pair)
146
+ if Util.begins_its_line?(current_pair.source_range)
147
+ { key: first_pair.key_delta(current_pair) }
148
+ else
149
+ {}
150
+ end
151
+ end
152
+ end
142
153
  end
143
154
  end
144
155
  end
@@ -4,18 +4,16 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for cops checking single/double quotes.
6
6
  module StringLiteralsHelp
7
- include StringHelp
8
-
9
7
  private
10
8
 
11
- def wrong_quotes?(node)
12
- src = node.source
9
+ def wrong_quotes?(src_or_node)
10
+ src = src_or_node.is_a?(RuboCop::AST::Node) ? src_or_node.source : src_or_node
13
11
  return false if src.start_with?('%', '?')
14
12
 
15
13
  if style == :single_quotes
16
14
  !double_quotes_required?(src)
17
15
  else
18
- !/" | \\[^'] | \#(@|\{)/x.match?(src)
16
+ !/" | \\[^'\\] | \#[@{$]/x.match?(src)
19
17
  end
20
18
  end
21
19
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Classes that include this module just implement functions for working
6
+ # with symbol nodes.
7
+ module SymbolHelp
8
+ def hash_key?(node)
9
+ node.parent&.pair_type? && node == node.parent.child_nodes.first
10
+ end
11
+ end
12
+ end
13
+ end
@@ -87,9 +87,18 @@ module RuboCop
87
87
  end
88
88
 
89
89
  def compact_node(corrector, node)
90
- replacement = "#{node.body.type} #{compact_identifier_name(node)}"
91
90
  range = range_between(node.loc.keyword.begin_pos, node.body.loc.name.end_pos)
92
- corrector.replace(range, replacement)
91
+ corrector.replace(range, compact_replacement(node))
92
+ end
93
+
94
+ def compact_replacement(node)
95
+ replacement = "#{node.body.type} #{compact_identifier_name(node)}"
96
+
97
+ body_comments = processed_source.ast_with_comments[node.body]
98
+ unless body_comments.empty?
99
+ replacement = body_comments.map(&:text).push(replacement).join("\n")
100
+ end
101
+ replacement
93
102
  end
94
103
 
95
104
  def compact_identifier_name(node)
@@ -132,7 +141,10 @@ module RuboCop
132
141
  end
133
142
 
134
143
  def check_compact_style(node, body)
135
- return unless one_child?(body) && !compact_node_name?(node)
144
+ parent = node.parent
145
+ return if parent&.class_type? || parent&.module_type?
146
+
147
+ return unless needs_compacting?(body)
136
148
 
137
149
  add_offense(node.loc.name, message: COMPACT_MSG) do |corrector|
138
150
  autocorrect(corrector, node)
@@ -145,12 +157,12 @@ module RuboCop
145
157
  nest_or_compact(corrector, node)
146
158
  end
147
159
 
148
- def one_child?(body)
160
+ def needs_compacting?(body)
149
161
  body && %i[module class].include?(body.type)
150
162
  end
151
163
 
152
164
  def compact_node_name?(node)
153
- /::/.match?(node.loc.name.source)
165
+ /::/.match?(node.identifier.source)
154
166
  end
155
167
  end
156
168
  end
@@ -62,7 +62,7 @@ module RuboCop
62
62
  ARR_MSG
63
63
  elsif offense_hash_node?(node)
64
64
  HASH_MSG
65
- elsif str_node(node) && !frozen_string_literals_enabled?
65
+ elsif str_node(node) && !frozen_strings?
66
66
  format(STR_MSG, prefer: preferred_string_literal)
67
67
  end
68
68
  end
@@ -125,6 +125,13 @@ module RuboCop
125
125
  end
126
126
  end
127
127
  end
128
+
129
+ def frozen_strings?
130
+ return true if frozen_string_literals_enabled?
131
+
132
+ frozen_string_cop_enabled = config.for_cop('Style/FrozenStringLiteral')['Enabled']
133
+ frozen_string_cop_enabled && !frozen_string_literals_disabled?
134
+ end
128
135
  end
129
136
  end
130
137
  end
@@ -17,6 +17,11 @@ module RuboCop
17
17
  # # good
18
18
  # hash.each_key { |k| p k }
19
19
  # hash.each_value { |v| p v }
20
+ #
21
+ # @example AllowedReceivers: ['execute']
22
+ # # good
23
+ # execute(sql).keys.each { |v| p v }
24
+ # execute(sql).values.each { |v| p v }
20
25
  class HashEachMethods < Base
21
26
  include Lint::UnusedArgument
22
27
  extend AutoCorrector
@@ -36,7 +41,9 @@ module RuboCop
36
41
 
37
42
  def register_kv_offense(node)
38
43
  kv_each(node) do |target, method|
39
- return unless target.receiver.receiver
44
+ parent_receiver = target.receiver.receiver
45
+ return unless parent_receiver
46
+ return if allowed_receiver?(parent_receiver)
40
47
 
41
48
  msg = format(message, prefer: "each_#{method[0..-2]}", current: "#{method}.each")
42
49
 
@@ -80,6 +87,16 @@ module RuboCop
80
87
  def kv_range(outer_node)
81
88
  outer_node.receiver.loc.selector.join(outer_node.loc.selector)
82
89
  end
90
+
91
+ def allowed_receiver?(receiver)
92
+ receiver_name = receiver.send_type? ? receiver.method_name.to_s : receiver.source
93
+
94
+ allowed_receivers.include?(receiver_name)
95
+ end
96
+
97
+ def allowed_receivers
98
+ cop_config.fetch('AllowedReceivers', [])
99
+ end
83
100
  end
84
101
  end
85
102
  end
@@ -67,40 +67,90 @@ module RuboCop
67
67
  # do_x
68
68
  # do_z
69
69
  # end
70
+ #
71
+ # # bad
72
+ # case foo
73
+ # in 1
74
+ # do_x
75
+ # in 2
76
+ # do_x
77
+ # else
78
+ # do_x
79
+ # end
80
+ #
81
+ # # good
82
+ # case foo
83
+ # in 1
84
+ # do_x
85
+ # do_y
86
+ # in 2
87
+ # # nothing
88
+ # else
89
+ # do_x
90
+ # do_z
91
+ # end
70
92
  class IdenticalConditionalBranches < Base
93
+ include RangeHelp
94
+ extend AutoCorrector
95
+
71
96
  MSG = 'Move `%<source>s` out of the conditional.'
72
97
 
73
98
  def on_if(node)
74
99
  return if node.elsif?
75
100
 
76
101
  branches = expand_elses(node.else_branch).unshift(node.if_branch)
77
- check_branches(branches)
102
+ check_branches(node, branches)
78
103
  end
79
104
 
80
105
  def on_case(node)
81
106
  return unless node.else? && node.else_branch
82
107
 
83
108
  branches = node.when_branches.map(&:body).push(node.else_branch)
84
- check_branches(branches)
109
+ check_branches(node, branches)
110
+ end
111
+
112
+ def on_case_match(node)
113
+ return unless node.else? && node.else_branch
114
+
115
+ branches = node.in_pattern_branches.map(&:body).push(node.else_branch)
116
+ check_branches(node, branches)
85
117
  end
86
118
 
87
119
  private
88
120
 
89
- def check_branches(branches) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
121
+ def check_branches(node, branches)
90
122
  # return if any branch is empty. An empty branch can be an `if`
91
123
  # without an `else` or a branch that contains only comments.
92
124
  return if branches.any?(&:nil?)
93
125
 
94
126
  tails = branches.map { |branch| tail(branch) }
95
- check_expressions(tails) if tails.none?(&:nil?)
127
+ check_expressions(node, tails, :after_condition) if duplicated_expressions?(tails)
128
+
96
129
  heads = branches.map { |branch| head(branch) }
97
- check_expressions(heads) if tails.none?(&:nil?)
130
+ check_expressions(node, heads, :before_condition) if duplicated_expressions?(heads)
98
131
  end
99
132
 
100
- def check_expressions(expressions)
101
- return unless expressions.size > 1 && expressions.uniq.one?
133
+ def duplicated_expressions?(expressions)
134
+ expressions.size > 1 && expressions.uniq.one?
135
+ end
102
136
 
103
- expressions.each { |expression| add_offense(expression) }
137
+ def check_expressions(node, expressions, insert_position)
138
+ inserted_expression = false
139
+
140
+ expressions.each do |expression|
141
+ add_offense(expression) do |corrector|
142
+ range = range_by_whole_lines(expression.source_range, include_final_newline: true)
143
+ corrector.remove(range)
144
+ next if inserted_expression
145
+
146
+ if insert_position == :after_condition
147
+ corrector.insert_after(node, "\n#{expression.source}")
148
+ else
149
+ corrector.insert_before(node, "#{expression.source}\n")
150
+ end
151
+ inserted_expression = true
152
+ end
153
+ end
104
154
  end
105
155
 
106
156
  def message(node)
@@ -67,15 +67,14 @@ module RuboCop
67
67
 
68
68
  def autocorrect(corrector, node)
69
69
  replacement = if node.modifier_form?
70
- indentation = ' ' * node.source_range.column
71
- last_argument = node.if_branch.last_argument
70
+ last_argument = node.if_branch.last_argument if node.if_branch.send_type?
72
71
 
73
72
  if last_argument.respond_to?(:heredoc?) && last_argument.heredoc?
74
73
  heredoc = extract_heredoc_from(last_argument)
75
74
  remove_heredoc(corrector, heredoc)
76
- to_normal_form_with_heredoc(node, indentation, heredoc)
75
+ to_normal_form_with_heredoc(node, indent(node), heredoc)
77
76
  else
78
- to_normal_form(node, indentation)
77
+ to_normal_form(node, indent(node))
79
78
  end
80
79
  else
81
80
  to_modifier_form(node)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for `in;` uses in `case` expressions.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # case expression
11
+ # in pattern_a; foo
12
+ # in pattern_b; bar
13
+ # end
14
+ #
15
+ # # good
16
+ # case expression
17
+ # in pattern_a then foo
18
+ # in pattern_b then bar
19
+ # end
20
+ #
21
+ class InPatternThen < Base
22
+ extend AutoCorrector
23
+ extend TargetRubyVersion
24
+
25
+ minimum_target_ruby_version 2.7
26
+
27
+ MSG = 'Do not use `in %<pattern>s;`. Use `in %<pattern>s then` instead.'
28
+
29
+ def on_in_pattern(node)
30
+ return if node.multiline? || node.then? || !node.body
31
+
32
+ pattern = node.pattern
33
+ pattern_source = if pattern.match_alt_type?
34
+ alternative_pattern_source(pattern)
35
+ else
36
+ pattern.source
37
+ end
38
+
39
+ add_offense(node.loc.begin, message: format(MSG, pattern: pattern_source)) do |corrector|
40
+ corrector.replace(node.loc.begin, ' then')
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def alternative_pattern_source(pattern)
47
+ return pattern.children.map(&:source) unless pattern.children.first.match_alt_type?
48
+
49
+ pattern_sources = alternative_pattern_source(pattern.children.first)
50
+
51
+ (pattern_sources << pattern.children[1].source).join(' | ')
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -116,7 +116,8 @@ module RuboCop
116
116
  end
117
117
 
118
118
  def call_with_braced_block?(node)
119
- (node.send_type? || node.super_type?) && node.block_node && node.block_node.braces?
119
+ (node.send_type? || node.super_type?) &&
120
+ ((node.parent&.block_type? || node.parent&.numblock_type?) && node.parent&.braces?)
120
121
  end
121
122
 
122
123
  def call_as_argument_or_chain?(node)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks uses of the `then` keyword in multi-line `in` statement.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # case expression
11
+ # in pattern then
12
+ # end
13
+ #
14
+ # # good
15
+ # case expression
16
+ # in pattern
17
+ # end
18
+ #
19
+ # # good
20
+ # case expression
21
+ # in pattern then do_something
22
+ # end
23
+ #
24
+ # # good
25
+ # case expression
26
+ # in pattern then do_something(arg1,
27
+ # arg2)
28
+ # end
29
+ #
30
+ class MultilineInPatternThen < Base
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+ extend TargetRubyVersion
34
+
35
+ minimum_target_ruby_version 2.7
36
+
37
+ MSG = 'Do not use `then` for multiline `in` statement.'
38
+
39
+ def on_in_pattern(node)
40
+ return if !node.then? || require_then?(node)
41
+
42
+ range = node.loc.begin
43
+ add_offense(range) do |corrector|
44
+ corrector.remove(
45
+ range_with_surrounding_space(range: range, side: :left, newlines: false)
46
+ )
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Requires `then` for write `in` and its body on the same line.
53
+ def require_then?(in_pattern_node)
54
+ return true if in_pattern_node.pattern.first_line != in_pattern_node.pattern.last_line
55
+ return false unless in_pattern_node.body
56
+
57
+ in_pattern_node.loc.line == in_pattern_node.body.loc.line
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end