Class: RuboCop::Cop::Sorbet::CallbackConditionalsBinding
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Sorbet::CallbackConditionalsBinding
- Extended by:
- AutoCorrector
- Includes:
- Alignment
- Defined in:
- lib/rubocop/cop/sorbet/callback_conditionals_binding.rb
Overview
Ensures that callback conditionals are bound to the right type so that they are type checked properly.
Auto-correction is unsafe because other libraries define similar style callbacks as Rails, but don’t always need binding to the attached class. Auto-correcting those usages can lead to false positives and auto-correction introduces new typing errors.
Constant Summary collapse
- MSG =
"Callback conditionals should be bound to the right type. Use T.bind(self, %{type})"
- RESTRICT_ON_SEND =
[ :validate, :validates, :validates_with, :before_validation, :around_validation, :before_create, :before_save, :before_destroy, :before_update, :after_create, :after_save, :after_destroy, :after_update, :after_touch, :after_initialize, :after_find, :around_create, :around_save, :around_destroy, :around_update, :before_commit, :after_commit, :after_create_commit, :after_destroy_commit, :after_rollback, :after_save_commit, :after_update_commit, :before_action, :prepend_before_action, :append_before_action, :around_action, :prepend_around_action, :append_around_action, :after_action, :prepend_after_action, :append_after_action, ].freeze
Instance Method Summary collapse
Instance Method Details
#argumentless_unbound_callable_callback_conditional?(node) ⇒ Object
81 82 83 84 85 86 87 88 89 |
# File 'lib/rubocop/cop/sorbet/callback_conditionals_binding.rb', line 81 def_node_matcher :argumentless_unbound_callable_callback_conditional?, <<~PATTERN (pair (sym {:if :unless}) # callback conditional $(block (send nil? {:lambda :proc}) # callable (args) # argumentless !`(send(const {cbase nil?} :T) :bind self $_ ) # unbound ) ) PATTERN |
#on_send(node) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/rubocop/cop/sorbet/callback_conditionals_binding.rb', line 91 def on_send(node) type = immediately_enclosing_module_name(node) return unless type node.arguments.each do |arg| next unless arg.hash_type? # Skip non-keyword arguments arg.each_child_node do |pair_node| argumentless_unbound_callable_callback_conditional?(pair_node) do |block| add_offense(pair_node, message: format(MSG, type: type)) do |corrector| block_opening_indentation = block.source_range.source_line[/\A */] block_body_indentation = block_opening_indentation + SPACE * configured_indentation_width if block.single_line? # then convert to multi-line block first # 1. Replace whitespace (if any) between the opening delimiter and the block body, # with newline and the correct indentation for the block body. preceeding_whitespace_range = block.loc.begin.end.join(block.body.source_range.begin) corrector.replace(preceeding_whitespace_range, "\n#{block_body_indentation}") # 2. Replace whitespace (if any) between the block body and the closing delimiter, # with newline and the same indentation as the block opening. trailing_whitespace_range = block.body.source_range.end.join(block.loc.end.begin) corrector.replace(trailing_whitespace_range, "\n#{block_opening_indentation}") end # Prepend the binding to the block body corrector.insert_before(block.body, "T.bind(self, #{type})\n#{block_body_indentation}") end end end end end |