rubocop 1.69.1 → 1.70.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +19 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  6. data/lib/rubocop/config.rb +13 -4
  7. data/lib/rubocop/config_loader.rb +4 -0
  8. data/lib/rubocop/config_loader_resolver.rb +14 -3
  9. data/lib/rubocop/config_validator.rb +18 -8
  10. data/lib/rubocop/cop/autocorrect_logic.rb +31 -34
  11. data/lib/rubocop/cop/base.rb +6 -0
  12. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  13. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  15. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  16. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +4 -2
  17. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  18. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -7
  19. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  20. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  21. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  22. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -7
  23. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  24. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -6
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  26. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -0
  27. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  28. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  29. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/line_length.rb +1 -0
  31. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +24 -0
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  33. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  34. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
  35. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  36. data/lib/rubocop/cop/lint/constant_reassignment.rb +152 -0
  37. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  38. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +11 -3
  39. data/lib/rubocop/cop/lint/nested_method_definition.rb +5 -1
  40. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -2
  41. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +6 -14
  42. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  43. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  44. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
  45. data/lib/rubocop/cop/lint/void.rb +3 -2
  46. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  47. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -7
  49. data/lib/rubocop/cop/mixin/comments_help.rb +6 -1
  50. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  51. data/lib/rubocop/cop/mixin/line_length_help.rb +5 -4
  52. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +46 -22
  53. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  54. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  55. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  56. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  57. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  58. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -1
  59. data/lib/rubocop/cop/style/and_or.rb +1 -1
  60. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -4
  61. data/lib/rubocop/cop/style/block_delimiters.rb +8 -1
  62. data/lib/rubocop/cop/style/class_and_module_children.rb +5 -2
  63. data/lib/rubocop/cop/style/each_for_simple_loop.rb +3 -6
  64. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  65. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  66. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  67. data/lib/rubocop/cop/style/exact_regexp_match.rb +1 -2
  68. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  69. data/lib/rubocop/cop/style/file_null.rb +20 -4
  70. data/lib/rubocop/cop/style/float_division.rb +8 -4
  71. data/lib/rubocop/cop/style/hash_except.rb +54 -67
  72. data/lib/rubocop/cop/style/hash_syntax.rb +5 -2
  73. data/lib/rubocop/cop/style/if_with_semicolon.rb +6 -4
  74. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +11 -1
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  77. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -1
  78. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +34 -22
  80. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  81. data/lib/rubocop/cop/style/object_then.rb +13 -15
  82. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  83. data/lib/rubocop/cop/style/raise_args.rb +5 -3
  84. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  85. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  86. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  87. data/lib/rubocop/cop/style/redundant_line_continuation.rb +12 -9
  88. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -4
  89. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  90. data/lib/rubocop/cop/style/redundant_self_assignment.rb +6 -5
  91. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  92. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  93. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  94. data/lib/rubocop/cop/style/single_line_methods.rb +2 -3
  95. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  96. data/lib/rubocop/cop/style/super_arguments.rb +63 -15
  97. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  98. data/lib/rubocop/cop/style/yoda_expression.rb +1 -0
  99. data/lib/rubocop/cop/util.rb +9 -2
  100. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  101. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  102. data/lib/rubocop/lsp/logger.rb +2 -2
  103. data/lib/rubocop/lsp/routes.rb +7 -23
  104. data/lib/rubocop/lsp/runtime.rb +15 -49
  105. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  106. data/lib/rubocop/magic_comment.rb +3 -3
  107. data/lib/rubocop/path_util.rb +11 -8
  108. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  109. data/lib/rubocop/runner.rb +5 -6
  110. data/lib/rubocop/target_ruby.rb +15 -0
  111. data/lib/rubocop/version.rb +1 -1
  112. data/lib/rubocop.rb +3 -0
  113. data/lib/ruby_lsp/rubocop/addon.rb +78 -0
  114. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +50 -0
  115. metadata +16 -8
@@ -55,12 +55,9 @@ module RuboCop
55
55
  return if node.each_descendant(:dstr).any?
56
56
 
57
57
  regexp_constructor(node) do |text|
58
- Regexp::Parser.parse(text.value)&.each_expression do |expr|
58
+ parse_regexp(text.value)&.each_expression do |expr|
59
59
  detect_offenses(text, expr)
60
60
  end
61
- rescue Regexp::Parser::ParserError
62
- # Upon encountering an invalid regular expression,
63
- # we aim to proceed and identify any remaining potential offenses.
64
61
  end
65
62
  end
66
63
 
@@ -113,7 +113,8 @@ module RuboCop
113
113
  end
114
114
 
115
115
  def check_expression(expr)
116
- expr = expr.body if expr.if_type? && expr.modifier_form?
116
+ expr = expr.body if expr.if_type?
117
+ return unless expr
117
118
 
118
119
  check_literal(expr)
119
120
  check_var(expr)
@@ -229,7 +230,7 @@ module RuboCop
229
230
  end
230
231
 
231
232
  def autocorrect_void_expression(corrector, node)
232
- return if node.parent.if_type? && node.parent.modifier_form?
233
+ return if node.parent.if_type?
233
234
 
234
235
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
235
236
  end
@@ -51,15 +51,7 @@ module RuboCop
51
51
  end
52
52
 
53
53
  def on_casgn(node)
54
- parent = node.parent
55
-
56
- block_node = if parent&.assignment?
57
- parent.expression
58
- elsif parent&.parent&.masgn_type?
59
- parent.parent.expression
60
- else
61
- node.expression
62
- end
54
+ block_node = node.expression || find_expression_within_parent(node.parent)
63
55
 
64
56
  return unless block_node.respond_to?(:class_definition?) && block_node.class_definition?
65
57
 
@@ -71,6 +63,14 @@ module RuboCop
71
63
  def message(length, max_length)
72
64
  format('Class has too many lines. [%<length>d/%<max>d]', length: length, max: max_length)
73
65
  end
66
+
67
+ def find_expression_within_parent(parent)
68
+ if parent&.assignment?
69
+ parent.expression
70
+ elsif parent&.parent&.masgn_type?
71
+ parent.parent.expression
72
+ end
73
+ end
74
74
  end
75
75
  end
76
76
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  LABEL = 'Method'
49
49
 
50
50
  def on_def(node)
51
- return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
51
+ return if allowed?(node.method_name)
52
52
 
53
53
  check_code_length(node)
54
54
  end
@@ -57,6 +57,9 @@ module RuboCop
57
57
  def on_block(node)
58
58
  return unless node.method?(:define_method)
59
59
 
60
+ method_name = node.send_node.first_argument
61
+ return if method_name.basic_literal? && allowed?(method_name.value)
62
+
60
63
  check_code_length(node)
61
64
  end
62
65
  alias on_numblock on_block
@@ -66,6 +69,10 @@ module RuboCop
66
69
  def cop_label
67
70
  LABEL
68
71
  end
72
+
73
+ def allowed?(method_name)
74
+ allowed_method?(method_name) || matches_allowed_pattern?(method_name)
75
+ end
69
76
  end
70
77
  end
71
78
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # (Note: Passes may not happen exactly in this sequence.)
44
44
  module CheckLineBreakable
45
45
  def extract_breakable_node(node, max)
46
- if node.send_type?
46
+ if node.call_type?
47
47
  return if chained_to_heredoc?(node)
48
48
 
49
49
  args = process_args(node.arguments)
@@ -74,9 +74,9 @@ module RuboCop
74
74
  def extract_first_element_over_column_limit(node, elements, max)
75
75
  line = node.first_line
76
76
 
77
- # If a `send` node is not parenthesized, don't move the first element, because it
77
+ # If a `send` or `csend` node is not parenthesized, don't move the first element, because it
78
78
  # can result in changed behavior or a syntax error.
79
- if node.send_type? && !node.parenthesized? && !first_argument_is_heredoc?(node)
79
+ if node.call_type? && !node.parenthesized? && !first_argument_is_heredoc?(node)
80
80
  elements = elements.drop(1)
81
81
  end
82
82
 
@@ -98,10 +98,10 @@ module RuboCop
98
98
  end
99
99
 
100
100
  # @api private
101
- # If a send node contains a heredoc argument, splitting cannot happen
101
+ # If a `send` or `csend` node contains a heredoc argument, splitting cannot happen
102
102
  # after the heredoc or else it will cause a syntax error.
103
103
  def shift_elements_for_heredoc_arg(node, elements, index)
104
- return index unless node.send_type? || node.array_type?
104
+ return index unless node.call_type? || node.array_type?
105
105
 
106
106
  heredoc_index = elements.index { |arg| arg.respond_to?(:heredoc?) && arg.heredoc? }
107
107
  return index unless heredoc_index
@@ -156,7 +156,7 @@ module RuboCop
156
156
 
157
157
  if ancestor.hash_type? || ancestor.array_type?
158
158
  elements = ancestor.children
159
- elsif ancestor.send_type?
159
+ elsif ancestor.call_type?
160
160
  elements = process_args(ancestor.arguments)
161
161
  else
162
162
  next
@@ -176,7 +176,7 @@ module RuboCop
176
176
  return children_could_be_broken_up?(ancestor.children)
177
177
  end
178
178
 
179
- next unless ancestor.send_type?
179
+ next unless ancestor.call_type?
180
180
 
181
181
  args = process_args(ancestor.arguments)
182
182
  return children_could_be_broken_up?(args) if breakable_collection?(ancestor, args)
@@ -16,6 +16,8 @@ module RuboCop
16
16
  end
17
17
 
18
18
  def comments_in_range(node)
19
+ return [] unless node.source_range
20
+
19
21
  start_line = node.source_range.line
20
22
  end_line = find_end_line(node)
21
23
 
@@ -71,10 +73,13 @@ module RuboCop
71
73
  node.else_branch.loc.line
72
74
  elsif node.elsif?
73
75
  node.each_ancestor(:if).find(&:if?).loc.end.line
76
+ elsif node.if? && node.parent && parentheses?(node.parent)
77
+ node.parent.loc.end.line
74
78
  end
75
79
  elsif node.block_type? || node.numblock_type?
76
80
  node.loc.end.line
77
- elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
81
+ elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node) &&
82
+ next_sibling.source_range
78
83
  next_sibling.loc.line
79
84
  elsif (parent = node.parent)
80
85
  if parent.loc.respond_to?(:end) && parent.loc.end
@@ -20,7 +20,7 @@ module RuboCop
20
20
  private
21
21
 
22
22
  def dig_chain_enabled?
23
- @config.for_cop('Style/DigChain')['Enabled']
23
+ @config.cop_enabled?('Style/DigChain')
24
24
  end
25
25
  end
26
26
  end
@@ -37,12 +37,13 @@ module RuboCop
37
37
  last_uri_match = match_uris(line).last
38
38
  return nil unless last_uri_match
39
39
 
40
- begin_position, end_position = last_uri_match.offset(0).map do |pos|
41
- pos + indentation_difference(line)
42
- end
43
-
40
+ begin_position, end_position = last_uri_match.offset(0)
44
41
  end_position = extend_uri_end_position(line, end_position)
45
42
 
43
+ line_indentation_difference = indentation_difference(line)
44
+ begin_position += line_indentation_difference
45
+ end_position += line_indentation_difference
46
+
46
47
  return nil if begin_position < max_line_length && end_position < max_line_length
47
48
 
48
49
  begin_position...end_position
@@ -5,6 +5,10 @@ module RuboCop
5
5
  # Common functionality for checking whether an AST node/token is aligned
6
6
  # with something on a preceding or following line
7
7
  module PrecedingFollowingAlignment
8
+ # Tokens that end with an `=`, as well as `<<`, that can be aligned together:
9
+ # `=`, `==`, `===`, `!=`, `<=`, `>=`, `<<` and operator assignment (`+=`, etc).
10
+ ASSIGNMENT_OR_COMPARISON_TOKENS = %i[tEQL tEQ tEQQ tNEQ tLEQ tGEQ tOP_ASGN tLSHFT].freeze
11
+
8
12
  private
9
13
 
10
14
  def allow_for_alignment?
@@ -19,16 +23,20 @@ module RuboCop
19
23
  aligned_with_adjacent_line?(range, method(:aligned_operator?))
20
24
  end
21
25
 
22
- def aligned_with_preceding_assignment(token)
26
+ # Allows alignment with a preceding operator that ends with an `=`,
27
+ # including assignment and comparison operators.
28
+ def aligned_with_preceding_equals_operator(token)
23
29
  preceding_line_range = token.line.downto(1)
24
30
 
25
- aligned_with_assignment(token, preceding_line_range)
31
+ aligned_with_equals_sign(token, preceding_line_range)
26
32
  end
27
33
 
28
- def aligned_with_subsequent_assignment(token)
34
+ # Allows alignment with a subsequent operator that ends with an `=`,
35
+ # including assignment and comparison operators.
36
+ def aligned_with_subsequent_equals_operator(token)
29
37
  subsequent_line_range = token.line.upto(processed_source.lines.length)
30
38
 
31
- aligned_with_assignment(token, subsequent_line_range)
39
+ aligned_with_equals_sign(token, subsequent_line_range)
32
40
  end
33
41
 
34
42
  def aligned_with_adjacent_line?(range, predicate)
@@ -62,7 +70,7 @@ module RuboCop
62
70
  next unless index
63
71
  next if indent && indent != index
64
72
 
65
- return yield(range, line)
73
+ return yield(range, line, lineno + 1)
66
74
  end
67
75
  false
68
76
  end
@@ -74,12 +82,12 @@ module RuboCop
74
82
  end.map(&:line)
75
83
  end
76
84
 
77
- def aligned_token?(range, line)
78
- aligned_words?(range, line) || aligned_assignment?(range, line)
85
+ def aligned_token?(range, line, lineno)
86
+ aligned_words?(range, line) || aligned_equals_operator?(range, lineno)
79
87
  end
80
88
 
81
- def aligned_operator?(range, line)
82
- aligned_identical?(range, line) || aligned_assignment?(range, line)
89
+ def aligned_operator?(range, line, lineno)
90
+ aligned_identical?(range, line) || aligned_equals_operator?(range, lineno)
83
91
  end
84
92
 
85
93
  def aligned_words?(range, line)
@@ -90,23 +98,42 @@ module RuboCop
90
98
  token == line[left_edge, token.length]
91
99
  end
92
100
 
93
- def aligned_assignment?(range, line)
94
- (range.source[-1] == '=' && line[range.last_column - 1] == '=') ||
95
- aligned_with_append_operator?(range, line)
101
+ def aligned_equals_operator?(range, lineno)
102
+ # Check that the operator is aligned with a previous assignment or comparison operator
103
+ # ie. an equals sign, an operator assignment (eg. `+=`), a comparison (`==`, `<=`, etc.).
104
+ # Since append operators (`<<`) are a type of assignment, they are allowed as well,
105
+ # despite not ending with a literal equals character.
106
+ line_range = processed_source.buffer.line_range(lineno)
107
+ return false unless line_range
108
+
109
+ # Find the specific token to avoid matching up to operators inside strings
110
+ operator_token = processed_source.tokens_within(line_range).detect do |token|
111
+ ASSIGNMENT_OR_COMPARISON_TOKENS.include?(token.type)
112
+ end
113
+
114
+ aligned_with_preceding_equals?(range, operator_token) ||
115
+ aligned_with_append_operator?(range, operator_token)
96
116
  end
97
117
 
98
- def aligned_with_append_operator?(range, line)
99
- last_column = range.last_column
118
+ def aligned_with_preceding_equals?(range, token)
119
+ return false unless token
100
120
 
101
- (range.source == '<<' && line[last_column - 1] == '=') ||
102
- (range.source[-1] == '=' && line[(last_column - 2)..(last_column - 1)] == '<<')
121
+ range.source[-1] == '=' && range.last_column == token.pos.last_column
122
+ end
123
+
124
+ def aligned_with_append_operator?(range, token)
125
+ return false unless token
126
+
127
+ ((range.source == '<<' && token.equal_sign?) ||
128
+ (range.source[-1] == '=' && token.type == :tLSHFT)) &&
129
+ token && range.last_column == token.pos.last_column
103
130
  end
104
131
 
105
132
  def aligned_identical?(range, line)
106
133
  range.source == line[range.column, range.size]
107
134
  end
108
135
 
109
- def aligned_with_assignment(token, line_range)
136
+ def aligned_with_equals_sign(token, line_range)
110
137
  token_line_indent = processed_source.line_indentation(token.line)
111
138
  assignment_lines = relevant_assignment_lines(line_range)
112
139
  relevant_line_number = assignment_lines[1]
@@ -116,12 +143,9 @@ module RuboCop
116
143
  relevant_indent = processed_source.line_indentation(relevant_line_number)
117
144
 
118
145
  return :none if relevant_indent < token_line_indent
146
+ return :none unless processed_source.lines[relevant_line_number - 1]
119
147
 
120
- assignment_line = processed_source.lines[relevant_line_number - 1]
121
-
122
- return :none unless assignment_line
123
-
124
- aligned_assignment?(token.pos, assignment_line) ? :yes : :no
148
+ aligned_equals_operator?(token.pos, relevant_line_number) ? :yes : :no
125
149
  end
126
150
 
127
151
  def assignment_lines
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def space_required_after?(token)
39
- (token.left_curly_brace? || token.type == :tLAMBEG) && space_required_after_lcurly?
39
+ token.left_curly_brace? && space_required_after_lcurly?
40
40
  end
41
41
 
42
42
  def space_required_after_lcurly?
@@ -96,7 +96,7 @@ module RuboCop
96
96
  end
97
97
 
98
98
  def max_line_length
99
- return unless config.for_cop('Layout/LineLength')['Enabled']
99
+ return unless config.cop_enabled?('Layout/LineLength')
100
100
 
101
101
  config.for_cop('Layout/LineLength')['Max']
102
102
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
  end
27
27
 
28
28
  def string_literals_config
29
- config.for_cop('Style/StringLiterals')
29
+ config.for_enabled_cop('Style/StringLiterals')
30
30
  end
31
31
  end
32
32
  end
@@ -138,7 +138,7 @@ module RuboCop
138
138
  def use_block_argument_as_local_variable?(node, last_argument)
139
139
  return false if node.body.nil?
140
140
 
141
- node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
141
+ node.body.each_node(:lvar, :lvasgn).any? do |lvar|
142
142
  !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
143
143
  end
144
144
  end
@@ -154,7 +154,7 @@ module RuboCop
154
154
  end
155
155
 
156
156
  def variable_name(node)
157
- node.exception_variable&.name
157
+ node.exception_variable.name if node.exception_variable.respond_to?(:name)
158
158
  end
159
159
 
160
160
  def message(node)
@@ -112,6 +112,26 @@ module RuboCop
112
112
  # private attr :quux
113
113
  #
114
114
  # end
115
+ #
116
+ # @example AllowModifiersOnAliasMethod: true (default)
117
+ # # good
118
+ # class Foo
119
+ #
120
+ # public alias_method :bar, :foo
121
+ # protected alias_method :baz, :foo
122
+ # private alias_method :qux, :foo
123
+ #
124
+ # end
125
+ #
126
+ # @example AllowModifiersOnAliasMethod: false
127
+ # # bad
128
+ # class Foo
129
+ #
130
+ # public alias_method :bar, :foo
131
+ # protected alias_method :baz, :foo
132
+ # private alias_method :qux, :foo
133
+ #
134
+ # end
115
135
  class AccessModifierDeclarations < Base
116
136
  extend AutoCorrector
117
137
 
@@ -145,6 +165,12 @@ module RuboCop
145
165
  (send nil? {:attr :attr_reader :attr_writer :attr_accessor} _+))
146
166
  PATTERN
147
167
 
168
+ # @!method access_modifier_with_alias_method?, <<~PATTERN
169
+ def_node_matcher :access_modifier_with_alias_method?, <<~PATTERN
170
+ (send nil? {:private :protected :public :module_function}
171
+ (send nil? :alias_method _ _))
172
+ PATTERN
173
+
148
174
  def on_send(node)
149
175
  return if allowed?(node)
150
176
 
@@ -164,7 +190,8 @@ module RuboCop
164
190
  !node.access_modifier? ||
165
191
  ALLOWED_NODE_TYPES.include?(node.parent&.type) ||
166
192
  allow_modifiers_on_symbols?(node) ||
167
- allow_modifiers_on_attrs?(node)
193
+ allow_modifiers_on_attrs?(node) ||
194
+ allow_modifiers_on_alias_method?(node)
168
195
  end
169
196
 
170
197
  def autocorrect(corrector, node)
@@ -194,6 +221,10 @@ module RuboCop
194
221
  cop_config['AllowModifiersOnAttrs'] && access_modifier_with_attr?(node)
195
222
  end
196
223
 
224
+ def allow_modifiers_on_alias_method?(node)
225
+ cop_config['AllowModifiersOnAliasMethod'] && access_modifier_with_alias_method?(node)
226
+ end
227
+
197
228
  def offense?(node)
198
229
  (group_style? && access_modifier_is_inlined?(node) &&
199
230
  !node.parent&.if_type? && !right_siblings_same_inline_method?(node)) ||
@@ -127,7 +127,7 @@ module RuboCop
127
127
  end
128
128
 
129
129
  def correct_other(node, corrector)
130
- return if node.source_range.begin.is?('(')
130
+ return if node.parenthesized_call?
131
131
 
132
132
  corrector.wrap(node, '(', ')')
133
133
  end
@@ -540,10 +540,7 @@ module RuboCop
540
540
  end
541
541
 
542
542
  def explicit_block_name?
543
- block_forwarding_config = config.for_cop('Naming/BlockForwarding')
544
- return false unless block_forwarding_config['Enabled']
545
-
546
- block_forwarding_config['EnforcedStyle'] == 'explicit'
543
+ config.for_enabled_cop('Naming/BlockForwarding')['EnforcedStyle'] == 'explicit'
547
544
  end
548
545
  end
549
546
  end
@@ -183,7 +183,8 @@ module RuboCop
183
183
  def on_send(node)
184
184
  return unless node.arguments?
185
185
  return if node.parenthesized?
186
- return if node.operator_method? || node.assignment_method?
186
+ return if node.assignment_method?
187
+ return if single_argument_operator_method?(node)
187
188
 
188
189
  node.arguments.each do |arg|
189
190
  get_blocks(arg) do |block|
@@ -501,6 +502,12 @@ module RuboCop
501
502
  # `begin`...`end` when changing `do-end` to `{}`.
502
503
  block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line?
503
504
  end
505
+
506
+ def single_argument_operator_method?(node)
507
+ return false unless node.operator_method?
508
+
509
+ node.arguments.one? && node.first_argument.block_type?
510
+ end
504
511
  end
505
512
  end
506
513
  end
@@ -126,9 +126,12 @@ module RuboCop
126
126
  end
127
127
 
128
128
  def unindent(corrector, node)
129
- return if node.body.children.last.nil?
129
+ return unless node.body.children.last
130
130
 
131
- column_delta = configured_indentation_width - leading_spaces(node.body.children.last).size
131
+ last_child_leading_spaces = leading_spaces(node.body.children.last)
132
+ return if leading_spaces(node).size == last_child_leading_spaces.size
133
+
134
+ column_delta = configured_indentation_width - last_child_leading_spaces.size
132
135
  return if column_delta.zero?
133
136
 
134
137
  AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
@@ -53,8 +53,7 @@ module RuboCop
53
53
  (block
54
54
  (call
55
55
  (begin
56
- (${irange erange}
57
- (int $_) (int $_)))
56
+ ($range (int $_) (int $_)))
58
57
  :each)
59
58
  (args ...)
60
59
  ...)
@@ -65,8 +64,7 @@ module RuboCop
65
64
  (block
66
65
  (call
67
66
  (begin
68
- ({irange erange}
69
- (int 0) (int _)))
67
+ (range (int 0) (int _)))
70
68
  :each)
71
69
  (args ...)
72
70
  ...)
@@ -77,8 +75,7 @@ module RuboCop
77
75
  (block
78
76
  (call
79
77
  (begin
80
- ({irange erange}
81
- (int _) (int _)))
78
+ (range (int _) (int _)))
82
79
  :each)
83
80
  (args)
84
81
  ...)
@@ -131,6 +131,8 @@ module RuboCop
131
131
  extend AutoCorrector
132
132
 
133
133
  MSG = 'Redundant `else`-clause.'
134
+ NIL_STYLES = %i[nil both].freeze
135
+ EMPTY_STYLES = %i[empty both].freeze
134
136
 
135
137
  def on_normal_if_unless(node)
136
138
  check(node)
@@ -150,11 +152,11 @@ module RuboCop
150
152
  end
151
153
 
152
154
  def nil_style?
153
- style == :nil || style == :both
155
+ NIL_STYLES.include?(style)
154
156
  end
155
157
 
156
158
  def empty_style?
157
- style == :empty || style == :both
159
+ EMPTY_STYLES.include?(style)
158
160
  end
159
161
 
160
162
  def empty_check(node)
@@ -136,7 +136,7 @@ module RuboCop
136
136
  def frozen_strings?
137
137
  return true if frozen_string_literals_enabled?
138
138
 
139
- frozen_string_cop_enabled = config.for_cop('Style/FrozenStringLiteralComment')['Enabled']
139
+ frozen_string_cop_enabled = config.cop_enabled?('Style/FrozenStringLiteralComment')
140
140
  frozen_string_cop_enabled &&
141
141
  !frozen_string_literals_disabled? &&
142
142
  string_literals_frozen_by_default?.nil?
@@ -109,7 +109,7 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def max_line_length
112
- return unless config.for_cop('Layout/LineLength')['Enabled']
112
+ return unless config.cop_enabled?('Layout/LineLength')
113
113
 
114
114
  config.for_cop('Layout/LineLength')['Max']
115
115
  end
@@ -40,8 +40,7 @@ module RuboCop
40
40
  def on_send(node)
41
41
  return unless (receiver = node.receiver)
42
42
  return unless (regexp = exact_regexp_match(node))
43
-
44
- parsed_regexp = Regexp::Parser.parse(regexp)
43
+ return unless (parsed_regexp = parse_regexp(regexp))
45
44
  return unless exact_match_pattern?(parsed_regexp)
46
45
 
47
46
  prefer = "#{receiver.source} #{new_method(node)} '#{parsed_regexp[1].text}'"
@@ -8,7 +8,7 @@ module RuboCop
8
8
  #
9
9
  # * `scientific` which enforces a mantissa between 1 (inclusive) and 10 (exclusive).
10
10
  # * `engineering` which enforces the exponent to be a multiple of 3 and the mantissa
11
- # to be between 0.1 (inclusive) and 10 (exclusive).
11
+ # to be between 0.1 (inclusive) and 1000 (exclusive).
12
12
  # * `integral` which enforces the mantissa to always be a whole number without
13
13
  # trailing zeroes.
14
14
  #
@@ -8,6 +8,12 @@ module RuboCop
8
8
  # Only looks for full string matches, substrings within a longer string are not
9
9
  # considered.
10
10
  #
11
+ # However, only files that use the string `'/dev/null'` are targeted for detection.
12
+ # This is because the string `'NUL'` is not limited to the null device.
13
+ # This behavior results in false negatives when the `'/dev/null'` string is not used,
14
+ # but it is a trade-off to avoid false positives. `NULL:`
15
+ # Unlike `'NUL'`, `'NUL:'` is regarded as something like `C:` and is always detected.
16
+ #
11
17
  # NOTE: Uses inside arrays and hashes are ignored.
12
18
  #
13
19
  # @safety
@@ -42,11 +48,21 @@ module RuboCop
42
48
  REGEXP = %r{\A(/dev/null|NUL:?)\z}i.freeze
43
49
  MSG = 'Use `File::NULL` instead of `%<source>s`.'
44
50
 
51
+ def on_new_investigation
52
+ return unless (ast = processed_source.ast)
53
+
54
+ @contain_dev_null_string_in_file = ast.each_descendant(:str).any? do |str|
55
+ content = str.str_content
56
+
57
+ valid_string?(content) && content.downcase == '/dev/null' # rubocop:disable Style/FileNull
58
+ end
59
+ end
60
+
45
61
  def on_str(node)
46
62
  value = node.value
47
-
48
- return if invalid_string?(value)
63
+ return unless valid_string?(value)
49
64
  return if acceptable?(node)
65
+ return if value.downcase == 'nul' && !@contain_dev_null_string_in_file # rubocop:disable Style/FileNull
50
66
  return unless REGEXP.match?(value)
51
67
 
52
68
  add_offense(node, message: format(MSG, source: value)) do |corrector|
@@ -56,8 +72,8 @@ module RuboCop
56
72
 
57
73
  private
58
74
 
59
- def invalid_string?(value)
60
- value.empty? || !value.valid_encoding?
75
+ def valid_string?(value)
76
+ !value.empty? && value.valid_encoding?
61
77
  end
62
78
 
63
79
  def acceptable?(node)