rubocop-rails 2.19.1 → 2.21.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +86 -12
  4. data/lib/rubocop/cop/mixin/index_method.rb +2 -2
  5. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +1 -1
  6. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  7. data/lib/rubocop/cop/rails/assert_not.rb +0 -1
  8. data/lib/rubocop/cop/rails/bulk_change_table.rb +20 -3
  9. data/lib/rubocop/cop/rails/dangerous_column_names.rb +439 -0
  10. data/lib/rubocop/cop/rails/date.rb +12 -3
  11. data/lib/rubocop/cop/rails/duplicate_association.rb +3 -0
  12. data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
  13. data/lib/rubocop/cop/rails/file_path.rb +129 -13
  14. data/lib/rubocop/cop/rails/find_each.rb +1 -1
  15. data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
  16. data/lib/rubocop/cop/rails/http_status.rb +4 -3
  17. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
  18. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +5 -1
  19. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +22 -2
  20. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +9 -10
  21. data/lib/rubocop/cop/rails/not_null_column.rb +1 -1
  22. data/lib/rubocop/cop/rails/rake_environment.rb +20 -4
  23. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +168 -0
  24. data/lib/rubocop/cop/rails/refute_methods.rb +0 -1
  25. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
  26. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
  27. data/lib/rubocop/cop/rails/save_bang.rb +2 -2
  28. data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
  29. data/lib/rubocop/cop/rails/select_map.rb +78 -0
  30. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +0 -1
  31. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +2 -4
  32. data/lib/rubocop/cop/rails/time_zone.rb +12 -5
  33. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +35 -9
  34. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +15 -19
  35. data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
  36. data/lib/rubocop/cop/rails/where_exists.rb +0 -1
  37. data/lib/rubocop/cop/rails_cops.rb +4 -0
  38. data/lib/rubocop/rails/schema_loader/schema.rb +4 -4
  39. data/lib/rubocop/rails/schema_loader.rb +1 -1
  40. data/lib/rubocop/rails/version.rb +1 -1
  41. data/lib/rubocop-rails.rb +8 -0
  42. metadata +8 -4
@@ -35,6 +35,8 @@ module RuboCop
35
35
  # Rails.root.join('app', 'models', 'goober').to_s
36
36
  #
37
37
  class FilePath < Base
38
+ extend AutoCorrector
39
+
38
40
  include ConfigurableEnforcedStyle
39
41
  include RangeHelp
40
42
 
@@ -56,13 +58,10 @@ module RuboCop
56
58
 
57
59
  def on_dstr(node)
58
60
  return unless rails_root_nodes?(node)
59
- return unless node.children.last.str_type?
60
-
61
- last_child_source = node.children.last.source
62
- return unless last_child_source.start_with?('.') || last_child_source.include?(File::SEPARATOR)
63
- return if last_child_source.start_with?(':')
61
+ return if dstr_separated_by_colon?(node)
64
62
 
65
- register_offense(node, require_to_s: true)
63
+ check_for_slash_after_rails_root_in_dstr(node)
64
+ check_for_extension_after_rails_root_join_in_dstr(node)
66
65
  end
67
66
 
68
67
  def on_send(node)
@@ -73,11 +72,33 @@ module RuboCop
73
72
 
74
73
  private
75
74
 
75
+ def check_for_slash_after_rails_root_in_dstr(node)
76
+ rails_root_index = find_rails_root_index(node)
77
+ slash_node = node.children[rails_root_index + 1]
78
+ return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
79
+
80
+ register_offense(node, require_to_s: false) do |corrector|
81
+ autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
82
+ end
83
+ end
84
+
85
+ def check_for_extension_after_rails_root_join_in_dstr(node)
86
+ rails_root_index = find_rails_root_index(node)
87
+ extension_node = node.children[rails_root_index + 1]
88
+ return unless extension_node?(extension_node)
89
+
90
+ register_offense(node, require_to_s: false) do |corrector|
91
+ autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
92
+ end
93
+ end
94
+
76
95
  def check_for_file_join_with_rails_root(node)
77
96
  return unless file_join_nodes?(node)
78
97
  return unless node.arguments.any? { |e| rails_root_nodes?(e) }
79
98
 
80
- register_offense(node, require_to_s: true)
99
+ register_offense(node, require_to_s: true) do |corrector|
100
+ autocorrect_file_join(corrector, node)
101
+ end
81
102
  end
82
103
 
83
104
  def check_for_rails_root_join_with_string_arguments(node)
@@ -87,7 +108,9 @@ module RuboCop
87
108
  return unless node.arguments.size > 1
88
109
  return unless node.arguments.all?(&:str_type?)
89
110
 
90
- register_offense(node, require_to_s: false)
111
+ register_offense(node, require_to_s: false) do |corrector|
112
+ autocorrect_rails_root_join_with_string_arguments(corrector, node)
113
+ end
91
114
  end
92
115
 
93
116
  def check_for_rails_root_join_with_slash_separated_path(node)
@@ -96,21 +119,22 @@ module RuboCop
96
119
  return unless rails_root_join_nodes?(node)
97
120
  return unless node.arguments.any? { |arg| string_with_slash?(arg) }
98
121
 
99
- register_offense(node, require_to_s: false)
122
+ register_offense(node, require_to_s: false) do |corrector|
123
+ autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
124
+ end
100
125
  end
101
126
 
102
127
  def string_with_slash?(node)
103
- node.str_type? && node.source.include?('/')
128
+ node.str_type? && node.source.include?(File::SEPARATOR)
104
129
  end
105
130
 
106
- def register_offense(node, require_to_s:)
131
+ def register_offense(node, require_to_s:, &block)
107
132
  line_range = node.loc.column...node.loc.last_column
108
133
  source_range = source_range(processed_source.buffer, node.first_line, line_range)
109
- require_to_s = false if node.dstr_type?
110
134
 
111
135
  message = build_message(require_to_s)
112
136
 
113
- add_offense(source_range, message: message)
137
+ add_offense(source_range, message: message, &block)
114
138
  end
115
139
 
116
140
  def build_message(require_to_s)
@@ -119,6 +143,98 @@ module RuboCop
119
143
 
120
144
  format(message_template, to_s: to_s)
121
145
  end
146
+
147
+ def dstr_separated_by_colon?(node)
148
+ node.children[1..].any? do |child|
149
+ child.str_type? && child.source.start_with?(':')
150
+ end
151
+ end
152
+
153
+ def autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
154
+ rails_root_node = node.children[rails_root_index].children.first
155
+ argument_source = extract_rails_root_join_argument_source(node, rails_root_index)
156
+ if rails_root_node.method?(:join)
157
+ append_argument(corrector, rails_root_node, argument_source)
158
+ else
159
+ replace_with_rails_root_join(corrector, rails_root_node, argument_source)
160
+ end
161
+ node.children[rails_root_index + 1..].each { |child| corrector.remove(child) }
162
+ end
163
+
164
+ def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
165
+ rails_root_node = node.children[rails_root_index].children.first
166
+ return unless rails_root_node.arguments.last.str_type?
167
+
168
+ corrector.insert_before(rails_root_node.arguments.last.location.end, extension_node.source)
169
+ corrector.remove(extension_node)
170
+ end
171
+
172
+ def autocorrect_file_join(corrector, node)
173
+ corrector.replace(node.receiver, 'Rails.root')
174
+ corrector.remove(
175
+ range_with_surrounding_space(
176
+ range_with_surrounding_comma(
177
+ node.arguments.first.source_range,
178
+ :right
179
+ ),
180
+ side: :right
181
+ )
182
+ )
183
+ node.arguments.filter(&:str_type?).each do |argument|
184
+ corrector.replace(argument, argument.value.delete_prefix('/').inspect)
185
+ end
186
+ corrector.insert_after(node, '.to_s')
187
+ end
188
+
189
+ def autocorrect_rails_root_join_with_string_arguments(corrector, node)
190
+ corrector.replace(node.arguments.first, %("#{node.arguments.map(&:value).join('/')}"))
191
+ node.arguments[1..].each do |argument|
192
+ corrector.remove(
193
+ range_with_surrounding_comma(
194
+ range_with_surrounding_space(
195
+ argument.source_range,
196
+ side: :left
197
+ ),
198
+ :left
199
+ )
200
+ )
201
+ end
202
+ end
203
+
204
+ def autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
205
+ node.arguments.each do |argument|
206
+ next unless string_with_slash?(argument)
207
+
208
+ index = argument.source.index(File::SEPARATOR)
209
+ rest = inner_range_of(argument).adjust(begin_pos: index - 1)
210
+ corrector.remove(rest)
211
+ corrector.insert_after(argument, %(, "#{rest.source.delete_prefix(File::SEPARATOR)}"))
212
+ end
213
+ end
214
+
215
+ def inner_range_of(node)
216
+ node.location.end.with(begin_pos: node.location.begin.end_pos).adjust(end_pos: -1)
217
+ end
218
+
219
+ def find_rails_root_index(node)
220
+ node.children.index { |child| rails_root_nodes?(child) }
221
+ end
222
+
223
+ def append_argument(corrector, node, argument_source)
224
+ corrector.insert_after(node.arguments.last, %(, "#{argument_source}"))
225
+ end
226
+
227
+ def replace_with_rails_root_join(corrector, node, argument_source)
228
+ corrector.replace(node, %<Rails.root.join("#{argument_source}")>)
229
+ end
230
+
231
+ def extract_rails_root_join_argument_source(node, rails_root_index)
232
+ node.children[rails_root_index + 1..].map(&:source).join.delete_prefix(File::SEPARATOR)
233
+ end
234
+
235
+ def extension_node?(node)
236
+ node&.str_type? && node.source.match?(/\A\.[A-Za-z]+/)
237
+ end
122
238
  end
123
239
  end
124
240
  end
@@ -34,7 +34,7 @@ module RuboCop
34
34
  RESTRICT_ON_SEND = %i[each].freeze
35
35
 
36
36
  SCOPE_METHODS = %i[
37
- all eager_load includes joins left_joins left_outer_joins not preload
37
+ all eager_load includes joins left_joins left_outer_joins not or preload
38
38
  references unscoped where
39
39
  ].freeze
40
40
 
@@ -69,7 +69,7 @@ module RuboCop
69
69
  return false unless CONVERT_METHODS.include?(method_name)
70
70
 
71
71
  child_node, child_method_name, time_argument = *node.children
72
- return if time_argument
72
+ return false if time_argument
73
73
 
74
74
  current_time?(child_node, child_method_name)
75
75
  end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # @example EnforcedStyle: symbolic (default)
9
9
  # # bad
10
10
  # render :foo, status: 200
11
+ # render :foo, status: '200'
11
12
  # render json: { foo: 'bar' }, status: 200
12
13
  # render plain: 'foo/bar', status: 304
13
14
  # redirect_to root_url, status: 301
@@ -50,7 +51,7 @@ module RuboCop
50
51
  PATTERN
51
52
 
52
53
  def_node_matcher :status_code, <<~PATTERN
53
- (hash <(pair (sym :status) ${int sym}) ...>)
54
+ (hash <(pair (sym :status) ${int sym str}) ...>)
54
55
  PATTERN
55
56
 
56
57
  def on_send(node)
@@ -108,7 +109,7 @@ module RuboCop
108
109
  private
109
110
 
110
111
  def symbol
111
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
112
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number.to_i)
112
113
  end
113
114
 
114
115
  def number
@@ -133,7 +134,7 @@ module RuboCop
133
134
  end
134
135
 
135
136
  def offensive?
136
- !node.int_type? && !permitted_symbol?
137
+ !node.int_type? && !permitted_symbol? && number
137
138
  end
138
139
 
139
140
  def message
@@ -5,7 +5,13 @@ module RuboCop
5
5
  module Rails
6
6
  # Checks for places where I18n "lazy" lookup can be used.
7
7
  #
8
- # @example
8
+ # This cop has two different enforcement modes. When the EnforcedStyle
9
+ # is `lazy` (the default), explicit lookups are added as offenses.
10
+ #
11
+ # When the EnforcedStyle is `explicit` then lazy lookups are added as
12
+ # offenses.
13
+ #
14
+ # @example EnforcedStyle: lazy (default)
9
15
  # # en.yml
10
16
  # # en:
11
17
  # # books:
@@ -28,11 +34,29 @@ module RuboCop
28
34
  # end
29
35
  # end
30
36
  #
37
+ # @example EnforcedStyle: explicit
38
+ # # bad
39
+ # class BooksController < ApplicationController
40
+ # def create
41
+ # # ...
42
+ # redirect_to books_url, notice: t('.success')
43
+ # end
44
+ # end
45
+ #
46
+ # # good
47
+ # class BooksController < ApplicationController
48
+ # def create
49
+ # # ...
50
+ # redirect_to books_url, notice: t('books.create.success')
51
+ # end
52
+ # end
53
+ #
31
54
  class I18nLazyLookup < Base
55
+ include ConfigurableEnforcedStyle
32
56
  include VisibilityHelp
33
57
  extend AutoCorrector
34
58
 
35
- MSG = 'Use "lazy" lookup for the text used in controllers.'
59
+ MSG = 'Use %<style>s lookup for the text used in controllers.'
36
60
 
37
61
  RESTRICT_ON_SEND = %i[translate t].freeze
38
62
 
@@ -42,23 +66,45 @@ module RuboCop
42
66
 
43
67
  def on_send(node)
44
68
  translate_call?(node) do |key_node|
45
- key = key_node.value
46
- return if key.to_s.start_with?('.')
69
+ case style
70
+ when :lazy
71
+ handle_lazy_style(node, key_node)
72
+ when :explicit
73
+ handle_explicit_style(node, key_node)
74
+ end
75
+ end
76
+ end
77
+
78
+ private
47
79
 
48
- controller, action = controller_and_action(node)
49
- return unless controller && action
80
+ def handle_lazy_style(node, key_node)
81
+ key = key_node.value
82
+ return if key.to_s.start_with?('.')
50
83
 
51
- scoped_key = get_scoped_key(key_node, controller, action)
52
- return unless key == scoped_key
84
+ controller, action = controller_and_action(node)
85
+ return unless controller && action
53
86
 
54
- add_offense(key_node) do |corrector|
55
- unscoped_key = key_node.value.to_s.split('.').last
56
- corrector.replace(key_node, "'.#{unscoped_key}'")
57
- end
87
+ scoped_key = get_scoped_key(key_node, controller, action)
88
+ return unless key == scoped_key
89
+
90
+ add_offense(key_node) do |corrector|
91
+ unscoped_key = key_node.value.to_s.split('.').last
92
+ corrector.replace(key_node, "'.#{unscoped_key}'")
58
93
  end
59
94
  end
60
95
 
61
- private
96
+ def handle_explicit_style(node, key_node)
97
+ key = key_node.value
98
+ return unless key.to_s.start_with?('.')
99
+
100
+ controller, action = controller_and_action(node)
101
+ return unless controller && action
102
+
103
+ scoped_key = get_scoped_key(key_node, controller, action)
104
+ add_offense(key_node) do |corrector|
105
+ corrector.replace(key_node, "'#{scoped_key}'")
106
+ end
107
+ end
62
108
 
63
109
  def controller_and_action(node)
64
110
  action_node = node.each_ancestor(:def).first
@@ -90,6 +136,10 @@ module RuboCop
90
136
 
91
137
  path.delete_suffix('Controller').underscore
92
138
  end
139
+
140
+ def message(_range)
141
+ format(MSG, style: style)
142
+ end
93
143
  end
94
144
  end
95
145
  end
@@ -80,7 +80,11 @@ module RuboCop
80
80
  PATTERN
81
81
 
82
82
  def_node_matcher :flash_assignment?, <<~PATTERN
83
- (send (send nil? :flash) :[]= _ $str)
83
+ (send
84
+ {
85
+ (send nil? :flash)
86
+ (send (send nil? :flash) :now)
87
+ } :[]= _ $str)
84
88
  PATTERN
85
89
 
86
90
  def_node_search :mail_subject, <<~PATTERN
@@ -36,6 +36,10 @@ module RuboCop
36
36
  # if: -> { trusted_origin? && action_name != "admin" }
37
37
  # end
38
38
  class IgnoredSkipActionFilterOption < Base
39
+ extend AutoCorrector
40
+
41
+ include RangeHelp
42
+
39
43
  MSG = <<~MSG.chomp.freeze
40
44
  `%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
41
45
  MSG
@@ -60,9 +64,13 @@ module RuboCop
60
64
  options = options_hash(options)
61
65
 
62
66
  if if_and_only?(options)
63
- add_offense(options[:if], message: format(MSG, prefer: :only, ignore: :if))
67
+ add_offense(options[:if], message: format(MSG, prefer: :only, ignore: :if)) do |corrector|
68
+ remove_node_with_left_space_and_comma(corrector, options[:if])
69
+ end
64
70
  elsif if_and_except?(options)
65
- add_offense(options[:except], message: format(MSG, prefer: :if, ignore: :except))
71
+ add_offense(options[:except], message: format(MSG, prefer: :if, ignore: :except)) do |corrector|
72
+ remove_node_with_left_space_and_comma(corrector, options[:except])
73
+ end
66
74
  end
67
75
  end
68
76
 
@@ -81,6 +89,18 @@ module RuboCop
81
89
  def if_and_except?(options)
82
90
  options.key?(:if) && options.key?(:except)
83
91
  end
92
+
93
+ def remove_node_with_left_space_and_comma(corrector, node)
94
+ corrector.remove(
95
+ range_with_surrounding_comma(
96
+ range_with_surrounding_space(
97
+ node.source_range,
98
+ side: :left
99
+ ),
100
+ :left
101
+ )
102
+ )
103
+ end
84
104
  end
85
105
  end
86
106
  end
@@ -122,24 +122,23 @@ module RuboCop
122
122
  parent = node.each_ancestor(:class, :module).first
123
123
  return unless parent
124
124
 
125
+ # NOTE: a `:begin` node may not exist if the class/module consists of a single statement
125
126
  block = parent.each_child_node(:begin).first
126
- return unless block
127
-
128
127
  defined_action_methods = defined_action_methods(block)
129
128
 
130
- methods = array_values(methods_node).reject do |method|
131
- defined_action_methods.include?(method)
132
- end
129
+ unmatched_methods = array_values(methods_node) - defined_action_methods
130
+ return if unmatched_methods.empty?
133
131
 
134
- message = message(methods, parent)
135
- add_offense(node, message: message) unless methods.empty?
132
+ message = message(unmatched_methods, parent)
133
+ add_offense(node, message: message)
136
134
  end
137
135
 
138
136
  private
139
137
 
140
138
  def defined_action_methods(block)
141
- defined_methods = block.each_child_node(:def).map(&:method_name)
139
+ return [] unless block
142
140
 
141
+ defined_methods = block.each_child_node(:def).map(&:method_name)
143
142
  defined_methods + aliased_action_methods(block, defined_methods)
144
143
  end
145
144
 
@@ -176,14 +175,14 @@ module RuboCop
176
175
  when :sym
177
176
  [node.value]
178
177
  when :array
179
- node.values.map do |v|
178
+ node.values.filter_map do |v|
180
179
  case v.type
181
180
  when :str
182
181
  v.str_content.to_sym
183
182
  when :sym
184
183
  v.value
185
184
  end
186
- end.compact
185
+ end
187
186
  else
188
187
  []
189
188
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  def check_add_column(node)
47
47
  add_not_null_column?(node) do |type, pairs|
48
- return if type.value == :virtual || type.value == 'virtual'
48
+ return if type.respond_to?(:value) && (type.value == :virtual || type.value == 'virtual')
49
49
 
50
50
  check_pairs(pairs)
51
51
  end
@@ -45,16 +45,24 @@ module RuboCop
45
45
  return if with_dependencies?(task_method)
46
46
 
47
47
  add_offense(task_method) do |corrector|
48
- task_name = task_method.arguments[0]
49
- task_dependency = correct_task_dependency(task_name)
50
-
51
- corrector.replace(task_name, task_dependency)
48
+ if with_arguments?(task_method)
49
+ new_task_dependency = correct_task_arguments_dependency(task_method)
50
+ corrector.replace(task_arguments(task_method), new_task_dependency)
51
+ else
52
+ task_name = task_method.first_argument
53
+ new_task_dependency = correct_task_dependency(task_name)
54
+ corrector.replace(task_name, new_task_dependency)
55
+ end
52
56
  end
53
57
  end
54
58
  end
55
59
 
56
60
  private
57
61
 
62
+ def correct_task_arguments_dependency(task_method)
63
+ "#{task_arguments(task_method).source} => :environment"
64
+ end
65
+
58
66
  def correct_task_dependency(task_name)
59
67
  if task_name.sym_type?
60
68
  "#{task_name.source.delete(':|\'|"')}: :environment"
@@ -80,6 +88,14 @@ module RuboCop
80
88
  end
81
89
  end
82
90
 
91
+ def task_arguments(node)
92
+ node.arguments[1]
93
+ end
94
+
95
+ def with_arguments?(node)
96
+ node.arguments.size > 1 && node.arguments[1].array_type?
97
+ end
98
+
83
99
  def with_dependencies?(node)
84
100
  first_arg = node.arguments[0]
85
101
  return false unless first_arg
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Detect redundant `all` used as a receiver for Active Record query methods.
7
+ #
8
+ # @safety
9
+ # This cop is unsafe for autocorrection if the receiver for `all` is not an Active Record object.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # User.all.find(id)
14
+ # User.all.order(:created_at)
15
+ # users.all.where(id: ids)
16
+ # user.articles.all.order(:created_at)
17
+ #
18
+ # # good
19
+ # User.find(id)
20
+ # User.order(:created_at)
21
+ # users.where(id: ids)
22
+ # user.articles.order(:created_at)
23
+ #
24
+ # @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
25
+ # # good
26
+ # ActionMailer::Preview.all.first
27
+ # ActiveSupport::TimeZone.all.first
28
+ class RedundantActiveRecordAllMethod < Base
29
+ include ActiveRecordHelper
30
+ include AllowedReceivers
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Redundant `all` detected.'
35
+
36
+ RESTRICT_ON_SEND = [:all].freeze
37
+
38
+ # Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.0.5.
39
+ QUERYING_METHODS = %i[
40
+ and
41
+ annotate
42
+ any?
43
+ average
44
+ calculate
45
+ count
46
+ create_or_find_by
47
+ create_or_find_by!
48
+ create_with
49
+ delete_all
50
+ delete_by
51
+ destroy_all
52
+ destroy_by
53
+ distinct
54
+ eager_load
55
+ except
56
+ excluding
57
+ exists?
58
+ extending
59
+ extract_associated
60
+ fifth
61
+ fifth!
62
+ find
63
+ find_by
64
+ find_by!
65
+ find_each
66
+ find_in_batches
67
+ find_or_create_by
68
+ find_or_create_by!
69
+ find_or_initialize_by
70
+ find_sole_by
71
+ first
72
+ first!
73
+ first_or_create
74
+ first_or_create!
75
+ first_or_initialize
76
+ forty_two
77
+ forty_two!
78
+ fourth
79
+ fourth!
80
+ from
81
+ group
82
+ having
83
+ ids
84
+ in_batches
85
+ in_order_of
86
+ includes
87
+ invert_where
88
+ joins
89
+ last
90
+ last!
91
+ left_joins
92
+ left_outer_joins
93
+ limit
94
+ lock
95
+ many?
96
+ maximum
97
+ merge
98
+ minimum
99
+ none
100
+ none?
101
+ offset
102
+ one?
103
+ only
104
+ optimizer_hints
105
+ or
106
+ order
107
+ pick
108
+ pluck
109
+ preload
110
+ readonly
111
+ references
112
+ reorder
113
+ reselect
114
+ rewhere
115
+ second
116
+ second!
117
+ second_to_last
118
+ second_to_last!
119
+ select
120
+ sole
121
+ strict_loading
122
+ sum
123
+ take
124
+ take!
125
+ third
126
+ third!
127
+ third_to_last
128
+ third_to_last!
129
+ touch_all
130
+ unscope
131
+ update_all
132
+ where
133
+ without
134
+ ].to_set.freeze
135
+
136
+ POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
137
+
138
+ def_node_matcher :followed_by_query_method?, <<~PATTERN
139
+ (send (send _ :all) QUERYING_METHODS ...)
140
+ PATTERN
141
+
142
+ def on_send(node)
143
+ return if !followed_by_query_method?(node.parent) || possible_enumerable_block_method?(node)
144
+ return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
145
+
146
+ range_of_all_method = offense_range(node)
147
+ add_offense(range_of_all_method) do |collector|
148
+ collector.remove(range_of_all_method)
149
+ collector.remove(node.parent.loc.dot)
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def possible_enumerable_block_method?(node)
156
+ parent = node.parent
157
+ return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
158
+
159
+ parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
160
+ end
161
+
162
+ def offense_range(node)
163
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end