rubocop-rails 2.17.4 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +22 -2
  4. data/config/default.yml +54 -29
  5. data/lib/rubocop/cop/mixin/enforce_superclass.rb +1 -1
  6. data/lib/rubocop/cop/mixin/index_method.rb +3 -3
  7. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +10 -2
  8. data/lib/rubocop/cop/rails/action_controller_test_case.rb +1 -1
  9. data/lib/rubocop/cop/rails/action_order.rb +5 -6
  10. data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -0
  11. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +6 -3
  12. data/lib/rubocop/cop/rails/add_column_index.rb +2 -2
  13. data/lib/rubocop/cop/rails/application_job.rb +1 -1
  14. data/lib/rubocop/cop/rails/arel_star.rb +1 -1
  15. data/lib/rubocop/cop/rails/assert_not.rb +1 -1
  16. data/lib/rubocop/cop/rails/belongs_to.rb +1 -4
  17. data/lib/rubocop/cop/rails/blank.rb +5 -5
  18. data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -4
  19. data/lib/rubocop/cop/rails/compact_blank.rb +2 -2
  20. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  21. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +16 -3
  22. data/lib/rubocop/cop/rails/delegate.rb +17 -3
  23. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +1 -1
  24. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +1 -1
  25. data/lib/rubocop/cop/rails/duration_arithmetic.rb +1 -1
  26. data/lib/rubocop/cop/rails/dynamic_find_by.rb +1 -1
  27. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +1 -1
  28. data/lib/rubocop/cop/rails/enum_hash.rb +1 -1
  29. data/lib/rubocop/cop/rails/environment_comparison.rb +1 -1
  30. data/lib/rubocop/cop/rails/file_path.rb +31 -15
  31. data/lib/rubocop/cop/rails/find_by_id.rb +2 -2
  32. data/lib/rubocop/cop/rails/find_each.rb +6 -2
  33. data/lib/rubocop/cop/rails/freeze_time.rb +3 -0
  34. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +2 -2
  35. data/lib/rubocop/cop/rails/http_positional_arguments.rb +18 -2
  36. data/lib/rubocop/cop/rails/http_status.rb +1 -1
  37. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -2
  38. data/lib/rubocop/cop/rails/inverse_of.rb +0 -3
  39. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +17 -7
  40. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -1
  41. data/lib/rubocop/cop/rails/mailer_name.rb +1 -1
  42. data/lib/rubocop/cop/rails/negate_include.rb +1 -1
  43. data/lib/rubocop/cop/rails/not_null_column.rb +9 -6
  44. data/lib/rubocop/cop/rails/output.rb +3 -2
  45. data/lib/rubocop/cop/rails/output_safety.rb +5 -1
  46. data/lib/rubocop/cop/rails/pluck.rb +13 -1
  47. data/lib/rubocop/cop/rails/pluck_id.rb +1 -1
  48. data/lib/rubocop/cop/rails/presence.rb +3 -3
  49. data/lib/rubocop/cop/rails/present.rb +5 -5
  50. data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
  51. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  52. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
  53. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  54. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  55. data/lib/rubocop/cop/rails/reflection_class_name.rb +17 -1
  56. data/lib/rubocop/cop/rails/relative_date_constant.rb +2 -2
  57. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  58. data/lib/rubocop/cop/rails/reversible_migration.rb +4 -29
  59. data/lib/rubocop/cop/rails/root_join_chain.rb +1 -1
  60. data/lib/rubocop/cop/rails/root_pathname_methods.rb +1 -1
  61. data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
  62. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +8 -1
  63. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +71 -0
  64. data/lib/rubocop/cop/rails/time_zone.rb +2 -2
  65. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +0 -2
  66. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  67. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -1
  68. data/lib/rubocop/cop/rails/validation.rb +1 -1
  69. data/lib/rubocop/cop/rails/where_equals.rb +1 -1
  70. data/lib/rubocop/cop/rails/where_exists.rb +1 -1
  71. data/lib/rubocop/cop/rails/where_missing.rb +3 -3
  72. data/lib/rubocop/cop/rails/where_not.rb +1 -1
  73. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +2 -2
  74. data/lib/rubocop/cop/rails_cops.rb +2 -0
  75. data/lib/rubocop/rails/version.rb +1 -1
  76. data/lib/rubocop/rails.rb +1 -1
  77. data/lib/rubocop-rails.rb +1 -0
  78. metadata +6 -4
@@ -78,7 +78,7 @@ module RuboCop
78
78
  def on_send(node)
79
79
  duration_arithmetic_argument?(node) do |*operation|
80
80
  add_offense(node) do |corrector|
81
- corrector.replace(node.source_range, corrected_source(*operation))
81
+ corrector.replace(node, corrected_source(*operation))
82
82
  end
83
83
  end
84
84
  end
@@ -99,7 +99,7 @@ module RuboCop
99
99
 
100
100
  def autocorrect_argument_keywords(corrector, node, keywords)
101
101
  keywords.each.with_index do |keyword, idx|
102
- corrector.insert_before(node.arguments[idx].loc.expression, keyword)
102
+ corrector.insert_before(node.arguments[idx], keyword)
103
103
  end
104
104
  end
105
105
 
@@ -59,7 +59,7 @@ module RuboCop
59
59
  private
60
60
 
61
61
  def replacement_range(node)
62
- stop = node.loc.expression.end
62
+ stop = node.source_range.end
63
63
  start = node.loc.selector.end
64
64
 
65
65
  if node.parenthesized_call?
@@ -42,7 +42,7 @@ module RuboCop
42
42
  "#{source(elem)} => #{index}"
43
43
  end.join(', ')
44
44
 
45
- corrector.replace(array.loc.expression, "{#{hash}}")
45
+ corrector.replace(array, "{#{hash}}")
46
46
  end
47
47
  end
48
48
  end
@@ -84,7 +84,7 @@ module RuboCop
84
84
  def autocorrect(corrector, node)
85
85
  replacement = build_predicate_method(node)
86
86
 
87
- corrector.replace(node.source_range, replacement)
87
+ corrector.replace(node, replacement)
88
88
  end
89
89
 
90
90
  def build_predicate_method(node)
@@ -3,34 +3,43 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Identifies usages of file path joining process
7
- # to use `Rails.root.join` clause. It is used to add uniformity when
8
- # joining paths.
6
+ # Identifies usages of file path joining process to use `Rails.root.join` clause.
7
+ # It is used to add uniformity when joining paths.
9
8
  #
10
9
  # @example EnforcedStyle: slashes (default)
11
10
  # # bad
12
11
  # Rails.root.join('app', 'models', 'goober')
12
+ #
13
+ # # good
14
+ # Rails.root.join('app/models/goober')
15
+ #
16
+ # # bad
13
17
  # File.join(Rails.root, 'app/models/goober')
14
18
  # "#{Rails.root}/app/models/goober"
15
19
  #
16
20
  # # good
17
- # Rails.root.join('app/models/goober')
21
+ # Rails.root.join('app/models/goober').to_s
18
22
  #
19
23
  # @example EnforcedStyle: arguments
20
24
  # # bad
21
25
  # Rails.root.join('app/models/goober')
26
+ #
27
+ # # good
28
+ # Rails.root.join('app', 'models', 'goober')
29
+ #
30
+ # # bad
22
31
  # File.join(Rails.root, 'app/models/goober')
23
32
  # "#{Rails.root}/app/models/goober"
24
33
  #
25
34
  # # good
26
- # Rails.root.join('app', 'models', 'goober')
35
+ # Rails.root.join('app', 'models', 'goober').to_s
27
36
  #
28
37
  class FilePath < Base
29
38
  include ConfigurableEnforcedStyle
30
39
  include RangeHelp
31
40
 
32
- MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')`.'
33
- MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')`.'
41
+ MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')%<to_s>s`.'
42
+ MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')%<to_s>s`.'
34
43
  RESTRICT_ON_SEND = %i[join].freeze
35
44
 
36
45
  def_node_matcher :file_join_nodes?, <<~PATTERN
@@ -53,7 +62,7 @@ module RuboCop
53
62
  return unless last_child_source.start_with?('.') || last_child_source.include?(File::SEPARATOR)
54
63
  return if last_child_source.start_with?(':')
55
64
 
56
- register_offense(node)
65
+ register_offense(node, require_to_s: true)
57
66
  end
58
67
 
59
68
  def on_send(node)
@@ -68,7 +77,7 @@ module RuboCop
68
77
  return unless file_join_nodes?(node)
69
78
  return unless node.arguments.any? { |e| rails_root_nodes?(e) }
70
79
 
71
- register_offense(node)
80
+ register_offense(node, require_to_s: true)
72
81
  end
73
82
 
74
83
  def check_for_rails_root_join_with_string_arguments(node)
@@ -78,7 +87,7 @@ module RuboCop
78
87
  return unless node.arguments.size > 1
79
88
  return unless node.arguments.all?(&:str_type?)
80
89
 
81
- register_offense(node)
90
+ register_offense(node, require_to_s: false)
82
91
  end
83
92
 
84
93
  def check_for_rails_root_join_with_slash_separated_path(node)
@@ -87,21 +96,28 @@ module RuboCop
87
96
  return unless rails_root_join_nodes?(node)
88
97
  return unless node.arguments.any? { |arg| string_with_slash?(arg) }
89
98
 
90
- register_offense(node)
99
+ register_offense(node, require_to_s: false)
91
100
  end
92
101
 
93
102
  def string_with_slash?(node)
94
103
  node.str_type? && node.source.include?('/')
95
104
  end
96
105
 
97
- def register_offense(node)
106
+ def register_offense(node, require_to_s:)
98
107
  line_range = node.loc.column...node.loc.last_column
99
108
  source_range = source_range(processed_source.buffer, node.first_line, line_range)
100
- add_offense(source_range)
109
+ require_to_s = false if node.dstr_type?
110
+
111
+ message = build_message(require_to_s)
112
+
113
+ add_offense(source_range, message: message)
101
114
  end
102
115
 
103
- def message(_range)
104
- format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
116
+ def build_message(require_to_s)
117
+ message_template = style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES
118
+ to_s = require_to_s ? '.to_s' : ''
119
+
120
+ format(message_template, to_s: to_s)
105
121
  end
106
122
  end
107
123
  end
@@ -65,11 +65,11 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def where_take_offense_range(node, where)
68
- range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
68
+ range_between(where.loc.selector.begin_pos, node.source_range.end_pos)
69
69
  end
70
70
 
71
71
  def find_by_offense_range(node)
72
- range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
72
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
73
73
  end
74
74
 
75
75
  def build_good_method(id_value)
@@ -3,8 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Identifies usages of `all.each` and
7
- # change them to use `all.find_each` instead.
6
+ # Identifies usages of `all.each` and change them to use `all.find_each` instead.
7
+ #
8
+ # @safety
9
+ # This cop is unsafe if the receiver object is not an Active Record object.
10
+ # Also, `all.each` returns an `Array` instance and `all.find_each` returns nil,
11
+ # so the return values are different.
8
12
  #
9
13
  # @example
10
14
  # # bad
@@ -26,6 +26,9 @@ module RuboCop
26
26
  #
27
27
  class FreezeTime < Base
28
28
  extend AutoCorrector
29
+ extend TargetRailsVersion
30
+
31
+ minimum_target_rails_version 5.2
29
32
 
30
33
  MSG = 'Use `freeze_time` instead of `travel_to`.'
31
34
  NOW_METHODS = %i[now new current].freeze
@@ -41,11 +41,11 @@ module RuboCop
41
41
  PATTERN
42
42
 
43
43
  def_node_matcher :association_without_options?, <<~PATTERN
44
- (send nil? {:has_many :has_one} _)
44
+ (send _ {:has_many :has_one} _)
45
45
  PATTERN
46
46
 
47
47
  def_node_matcher :association_with_options?, <<~PATTERN
48
- (send nil? {:has_many :has_one} ... (hash $...))
48
+ (send _ {:has_many :has_one} ... (hash $...))
49
49
  PATTERN
50
50
 
51
51
  def_node_matcher :dependent_option?, <<~PATTERN
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your
11
11
  # .rubocop.yml file to 4.2.
12
12
  #
13
+ # NOTE: It does not detect any cases where `include Rack::Test::Methods` is used
14
+ # which makes the http methods incompatible behavior.
15
+ #
13
16
  # @example
14
17
  # # bad
15
18
  # get :new, { user_id: 1}
@@ -37,8 +40,15 @@ module RuboCop
37
40
  (hash (kwsplat _))
38
41
  PATTERN
39
42
 
43
+ def_node_matcher :include_rack_test_methods?, <<~PATTERN
44
+ (send nil? :include
45
+ (const
46
+ (const
47
+ (const {nil? cbase} :Rack) :Test) :Methods))
48
+ PATTERN
49
+
40
50
  def on_send(node)
41
- return if in_routing_block?(node)
51
+ return if in_routing_block?(node) || use_rack_test_methods?
42
52
 
43
53
  http_request?(node) do |data|
44
54
  return unless needs_conversion?(data)
@@ -56,7 +66,7 @@ module RuboCop
56
66
  # that represents the path/action on the Rails controller
57
67
  # the data is the http parameters and environment sent in
58
68
  # the Rails 5 http call
59
- corrector.replace(node.loc.expression, correction(node))
69
+ corrector.replace(node, correction(node))
60
70
  end
61
71
  end
62
72
  end
@@ -67,6 +77,12 @@ module RuboCop
67
77
  !!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.method_name) }
68
78
  end
69
79
 
80
+ def use_rack_test_methods?
81
+ processed_source.ast.each_descendant(:send).any? do |node|
82
+ include_rack_test_methods?(node)
83
+ end
84
+ end
85
+
70
86
  def needs_conversion?(data)
71
87
  return true unless data.hash_type?
72
88
  return false if kwsplat_hash?(data)
@@ -66,7 +66,7 @@ module RuboCop
66
66
  return unless checker.offensive?
67
67
 
68
68
  add_offense(checker.node, message: checker.message) do |corrector|
69
- corrector.replace(checker.node.loc.expression, checker.preferred_style)
69
+ corrector.replace(checker.node, checker.preferred_style)
70
70
  end
71
71
  end
72
72
  end
@@ -35,8 +35,6 @@ module RuboCop
35
35
  # skip_before_action :login_required,
36
36
  # if: -> { trusted_origin? && action_name != "admin" }
37
37
  # end
38
- #
39
- # @see https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options
40
38
  class IgnoredSkipActionFilterOption < Base
41
39
  MSG = <<~MSG.chomp.freeze
42
40
  `%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
@@ -137,9 +137,6 @@ module RuboCop
137
137
  # class Blog < ApplicationRecord
138
138
  # has_many :posts, -> { order(published_at: :desc) }
139
139
  # end
140
- #
141
- # @see https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
142
- # @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
143
140
  class InverseOf < Base
144
141
  SPECIFY_MSG = 'Specify an `:inverse_of` option.'
145
142
  NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'
@@ -144,19 +144,29 @@ module RuboCop
144
144
  end
145
145
 
146
146
  def aliased_action_methods(node, defined_methods)
147
- alias_methods = node.each_child_node(:send).select { |send_node| send_node.method?(:alias_method) }
148
-
149
- hash_of_alias_methods = alias_methods.each_with_object({}) do |alias_method, result|
150
- result[alias_method.last_argument.value] = alias_method.first_argument.value
151
- end
152
-
147
+ alias_methods = alias_methods(node)
153
148
  defined_methods.each_with_object([]) do |defined_method, aliased_method|
154
- if (new_method_name = hash_of_alias_methods[defined_method])
149
+ if (new_method_name = alias_methods[defined_method])
155
150
  aliased_method << new_method_name
156
151
  end
157
152
  end
158
153
  end
159
154
 
155
+ def alias_methods(node)
156
+ result = {}
157
+ node.each_child_node(:send, :alias) do |child_node|
158
+ case child_node.type
159
+ when :send
160
+ if child_node.method?(:alias_method)
161
+ result[child_node.last_argument.value] = child_node.first_argument.value
162
+ end
163
+ when :alias
164
+ result[child_node.old_identifier.value] = child_node.new_identifier.value
165
+ end
166
+ end
167
+ result
168
+ end
169
+
160
170
  # @param node [RuboCop::AST::Node]
161
171
  # @return [Array<Symbol>]
162
172
  def array_values(node) # rubocop:disable Metrics/MethodLength
@@ -68,7 +68,7 @@ module RuboCop
68
68
 
69
69
  def append_to_rel(rel_node, corrector)
70
70
  existing_rel = rel_node.children.last.value
71
- str_range = rel_node.children.last.loc.expression.adjust(begin_pos: 1, end_pos: -1)
71
+ str_range = rel_node.children.last.source_range.adjust(begin_pos: 1, end_pos: -1)
72
72
  corrector.replace(str_range, "#{existing_rel} noopener")
73
73
  end
74
74
 
@@ -77,7 +77,7 @@ module RuboCop
77
77
  corrector.replace(node.loc.name, "#{name}Mailer")
78
78
  else
79
79
  name = node.children.last
80
- corrector.replace(node.source_range, "#{name}Mailer")
80
+ corrector.replace(node, "#{name}Mailer")
81
81
  end
82
82
  end
83
83
 
@@ -26,7 +26,7 @@ module RuboCop
26
26
  RESTRICT_ON_SEND = %i[!].freeze
27
27
 
28
28
  def_node_matcher :negate_include_call?, <<~PATTERN
29
- (send (send $_ :include? $_) :!)
29
+ (send (send $!nil? :include? $_) :!)
30
30
  PATTERN
31
31
 
32
32
  def on_send(node)
@@ -21,7 +21,7 @@ module RuboCop
21
21
  RESTRICT_ON_SEND = %i[add_column add_reference].freeze
22
22
 
23
23
  def_node_matcher :add_not_null_column?, <<~PATTERN
24
- (send nil? :add_column _ _ _ (hash $...))
24
+ (send nil? :add_column _ _ $_ (hash $...))
25
25
  PATTERN
26
26
 
27
27
  def_node_matcher :add_not_null_reference?, <<~PATTERN
@@ -44,17 +44,20 @@ module RuboCop
44
44
  private
45
45
 
46
46
  def check_add_column(node)
47
- pairs = add_not_null_column?(node)
48
- check_pairs(pairs)
47
+ add_not_null_column?(node) do |type, pairs|
48
+ return if type.value == :virtual || type.value == 'virtual'
49
+
50
+ check_pairs(pairs)
51
+ end
49
52
  end
50
53
 
51
54
  def check_add_reference(node)
52
- pairs = add_not_null_reference?(node)
53
- check_pairs(pairs)
55
+ add_not_null_reference?(node) do |pairs|
56
+ check_pairs(pairs)
57
+ end
54
58
  end
55
59
 
56
60
  def check_pairs(pairs)
57
- return unless pairs
58
61
  return if pairs.any? { |pair| default_option?(pair) }
59
62
 
60
63
  null_false = pairs.find { |pair| null_false?(pair) }
@@ -39,7 +39,8 @@ module RuboCop
39
39
  PATTERN
40
40
 
41
41
  def on_send(node)
42
- return unless (output?(node) || io_output?(node)) && node.arguments?
42
+ return if node.parent&.call_type?
43
+ return unless output?(node) || io_output?(node)
43
44
 
44
45
  range = offense_range(node)
45
46
 
@@ -56,7 +57,7 @@ module RuboCop
56
57
 
57
58
  def offense_range(node)
58
59
  if node.receiver
59
- range_between(node.loc.expression.begin_pos, node.loc.selector.end_pos)
60
+ range_between(node.source_range.begin_pos, node.loc.selector.end_pos)
60
61
  else
61
62
  node.loc.selector
62
63
  end
@@ -66,8 +66,12 @@ module RuboCop
66
66
  MSG = 'Tagging a string as html safe may be a security risk.'
67
67
  RESTRICT_ON_SEND = %i[html_safe raw safe_concat].freeze
68
68
 
69
+ def_node_search :i18n_method?, <<~PATTERN
70
+ (send {nil? (const {nil? cbase} :I18n)} {:t :translate :l :localize} ...)
71
+ PATTERN
72
+
69
73
  def on_send(node)
70
- return if non_interpolated_string?(node)
74
+ return if non_interpolated_string?(node) || i18n_method?(node)
71
75
 
72
76
  return unless looks_like_rails_html_safe?(node) ||
73
77
  looks_like_rails_raw?(node) ||
@@ -9,6 +9,18 @@ module RuboCop
9
9
  # element in an enumerable. When called on an Active Record relation, it
10
10
  # results in a more efficient query that only selects the necessary key.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because model can use column aliases.
14
+ #
15
+ # [source,ruby]
16
+ # ----
17
+ # # Original code
18
+ # User.select('name AS nickname').map { |user| user[:nickname] } # => array of nicknames
19
+ #
20
+ # # After autocorrection
21
+ # User.select('name AS nickname').pluck(:nickname) # => raises ActiveRecord::StatementInvalid
22
+ # ----
23
+ #
12
24
  # @example
13
25
  # # bad
14
26
  # Post.published.map { |post| post[:title] }
@@ -31,7 +43,7 @@ module RuboCop
31
43
 
32
44
  def on_block(node)
33
45
  pluck_candidate?(node) do |argument, key|
34
- next unless use_one_block_argument?(argument)
46
+ next if key.regexp_type? || !use_one_block_argument?(argument)
35
47
 
36
48
  match = if node.block_type?
37
49
  block_argument = argument.children.first.source
@@ -51,7 +51,7 @@ module RuboCop
51
51
  private
52
52
 
53
53
  def offense_range(node)
54
- range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
54
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
55
55
  end
56
56
  end
57
57
  end
@@ -93,7 +93,7 @@ module RuboCop
93
93
 
94
94
  def register_offense(node, receiver, other)
95
95
  add_offense(node, message: message(node, receiver, other)) do |corrector|
96
- corrector.replace(node.source_range, replacement(receiver, other, node.left_sibling))
96
+ corrector.replace(node, replacement(receiver, other, node.left_sibling))
97
97
  end
98
98
  end
99
99
 
@@ -112,10 +112,10 @@ module RuboCop
112
112
  end
113
113
 
114
114
  def current(node)
115
- if node.source.include?("\n")
115
+ if !node.ternary? && node.source.include?("\n")
116
116
  "#{node.loc.keyword.with(end_pos: node.condition.loc.selector.end_pos).source} ... end"
117
117
  else
118
- node.source
118
+ node.source.gsub(/\n\s*/, ' ')
119
119
  end
120
120
  end
121
121
 
@@ -8,7 +8,7 @@ module RuboCop
8
8
  #
9
9
  # Interaction with `Style/UnlessElse`:
10
10
  # The configuration of `NotBlank` will not produce an offense in the
11
- # context of `unless else` if `Style/UnlessElse` is inabled. This is
11
+ # context of `unless else` if `Style/UnlessElse` is enabled. This is
12
12
  # to prevent interference between the autocorrection of the two cops.
13
13
  #
14
14
  # @example NotNilAndNotEmpty: true (default)
@@ -128,10 +128,10 @@ module RuboCop
128
128
 
129
129
  if method_call
130
130
  corrector.replace(node.loc.keyword, 'if')
131
- range = method_call.loc.expression
131
+ range = method_call.source_range
132
132
  else
133
133
  variable1, _variable2 = exists_and_not_empty?(node) || not_blank?(node)
134
- range = node.loc.expression
134
+ range = node.source_range
135
135
  end
136
136
 
137
137
  corrector.replace(range, replacement(variable1))
@@ -141,9 +141,9 @@ module RuboCop
141
141
 
142
142
  def unless_condition(node, method_call)
143
143
  if node.modifier_form?
144
- node.loc.keyword.join(node.loc.expression.end)
144
+ node.loc.keyword.join(node.source_range.end)
145
145
  else
146
- node.loc.expression.begin.join(method_call.loc.expression)
146
+ node.source_range.begin.join(method_call.source_range)
147
147
  end
148
148
  end
149
149
 
@@ -48,7 +48,7 @@ module RuboCop
48
48
  task_name = task_method.arguments[0]
49
49
  task_dependency = correct_task_dependency(task_name)
50
50
 
51
- corrector.replace(task_name.loc.expression, task_dependency)
51
+ corrector.replace(task_name, task_dependency)
52
52
  end
53
53
  end
54
54
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  return if within_shadowing_method?(node)
53
53
 
54
54
  add_offense(node, message: build_message(node)) do |corrector|
55
- corrector.replace(node.source_range, node_replacement(node))
55
+ corrector.replace(node, node_replacement(node))
56
56
  end
57
57
  end
58
58
 
@@ -62,7 +62,7 @@ module RuboCop
62
62
  elsif prv_sib
63
63
  corrector.remove(range_between(node_end(prv_sib), node_end(allow_nil)))
64
64
  else
65
- corrector.remove(allow_nil.loc.expression)
65
+ corrector.remove(allow_nil)
66
66
  end
67
67
  end
68
68
  end
@@ -87,11 +87,11 @@ module RuboCop
87
87
  end
88
88
 
89
89
  def node_beg(node)
90
- node.loc.expression.begin_pos
90
+ node.source_range.begin_pos
91
91
  end
92
92
 
93
93
  def node_end(node)
94
- node.loc.expression.end_pos
94
+ node.source_range.end_pos
95
95
  end
96
96
  end
97
97
  end
@@ -40,7 +40,7 @@ module RuboCop
40
40
  def on_send(node)
41
41
  association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
42
42
  if redundant?(node, type, name, options, foreign_key)
43
- add_offense(foreign_key_pair.loc.expression) do |corrector|
43
+ add_offense(foreign_key_pair.source_range) do |corrector|
44
44
  range = range_with_surrounding_space(foreign_key_pair.source_range, side: :left)
45
45
  range = range_with_surrounding_comma(range, :left)
46
46
 
@@ -89,7 +89,7 @@ module RuboCop
89
89
  private
90
90
 
91
91
  def autocorrect(corrector, send_node, node)
92
- corrector.remove(send_node.receiver.source_range)
92
+ corrector.remove(send_node.receiver)
93
93
  corrector.remove(send_node.loc.dot)
94
94
  corrector.remove(block_argument_range(send_node)) unless node.numblock_type?
95
95
  end
@@ -18,6 +18,8 @@ module RuboCop
18
18
  # # good
19
19
  # has_many :accounts, class_name: 'Account'
20
20
  class ReflectionClassName < Base
21
+ extend AutoCorrector
22
+
21
23
  MSG = 'Use a string value for `class_name`.'
22
24
  RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
23
25
  ALLOWED_REFLECTION_CLASS_TYPES = %i[dstr str sym].freeze
@@ -32,12 +34,18 @@ module RuboCop
32
34
  (pair (sym :class_name) #reflection_class_value?)
33
35
  PATTERN
34
36
 
37
+ def_node_matcher :const_or_string, <<~PATTERN
38
+ {$(const nil? _) (send $(const nil? _) :name) (send $(const nil? _) :to_s)}
39
+ PATTERN
40
+
35
41
  def on_send(node)
36
42
  association_with_reflection(node) do |reflection_class_name|
37
43
  return if reflection_class_name.value.send_type? && reflection_class_name.value.receiver.nil?
38
44
  return if reflection_class_name.value.lvar_type? && str_assigned?(reflection_class_name)
39
45
 
40
- add_offense(reflection_class_name.loc.expression)
46
+ add_offense(reflection_class_name.source_range) do |corrector|
47
+ autocorrect(corrector, reflection_class_name)
48
+ end
41
49
  end
42
50
  end
43
51
 
@@ -64,6 +72,14 @@ module RuboCop
64
72
  !ALLOWED_REFLECTION_CLASS_TYPES.include?(class_value.type)
65
73
  end
66
74
  end
75
+
76
+ def autocorrect(corrector, class_config)
77
+ class_value = class_config.value
78
+ replacement = const_or_string(class_value)
79
+ return unless replacement.present?
80
+
81
+ corrector.replace(class_value, replacement.source.inspect)
82
+ end
67
83
  end
68
84
  end
69
85
  end
@@ -78,7 +78,7 @@ module RuboCop
78
78
  indent = ' ' * node.loc.column
79
79
  new_code = ["def self.#{const_name.downcase}", "#{indent}#{value.source}", 'end'].join("\n#{indent}")
80
80
 
81
- corrector.replace(node.source_range, new_code)
81
+ corrector.replace(node, new_code)
82
82
  end
83
83
 
84
84
  def message(method_name)
@@ -86,7 +86,7 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def offense_range(name, value)
89
- range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
89
+ range_between(name.source_range.begin_pos, value.source_range.end_pos)
90
90
  end
91
91
 
92
92
  def nested_relative_date(node, &callback)