class RuboCop::Cop::Rails::RedundantPresenceValidationOnBelongsTo


belongs_to :author, foreign_key: :user_id
# good
belongs_to :user
# good
validates :user_id, presence: true
belongs_to :author, foreign_key: :user_id
# bad
validates :user_id, presence: true
belongs_to :user
# bad
validates :user, presence: true
belongs_to :user
# bad
@example
from “can’t be blank” to “must exist”.
This cop’s autocorrection is unsafe because it changes the default error message
@safety
automatically, and explicit presence validation is redundant.
explicitly set to ‘false`. The presence validator is added
unless `config.active_record.belongs_to_required_by_default` is
Since Rails 5.0 the default for `belongs_to` is `optional: false`

def add_offense_and_correct(node, all_keys, keys, options, presence)

def add_offense_and_correct(node, all_keys, keys, options, presence)
  add_offense(presence, message: message_for(keys)) do |corrector|
    if options.children.one? # `presence: true` is the only option
      if keys == all_keys
        remove_validation(corrector, node)
      else
        remove_keys_from_validation(corrector, node, keys)
      end
    elsif keys == all_keys
      remove_presence_option(corrector, presence)
    else
      extract_validation_for_keys(corrector, node, keys, options)
    end
  end
end

def belongs_to_for(model_class_node, key)

def belongs_to_for(model_class_node, key)
  if key.to_s.end_with?('_id')
    normalized_key = key.to_s.delete_suffix('_id').to_sym
    belongs_to?(model_class_node, key: normalized_key, fk: key)
  else
    any_belongs_to?(model_class_node, association: key)
  end
end

def extract_validation_for_keys(corrector, node, keys, options)

def extract_validation_for_keys(corrector, node, keys, options)
  indentation = ' ' * node.source_range.column
  options_without_presence = options.children.reject { |pair| pair.key.value == :presence }
  source = [
    indentation,
    'validates ',
    keys.map(&:inspect).join(', '),
    ', ',
    options_without_presence.map(&:source).join(', '),
    "\n"
  ].join
  remove_keys_from_validation(corrector, node, keys)
  corrector.insert_after(validation_range(node), source)
end

def message_for(keys)

def message_for(keys)
  display_keys = keys.map { |key| "`#{key}`" }.join('/')
  format(MSG, association: display_keys)
end

def non_optional_belongs_to(node, keys)

def non_optional_belongs_to(node, keys)
  keys.select do |key|
    belongs_to = belongs_to_for(node, key)
    belongs_to && !optional?(belongs_to)
  end
end

def on_send(node)

def on_send(node)
  presence_validation?(node) do |all_keys, options, presence|
    # If presence is the only validation option and other non-validation options
    # are present, removing it will cause rails to error.
    used_option_keys = options.keys.select(&:sym_type?).map(&:value)
    remaining_validations = used_option_keys - NON_VALIDATION_OPTIONS - [:presence]
    return if remaining_validations.none? && options.keys.length > 1
    keys = non_optional_belongs_to(node.parent, all_keys)
    return if keys.none?
    add_offense_and_correct(node, all_keys, keys, options, presence)
  end
end

def remove_keys_from_validation(corrector, node, keys)

def remove_keys_from_validation(corrector, node, keys)
  keys.each do |key|
    key_node = node.arguments.find { |arg| arg.value == key }
    key_range = range_with_surrounding_space(
      range_with_surrounding_comma(key_node.source_range, :right),
      side: :right
    )
    corrector.remove(key_range)
  end
end

def remove_presence_option(corrector, presence)

def remove_presence_option(corrector, presence)
  range = range_with_surrounding_comma(
    range_with_surrounding_space(presence.source_range, side: :left),
    :left
  )
  corrector.remove(range)
end

def remove_validation(corrector, node)

def remove_validation(corrector, node)
  corrector.remove(validation_range(node))
end

def validation_range(node)

def validation_range(node)
  range_by_whole_lines(node.source_range, include_final_newline: true)
end