@@ -9,7 +9,9 @@ module Rails
99 # and the cop detects it using the `expect` method.
1010 #
1111 # - Method calls on `params[:key]` without comparison methods, methods that are safe to call
12- # on `nil` (such as `to_i`, `to_s`, or `is_a?`), or key-check methods such as `key?`
12+ # on `nil` (such as `to_i`, `to_s`, or `is_a?`), key-check methods such as `key?`,
13+ # collection methods such as `keys`, `merge`, or `slice`, or block-style calls such as
14+ # `params[:key].each { ... }` or `params[:key].map(&:to_s)`
1315 # - Passing `params[:key]` as an argument to finder methods that raise on missing records
1416 # - Strong parameter methods using `require` or `permit`
1517 #
@@ -54,9 +56,18 @@ class StrongParametersExpect < Base
5456
5557 MSG = 'Use `%<prefer>s` instead.'
5658 RESTRICT_ON_SEND = %i[ [] require permit ] . freeze
57- PRESENCE_CHECK_METHODS = %i[ nil? blank? present? presence ] . freeze
58- NIL_SAFE_METHODS = %i[ instance_of? is_a? kind_of? to_a to_f to_h to_i to_s try try! ] . freeze
59- KEY_CHECK_METHODS = %i[ key? has_key? include? member? ] . freeze
59+ # Method calls on `params[:key]` that should not be rewritten with `expect(:key)`.
60+ # Covers presence/nil checks, nil-safe conversions and type checks, key-check methods,
61+ # and collection methods that imply `params[:key]` is a Hash/Array.
62+ IGNORED_METHODS = %i[
63+ blank? compact compact! compact_blank compact_blank! deep_merge deep_merge!
64+ delete delete_if dig each except exclude? extract! fetch has_key? has_value?
65+ include? instance_of? is_a? keep_if key? keys kind_of? member? merge merge!
66+ nil? presence present? reverse_merge reverse_merge! slice stringify_keys
67+ to_a to_f to_h to_hash to_i to_s to_unsafe_h to_unsafe_hash
68+ transform_keys transform_keys! transform_values transform_values! try try!
69+ value? values values_at with_defaults with_defaults! without
70+ ] . freeze
6071 RAISING_FINDER_METHODS = %i[ find find_by! find_sole_by ] . freeze
6172
6273 minimum_target_rails_version 8.0
@@ -135,12 +146,9 @@ def offensive_bracket_access?(node)
135146
136147 if parent . receiver == node
137148 return false if parent . comparison_method? || parent . method? ( :[] )
149+ return false if block_call? ( parent )
138150
139- method_name = parent . method_name
140-
141- !PRESENCE_CHECK_METHODS . include? ( method_name ) &&
142- !NIL_SAFE_METHODS . include? ( method_name ) &&
143- !KEY_CHECK_METHODS . include? ( method_name )
151+ !IGNORED_METHODS . include? ( parent . method_name )
144152 else
145153 raising_finder_method? ( parent )
146154 end
@@ -151,6 +159,10 @@ def raising_finder_method?(node)
151159 RAISING_FINDER_METHODS . include? ( node . method_name )
152160 end
153161
162+ def block_call? ( send_node )
163+ send_node . block_literal? || send_node . last_argument &.block_pass_type?
164+ end
165+
154166 def offense_range ( method_node , node )
155167 method_node . loc . selector . join ( node . source_range . end )
156168 end
0 commit comments