rubocop 1.14.0 → 1.18.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +120 -34
  4. data/lib/rubocop.rb +8 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
  6. data/lib/rubocop/config_loader.rb +1 -1
  7. data/lib/rubocop/config_validator.rb +5 -5
  8. data/lib/rubocop/cop/base.rb +2 -2
  9. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  10. data/lib/rubocop/cop/bundler/gem_version.rb +38 -4
  11. data/lib/rubocop/cop/corrector.rb +4 -4
  12. data/lib/rubocop/cop/generator.rb +1 -1
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  15. data/lib/rubocop/cop/layout/argument_alignment.rb +30 -12
  16. data/lib/rubocop/cop/layout/array_alignment.rb +2 -2
  17. data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
  18. data/lib/rubocop/cop/layout/case_indentation.rb +57 -9
  19. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +7 -1
  20. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  21. data/lib/rubocop/cop/layout/dot_position.rb +7 -1
  22. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +13 -15
  23. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  24. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +14 -2
  25. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  26. data/lib/rubocop/cop/layout/hash_alignment.rb +40 -14
  27. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  28. data/lib/rubocop/cop/layout/indentation_width.rb +13 -2
  29. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +122 -0
  30. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +6 -6
  31. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +2 -2
  32. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -6
  33. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -6
  34. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +6 -6
  35. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +3 -3
  36. data/lib/rubocop/cop/layout/parameter_alignment.rb +2 -2
  37. data/lib/rubocop/cop/layout/redundant_line_break.rb +11 -9
  38. data/lib/rubocop/cop/layout/space_around_keyword.rb +28 -0
  39. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -1
  40. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -0
  41. data/lib/rubocop/cop/lint/empty_block.rb +18 -2
  42. data/lib/rubocop/cop/lint/empty_in_pattern.rb +62 -0
  43. data/lib/rubocop/cop/lint/literal_as_condition.rb +13 -1
  44. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +32 -17
  45. data/lib/rubocop/cop/lint/nested_percent_literal.rb +1 -1
  46. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  47. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  48. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +105 -74
  49. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -0
  50. data/lib/rubocop/cop/lint/symbol_conversion.rb +3 -13
  51. data/lib/rubocop/cop/lint/unused_block_argument.rb +1 -1
  52. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  53. data/lib/rubocop/cop/lint/void.rb +1 -1
  54. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  55. data/lib/rubocop/cop/migration/department_name.rb +3 -1
  56. data/lib/rubocop/cop/mixin/check_line_breakable.rb +28 -3
  57. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
  58. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +14 -3
  59. data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -4
  60. data/lib/rubocop/cop/mixin/symbol_help.rb +13 -0
  61. data/lib/rubocop/cop/naming/inclusive_language.rb +249 -0
  62. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -2
  63. data/lib/rubocop/cop/style/class_and_module_children.rb +28 -2
  64. data/lib/rubocop/cop/style/empty_literal.rb +8 -1
  65. data/lib/rubocop/cop/style/hash_each_methods.rb +18 -1
  66. data/lib/rubocop/cop/style/identical_conditional_branches.rb +58 -8
  67. data/lib/rubocop/cop/style/in_pattern_then.rb +56 -0
  68. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -1
  69. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +62 -0
  70. data/lib/rubocop/cop/style/multiline_when_then.rb +2 -11
  71. data/lib/rubocop/cop/style/multiple_comparison.rb +1 -1
  72. data/lib/rubocop/cop/style/nil_lambda.rb +29 -12
  73. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  74. data/lib/rubocop/cop/style/quoted_symbols.rb +110 -0
  75. data/lib/rubocop/cop/style/raise_args.rb +2 -0
  76. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  77. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  78. data/lib/rubocop/cop/style/redundant_self.rb +24 -2
  79. data/lib/rubocop/cop/style/regexp_literal.rb +10 -1
  80. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  81. data/lib/rubocop/cop/style/string_concatenation.rb +32 -5
  82. data/lib/rubocop/cop/style/string_literals.rb +3 -2
  83. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +1 -0
  84. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  85. data/lib/rubocop/cop/style/top_level_method_definition.rb +83 -0
  86. data/lib/rubocop/cop/style/trivial_accessors.rb +65 -0
  87. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  88. data/lib/rubocop/cop/style/when_then.rb +6 -2
  89. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  90. data/lib/rubocop/directive_comment.rb +58 -6
  91. data/lib/rubocop/formatter/junit_formatter.rb +21 -6
  92. data/lib/rubocop/options.rb +18 -24
  93. data/lib/rubocop/rake_task.rb +1 -1
  94. data/lib/rubocop/remote_config.rb +10 -2
  95. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  96. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  97. data/lib/rubocop/version.rb +1 -1
  98. metadata +13 -5
@@ -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)
@@ -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
@@ -35,17 +35,7 @@ module RuboCop
35
35
  MSG = 'Do not use `then` for multiline `when` statement.'
36
36
 
37
37
  def on_when(node)
38
- # Without `then`, there's no offense
39
- return unless node.then?
40
-
41
- # Single line usage of `then` is not an offense
42
- return if !node.children.last.nil? && !node.multiline?
43
-
44
- # Requires `then` for write `when` and its body on the same line.
45
- return if require_then?(node)
46
-
47
- # For arrays and hashes there's no offense
48
- return if accept_node_type?(node.body)
38
+ return if !node.then? || require_then?(node)
49
39
 
50
40
  range = node.loc.begin
51
41
  add_offense(range) do |corrector|
@@ -57,6 +47,7 @@ module RuboCop
57
47
 
58
48
  private
59
49
 
50
+ # Requires `then` for write `when` and its body on the same line.
60
51
  def require_then?(when_node)
61
52
  unless when_node.conditions.first.first_line == when_node.conditions.last.last_line
62
53
  return true
@@ -44,7 +44,7 @@ module RuboCop
44
44
  extend AutoCorrector
45
45
 
46
46
  MSG = 'Avoid comparing a variable with multiple items ' \
47
- 'in a conditional, use `Array#include?` instead.'
47
+ 'in a conditional, use `Array#include?` instead.'
48
48
 
49
49
  def on_new_investigation
50
50
  @last_comparison = nil
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop checks for lambdas that always return nil, which can be replaced
7
- # with an empty lambda instead.
6
+ # This cop checks for lambdas and procs that always return nil,
7
+ # which can be replaced with an empty lambda or proc instead.
8
8
  #
9
9
  # @example
10
10
  # # bad
@@ -14,6 +14,12 @@ module RuboCop
14
14
  # next nil
15
15
  # end
16
16
  #
17
+ # proc { nil }
18
+ #
19
+ # Proc.new do
20
+ # break nil
21
+ # end
22
+ #
17
23
  # # good
18
24
  # -> {}
19
25
  #
@@ -22,11 +28,15 @@ module RuboCop
22
28
  #
23
29
  # -> (x) { nil if x }
24
30
  #
31
+ # proc {}
32
+ #
33
+ # Proc.new { nil if x }
34
+ #
25
35
  class NilLambda < Base
26
36
  extend AutoCorrector
27
37
  include RangeHelp
28
38
 
29
- MSG = 'Use an empty lambda instead of always returning nil.'
39
+ MSG = 'Use an empty %<type>s instead of always returning nil.'
30
40
 
31
41
  # @!method nil_return?(node)
32
42
  def_node_matcher :nil_return?, <<~PATTERN
@@ -34,19 +44,26 @@ module RuboCop
34
44
  PATTERN
35
45
 
36
46
  def on_block(node)
37
- return unless node.lambda?
47
+ return unless node.lambda? || node.proc?
38
48
  return unless nil_return?(node.body)
39
49
 
40
- add_offense(node) do |corrector|
41
- range = if node.single_line?
42
- range_with_surrounding_space(range: node.body.loc.expression)
43
- else
44
- range_by_whole_lines(node.body.loc.expression, include_final_newline: true)
45
- end
46
-
47
- corrector.remove(range)
50
+ message = format(MSG, type: node.lambda? ? 'lambda' : 'proc')
51
+ add_offense(node, message: message) do |corrector|
52
+ autocorrect(corrector, node)
48
53
  end
49
54
  end
55
+
56
+ private
57
+
58
+ def autocorrect(corrector, node)
59
+ range = if node.single_line?
60
+ range_with_surrounding_space(range: node.body.loc.expression)
61
+ else
62
+ range_by_whole_lines(node.body.loc.expression, include_final_newline: true)
63
+ end
64
+
65
+ corrector.remove(range)
66
+ end
50
67
  end
51
68
  end
52
69
  end
@@ -68,7 +68,7 @@ module RuboCop
68
68
  delimiters = preferred_delimiters_for(type)
69
69
 
70
70
  "`#{type}`-literals should be delimited by " \
71
- "`#{delimiters[0]}` and `#{delimiters[1]}`."
71
+ "`#{delimiters[0]}` and `#{delimiters[1]}`."
72
72
  end
73
73
 
74
74
  def preferred_delimiters_for(type)
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks if the quotes used for quoted symbols match the configured defaults.
7
+ # By default uses the same configuration as `Style/StringLiterals`.
8
+ #
9
+ # String interpolation is always kept in double quotes.
10
+ #
11
+ # Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
12
+ # are not quoted that don't need to be. This cop is for configuring the quoting
13
+ # style to use for symbols that require quotes.
14
+ #
15
+ # @example EnforcedStyle: same_as_string_literals (default) / single_quotes
16
+ # # bad
17
+ # :"abc-def"
18
+ #
19
+ # # good
20
+ # :'abc-def'
21
+ # :"#{str}"
22
+ # :"a\'b"
23
+ #
24
+ # @example EnforcedStyle: double_quotes
25
+ # # bad
26
+ # :'abc-def'
27
+ #
28
+ # # good
29
+ # :"abc-def"
30
+ # :"#{str}"
31
+ # :"a\'b"
32
+ class QuotedSymbols < Base
33
+ include ConfigurableEnforcedStyle
34
+ include SymbolHelp
35
+ include StringLiteralsHelp
36
+ extend AutoCorrector
37
+
38
+ MSG_SINGLE = "Prefer single-quoted symbols when you don't need string interpolation " \
39
+ 'or special symbols.'
40
+ MSG_DOUBLE = 'Prefer double-quoted symbols unless you need single quotes to ' \
41
+ 'avoid extra backslashes for escaping.'
42
+
43
+ def on_sym(node)
44
+ return unless quoted?(node)
45
+
46
+ message = style == :single_quotes ? MSG_SINGLE : MSG_DOUBLE
47
+
48
+ if wrong_quotes?(node)
49
+ add_offense(node, message: message) do |corrector|
50
+ opposite_style_detected
51
+ autocorrect(corrector, node)
52
+ end
53
+ else
54
+ correct_style_detected
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def autocorrect(corrector, node)
61
+ str = if hash_colon_key?(node)
62
+ # strip quotes
63
+ correct_quotes(node.source[1..-2])
64
+ else
65
+ # strip leading `:` and quotes
66
+ ":#{correct_quotes(node.source[2..-2])}"
67
+ end
68
+
69
+ corrector.replace(node, str)
70
+ end
71
+
72
+ def hash_colon_key?(node)
73
+ # Is the node a hash key with the colon style?
74
+ hash_key?(node) && node.parent.colon?
75
+ end
76
+
77
+ def correct_quotes(str)
78
+ if style == :single_quotes
79
+ to_string_literal(str)
80
+ else
81
+ str.inspect
82
+ end
83
+ end
84
+
85
+ def style
86
+ return super unless super == :same_as_string_literals
87
+
88
+ string_literals_config = config.for_cop('Style/StringLiterals')
89
+ return :single_quotes unless string_literals_config['Enabled']
90
+
91
+ string_literals_config['EnforcedStyle'].to_sym
92
+ end
93
+
94
+ def alternative_style
95
+ (supported_styles - [style, :same_as_string_literals]).first
96
+ end
97
+
98
+ def quoted?(sym_node)
99
+ sym_node.source.match?(/\A:?(['"]).*?\1\z/m)
100
+ end
101
+
102
+ def wrong_quotes?(node)
103
+ return super if hash_key?(node)
104
+
105
+ super(node.source[1..-1])
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end