rubocop 1.52.0 → 1.53.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +38 -1
  4. data/lib/rubocop/cli/command/lsp.rb +19 -0
  5. data/lib/rubocop/cli.rb +3 -0
  6. data/lib/rubocop/config_loader_resolver.rb +4 -3
  7. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  8. data/lib/rubocop/cop/bundler/gem_version.rb +2 -2
  9. data/lib/rubocop/cop/gemspec/dependency_version.rb +2 -2
  10. data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
  11. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +3 -3
  12. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  13. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -2
  14. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  15. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +2 -0
  16. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  17. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  18. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  19. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +1 -1
  20. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -1
  21. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  22. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +2 -1
  23. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +46 -19
  24. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -2
  25. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +1 -1
  26. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  27. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +1 -2
  28. data/lib/rubocop/cop/lint/inherit_exception.rb +3 -1
  29. data/lib/rubocop/cop/lint/missing_super.rb +31 -5
  30. data/lib/rubocop/cop/lint/mixed_case_range.rb +109 -0
  31. data/lib/rubocop/cop/lint/number_conversion.rb +5 -0
  32. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  33. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +120 -0
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +8 -3
  35. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +2 -2
  36. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  37. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  38. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  39. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -1
  40. data/lib/rubocop/cop/lint/void.rb +1 -1
  41. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  42. data/lib/rubocop/cop/migration/department_name.rb +2 -2
  43. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  44. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  45. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  46. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  47. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +3 -3
  48. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  49. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  50. data/lib/rubocop/cop/style/block_delimiters.rb +3 -3
  51. data/lib/rubocop/cop/style/class_equality_comparison.rb +17 -39
  52. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  53. data/lib/rubocop/cop/style/dir.rb +1 -1
  54. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  55. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  56. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  57. data/lib/rubocop/cop/style/file_read.rb +2 -2
  58. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  59. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  60. data/lib/rubocop/cop/style/identical_conditional_branches.rb +6 -2
  61. data/lib/rubocop/cop/style/invertible_unless_condition.rb +1 -1
  62. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  63. data/lib/rubocop/cop/style/preferred_hash_methods.rb +1 -1
  64. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  65. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
  66. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +38 -0
  67. data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -2
  68. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  69. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +86 -0
  70. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  71. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +3 -1
  72. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  73. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -0
  74. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +81 -0
  75. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  76. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  77. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  78. data/lib/rubocop/cop/style/yaml_file_read.rb +66 -0
  79. data/lib/rubocop/cop/util.rb +1 -1
  80. data/lib/rubocop/cop/utils/regexp_ranges.rb +100 -0
  81. data/lib/rubocop/cop/variable_force/assignment.rb +14 -1
  82. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  83. data/lib/rubocop/ext/regexp_parser.rb +4 -1
  84. data/lib/rubocop/lsp/logger.rb +22 -0
  85. data/lib/rubocop/lsp/routes.rb +223 -0
  86. data/lib/rubocop/lsp/runtime.rb +79 -0
  87. data/lib/rubocop/lsp/server.rb +62 -0
  88. data/lib/rubocop/lsp/severity.rb +27 -0
  89. data/lib/rubocop/options.rb +11 -1
  90. data/lib/rubocop/version.rb +1 -1
  91. data/lib/rubocop.rb +8 -0
  92. metadata +36 -9
@@ -361,7 +361,7 @@ module RuboCop
361
361
  end
362
362
 
363
363
  def assignment_types_match?(*nodes)
364
- return unless assignment_type?(nodes.first)
364
+ return false unless assignment_type?(nodes.first)
365
365
 
366
366
  nodes.map(&:type).uniq.one?
367
367
  end
@@ -440,6 +440,8 @@ module RuboCop
440
440
  module ConditionalCorrectorHelper
441
441
  def remove_whitespace_in_branches(corrector, branch, condition, column)
442
442
  branch.each_node do |child|
443
+ next if child.source_range.nil?
444
+
443
445
  white_space = white_space_range(child, column)
444
446
  corrector.remove(white_space) if white_space.source.strip.empty?
445
447
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for places where the `#__dir__` method can replace more
6
+ # Checks for places where the `#\_\_dir\_\_` method can replace more
7
7
  # complex constructs to retrieve a canonicalized absolute path to the
8
8
  # current file.
9
9
  #
@@ -19,18 +19,16 @@ module RuboCop
19
19
  extend AutoCorrector
20
20
  extend TargetRubyVersion
21
21
 
22
- MSG = 'Use `Dir.empty?(%<arg>s)` instead.'
23
- RESTRICT_ON_SEND = %i[== > empty? none?].freeze
22
+ MSG = 'Use `%<replacement>s` instead.'
23
+ RESTRICT_ON_SEND = %i[== != > empty? none?].freeze
24
24
 
25
25
  minimum_target_ruby_version 2.4
26
26
 
27
27
  # @!method offensive?(node)
28
28
  def_node_matcher :offensive?, <<~PATTERN
29
29
  {
30
- (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) {:== :>} (int 2))
31
- (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) {:== :>} (int 0))
32
- (send (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) :!) {:== :>} (int 2))
33
- (send (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) :!) {:== :>} (int 0))
30
+ (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) {:== :!= :>} (int 2))
31
+ (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) {:== :!= :>} (int 0))
34
32
  (send (send $(const {nil? cbase} :Dir) :children $_) :empty?)
35
33
  (send (send $(const {nil? cbase} :Dir) :each_child $_) :none?)
36
34
  }
@@ -38,10 +36,9 @@ module RuboCop
38
36
 
39
37
  def on_send(node)
40
38
  offensive?(node) do |const_node, arg_node|
41
- add_offense(node, message: format(MSG, arg: arg_node.source)) do |corrector|
42
- bang(node)
43
- corrector.replace(node,
44
- "#{bang(node)}#{const_node.source}.empty?(#{arg_node.source})")
39
+ replacement = "#{bang(node)}#{const_node.source}.empty?(#{arg_node.source})"
40
+ add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
41
+ corrector.replace(node, replacement)
45
42
  end
46
43
  end
47
44
  end
@@ -49,10 +46,7 @@ module RuboCop
49
46
  private
50
47
 
51
48
  def bang(node)
52
- if (node.method?(:==) && node.child_nodes.first.method?(:!)) ||
53
- (node.method?(:>) && !node.child_nodes.first.method?(:!))
54
- '!'
55
- end
49
+ '!' if %i[!= >].include? node.method_name
56
50
  end
57
51
  end
58
52
  end
@@ -108,7 +108,7 @@ module RuboCop
108
108
  comments = heredoc_comment_blocks(arg_node.loc.heredoc_body.line_span)
109
109
  .concat(preceding_comment_blocks(arg_node.parent))
110
110
 
111
- return if comments.none?
111
+ return false if comments.none?
112
112
 
113
113
  regexp = comment_regexp(arg_node)
114
114
  comments.any?(regexp) || regexp.match?(comments.join)
@@ -4,12 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Ensures that eval methods (`eval`, `instance_eval`, `class_eval`
7
- # and `module_eval`) are given filename and line number values (`__FILE__`
8
- # and `__LINE__`). This data is used to ensure that any errors raised
7
+ # and `module_eval`) are given filename and line number values (`\_\_FILE\_\_`
8
+ # and `\_\_LINE\_\_`). This data is used to ensure that any errors raised
9
9
  # within the evaluated code will be given the correct identification
10
10
  # in a backtrace.
11
11
  #
12
- # The cop also checks that the line number given relative to `__LINE__` is
12
+ # The cop also checks that the line number given relative to `\_\_LINE\_\_` is
13
13
  # correct.
14
14
  #
15
15
  # This cop will autocorrect incorrect or missing filename and line number
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  # @!method block_read?(node)
65
65
  def_node_matcher :block_read?, <<~PATTERN
66
- (block _ (args (arg $_)) (send (lvar $_) :read))
66
+ (block _ (args (arg _name)) (send (lvar _name) :read))
67
67
  PATTERN
68
68
 
69
69
  def on_send(node)
@@ -100,7 +100,7 @@ module RuboCop
100
100
  def file_open_read?(node)
101
101
  return true if send_read?(node)
102
102
 
103
- block_read?(node) { |block_arg, read_lvar| block_arg == read_lvar }
103
+ block_read?(node)
104
104
  end
105
105
 
106
106
  def read_method(mode)
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the keys of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_keys` instead.
10
10
  # It should only be enabled on Ruby version 2.5 or newer.
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the values of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_values` instead.
10
10
  #
@@ -158,13 +158,16 @@ module RuboCop
158
158
  return false unless expressions.size >= 1 && unique_expressions.one?
159
159
 
160
160
  unique_expression = unique_expressions.first
161
- return true unless unique_expression.assignment?
161
+ return true unless unique_expression&.assignment?
162
162
 
163
163
  lhs = unique_expression.child_nodes.first
164
164
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
165
  end
166
166
 
167
- def check_expressions(node, expressions, insert_position) # rubocop:disable Metrics/MethodLength
167
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
168
+ def check_expressions(node, expressions, insert_position)
169
+ return if expressions.any?(&:nil?)
170
+
168
171
  inserted_expression = false
169
172
 
170
173
  expressions.each do |expression|
@@ -184,6 +187,7 @@ module RuboCop
184
187
  end
185
188
  end
186
189
  end
190
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
187
191
 
188
192
  def last_child_of_parent?(node)
189
193
  return true unless (parent = node.parent)
@@ -72,7 +72,7 @@ module RuboCop
72
72
  when :begin
73
73
  invertible?(node.children.first)
74
74
  when :send
75
- return if inheritance_check?(node)
75
+ return false if inheritance_check?(node)
76
76
 
77
77
  node.method?(:!) || inverse_methods.key?(node.method_name)
78
78
  when :or, :and
@@ -98,7 +98,7 @@ module RuboCop
98
98
 
99
99
  def call_in_literals?(node)
100
100
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
101
- return unless parent
101
+ return false unless parent
102
102
 
103
103
  parent.pair_type? ||
104
104
  parent.array_type? ||
@@ -109,7 +109,7 @@ module RuboCop
109
109
 
110
110
  def call_in_logical_operators?(node)
111
111
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
112
- return unless parent
112
+ return false unless parent
113
113
 
114
114
  logical_operator?(parent) ||
115
115
  (parent.send_type? &&
@@ -61,7 +61,7 @@ module RuboCop
61
61
  if style == :verbose
62
62
  "has_#{method_name}"
63
63
  else
64
- method_name.to_s.sub(/has_/, '')
64
+ method_name.to_s.delete_prefix('has_')
65
65
  end
66
66
  end
67
67
 
@@ -146,7 +146,7 @@ module RuboCop
146
146
  end
147
147
 
148
148
  def use_modifier_form_after_multiline_begin_block?(node)
149
- return unless (parent = node.parent)
149
+ return false unless (parent = node.parent)
150
150
 
151
151
  node.multiline? && parent.if_type? && parent.modifier_form?
152
152
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
  RUBY
64
64
 
65
65
  def offense?(node)
66
- return if node.modifier_form?
66
+ return false if node.modifier_form?
67
67
 
68
68
  redundant_condition?(node) || redundant_condition_inverted?(node)
69
69
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for uses a redundant current directory in path.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # require_relative './path/to/feature'
12
+ #
13
+ # # good
14
+ # require_relative 'path/to/feature'
15
+ #
16
+ class RedundantCurrentDirectoryInPath < Base
17
+ include RangeHelp
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Remove the redundant current directory path.'
21
+ CURRENT_DIRECTORY_PATH = './'
22
+
23
+ def on_send(node)
24
+ return unless node.method?(:require_relative)
25
+ return unless node.first_argument.str_content.start_with?(CURRENT_DIRECTORY_PATH)
26
+ return unless (index = node.first_argument.source.index(CURRENT_DIRECTORY_PATH))
27
+
28
+ begin_pos = node.first_argument.source_range.begin.begin_pos + index
29
+ range = range_between(begin_pos, begin_pos + 2)
30
+
31
+ add_offense(range) do |corrector|
32
+ corrector.remove(range)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -116,7 +116,7 @@ module RuboCop
116
116
  return false if argument_newline?(node)
117
117
 
118
118
  source = node.parent ? node.parent.source : node.source
119
- parse(source.gsub(/\\\n/, "\n")).valid_syntax?
119
+ parse(source.gsub("\\\n", "\n")).valid_syntax?
120
120
  end
121
121
 
122
122
  def inside_string_literal?(range, token)
@@ -150,7 +150,7 @@ module RuboCop
150
150
  end
151
151
 
152
152
  def same_line?(node, line)
153
- return unless (source_range = node.source_range)
153
+ return false unless (source_range = node.source_range)
154
154
 
155
155
  if node.is_a?(AST::StrNode)
156
156
  if node.heredoc?
@@ -85,7 +85,7 @@ module RuboCop
85
85
  end
86
86
 
87
87
  def allowed_ternary?(node)
88
- return unless node&.parent&.if_type?
88
+ return false unless node&.parent&.if_type?
89
89
 
90
90
  node.parent.ternary? && ternary_parentheses_required?
91
91
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies places where argument can be replaced from
7
+ # a deterministic regexp to a string.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # 'foo'.byteindex(/f/)
12
+ # 'foo'.byterindex(/f/)
13
+ # 'foo'.gsub(/f/, 'x')
14
+ # 'foo'.gsub!(/f/, 'x')
15
+ # 'foo'.partition(/f/)
16
+ # 'foo'.rpartition(/f/)
17
+ # 'foo'.scan(/f/)
18
+ # 'foo'.split(/f/)
19
+ # 'foo'.start_with?(/f/)
20
+ # 'foo'.sub(/f/, 'x')
21
+ # 'foo'.sub!(/f/, 'x')
22
+ #
23
+ # # good
24
+ # 'foo'.byteindex('f')
25
+ # 'foo'.byterindex('f')
26
+ # 'foo'.gsub('f', 'x')
27
+ # 'foo'.gsub!('f', 'x')
28
+ # 'foo'.partition('f')
29
+ # 'foo'.rpartition('f')
30
+ # 'foo'.scan('f')
31
+ # 'foo'.split('f')
32
+ # 'foo'.start_with?('f')
33
+ # 'foo'.sub('f', 'x')
34
+ # 'foo'.sub!('f', 'x')
35
+ class RedundantRegexpArgument < Base
36
+ extend AutoCorrector
37
+
38
+ MSG = 'Use string `%<prefer>s` as argument instead of regexp `%<current>s`.'
39
+ RESTRICT_ON_SEND = %i[
40
+ byteindex byterindex gsub gsub! partition rpartition scan split start_with? sub sub!
41
+ ].freeze
42
+ DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
43
+ STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
44
+
45
+ def on_send(node)
46
+ return unless (regexp_node = node.first_argument)
47
+ return unless regexp_node.regexp_type?
48
+ return if !regexp_node.regopt.children.empty? || regexp_node.content == ' '
49
+ return unless determinist_regexp?(regexp_node)
50
+
51
+ new_argument = replacement(regexp_node)
52
+ quote = new_argument.include?('"') ? "'" : '"'
53
+ prefer = "#{quote}#{new_argument}#{quote}"
54
+ message = format(MSG, prefer: prefer, current: regexp_node.source)
55
+
56
+ add_offense(regexp_node, message: message) do |corrector|
57
+ corrector.replace(regexp_node, prefer)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def determinist_regexp?(regexp_node)
64
+ DETERMINISTIC_REGEX.match?(regexp_node.source)
65
+ end
66
+
67
+ def replacement(regexp_node)
68
+ regexp_content = regexp_node.content
69
+ stack = []
70
+ chars = regexp_content.chars.each_with_object([]) do |char, strings|
71
+ if stack.empty? && char == '\\'
72
+ stack.push(char)
73
+ else
74
+ strings << "#{stack.pop}#{char}"
75
+ end
76
+ end
77
+ chars.map do |char|
78
+ char = char.dup
79
+ char.delete!('\\') unless STR_SPECIAL_CHARS.include?(char)
80
+ char
81
+ end.join
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -44,7 +44,8 @@ module RuboCop
44
44
 
45
45
  def on_regexp(node)
46
46
  each_escape(node) do |char, index, within_character_class|
47
- next if allowed_escape?(node, char, index, within_character_class)
47
+ next if char.valid_encoding? && allowed_escape?(node, char, index,
48
+ within_character_class)
48
49
 
49
50
  location = escape_range_at_index(node, index)
50
51
 
@@ -62,7 +62,9 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def multiple_statements?(branch)
65
- branch && branch.children.compact.count > 1
65
+ return false unless branch&.begin_type?
66
+
67
+ !branch.children.empty?
66
68
  end
67
69
 
68
70
  def self_assign?(variable, branch)
@@ -198,7 +198,7 @@ module RuboCop
198
198
  end
199
199
 
200
200
  def with_logical_operator?(node)
201
- return unless (parent = node.parent)
201
+ return false unless (parent = node.parent)
202
202
 
203
203
  parent.or_type? || parent.and_type?
204
204
  end
@@ -157,6 +157,8 @@ module RuboCop
157
157
  return delimiter?(node.parent, char)
158
158
  end
159
159
 
160
+ return true unless node.loc.begin
161
+
160
162
  delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]]
161
163
 
162
164
  delimiters.include?(char)
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks if `return` or `return nil` is used in predicate method definitions.
7
+ #
8
+ # @safety
9
+ # Autocorrection is marked as unsafe because the change of the return value
10
+ # from `nil` to `false` could potentially lead to incompatibility issues.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # def foo?
15
+ # return if condition
16
+ #
17
+ # do_something?
18
+ # end
19
+ #
20
+ # # bad
21
+ # def foo?
22
+ # return nil if condition
23
+ #
24
+ # do_something?
25
+ # end
26
+ #
27
+ # # good
28
+ # def foo?
29
+ # return false if condition
30
+ #
31
+ # do_something?
32
+ # end
33
+ #
34
+ # @example AllowedMethod: ['foo?']
35
+ # # good
36
+ # def foo?
37
+ # return if condition
38
+ #
39
+ # do_something?
40
+ # end
41
+ #
42
+ # @example AllowedPattern: [/foo/]
43
+ # # good
44
+ # def foo?
45
+ # return if condition
46
+ #
47
+ # do_something?
48
+ # end
49
+ #
50
+ class ReturnNilInPredicateMethodDefinition < Base
51
+ extend AutoCorrector
52
+ include AllowedMethods
53
+ include AllowedPattern
54
+
55
+ MSG = 'Use `return false` instead of `%<prefer>s` in the predicate method.'
56
+
57
+ # @!method return_nil?(node)
58
+ def_node_matcher :return_nil?, <<~PATTERN
59
+ {(return) (return (nil))}
60
+ PATTERN
61
+
62
+ def on_def(node)
63
+ return unless node.predicate_method?
64
+ return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
65
+ return unless (body = node.body)
66
+
67
+ body.each_descendant(:return) do |return_node|
68
+ next unless return_nil?(return_node)
69
+
70
+ message = format(MSG, prefer: return_node.source)
71
+
72
+ add_offense(return_node, message: message) do |corrector|
73
+ corrector.replace(return_node, 'return false')
74
+ end
75
+ end
76
+ end
77
+ alias on_defs on_def
78
+ end
79
+ end
80
+ end
81
+ end
@@ -198,7 +198,7 @@ module RuboCop
198
198
  end
199
199
 
200
200
  def command_or_kernel_call?(name, node)
201
- return unless node.method?(name)
201
+ return false unless node.method?(name)
202
202
 
203
203
  node.command?(name) || kernel_call?(node, name)
204
204
  end
@@ -186,7 +186,9 @@ module RuboCop
186
186
  begin_pos = condition.first_argument.source_range.begin_pos
187
187
  return if end_pos > begin_pos
188
188
 
189
- corrector.replace(range_between(end_pos, begin_pos), '(')
189
+ range = range_between(end_pos, begin_pos)
190
+ corrector.remove(range)
191
+ corrector.insert_after(range, '(')
190
192
  corrector.insert_after(condition.last_argument, ')')
191
193
  end
192
194
 
@@ -3,8 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- #
7
- # This cop looks for uses of Perl-style global variables.
6
+ # Looks for uses of Perl-style global variables.
8
7
  # Correcting to global variables in the 'English' library
9
8
  # will add a require statement to the top of the file if
10
9
  # enabled by RequireEnglish config.
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for the use of `YAML.load`, `YAML.safe_load`, and `YAML.parse` with
7
+ # `File.read` argument.
8
+ #
9
+ # NOTE: `YAML.safe_load_file` was introduced in Ruby 3.0.
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ # YAML.load(File.read(path))
15
+ # YAML.parse(File.read(path))
16
+ #
17
+ # # good
18
+ # YAML.load_file(path)
19
+ # YAML.parse_file(path)
20
+ #
21
+ # # bad
22
+ # YAML.safe_load(File.read(path)) # Ruby 3.0 and newer
23
+ #
24
+ # # good
25
+ # YAML.safe_load_file(path) # Ruby 3.0 and newer
26
+ #
27
+ class YAMLFileRead < Base
28
+ extend AutoCorrector
29
+
30
+ MSG = 'Use `%<prefer>s` instead.'
31
+ RESTRICT_ON_SEND = %i[load safe_load parse].freeze
32
+
33
+ # @!method yaml_file_read?(node)
34
+ def_node_matcher :yaml_file_read?, <<~PATTERN
35
+ (send
36
+ (const {cbase nil?} :YAML) _
37
+ (send
38
+ (const {cbase nil?} :File) :read $_) $...)
39
+ PATTERN
40
+
41
+ def on_send(node)
42
+ return if node.method?(:safe_load) && target_ruby_version <= 2.7
43
+ return unless (file_path, rest_arguments = yaml_file_read?(node))
44
+
45
+ range = offense_range(node)
46
+ rest_arguments = if rest_arguments.empty?
47
+ ''
48
+ else
49
+ ", #{rest_arguments.map(&:source).join(', ')}"
50
+ end
51
+ prefer = "#{node.method_name}_file(#{file_path.source}#{rest_arguments})"
52
+
53
+ add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
54
+ corrector.replace(range, prefer)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def offense_range(node)
61
+ node.loc.selector.join(node.source_range.end)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -142,7 +142,7 @@ module RuboCop
142
142
  end
143
143
 
144
144
  def escape_string(string)
145
- string.inspect[1..-2].tap { |s| s.gsub!(/\\"/, '"') }
145
+ string.inspect[1..-2].tap { |s| s.gsub!('\\"', '"') }
146
146
  end
147
147
 
148
148
  def to_string_literal(string)