From af824db3989eb422dfa43dde4171796d803f0029 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sun, 30 Apr 2023 08:06:53 -0400
Subject: [PATCH] Fix Rails/InverseOf cop (#24732)

---
 .rubocop_todo.yml                           | 13 -------------
 app/models/appeal.rb                        |  2 +-
 app/models/concerns/account_interactions.rb | 18 ++++++++++++------
 app/models/custom_emoji.rb                  |  3 ++-
 app/models/domain_block.rb                  |  2 +-
 app/models/follow_recommendation.rb         |  2 +-
 app/models/instance.rb                      | 10 ++++++----
 app/models/notification.rb                  | 16 +++++++++-------
 app/models/status.rb                        |  2 +-
 9 files changed, 33 insertions(+), 35 deletions(-)

diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 5d21adfbf5..fe22385a7f 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1348,19 +1348,6 @@ Rails/I18nLocaleTexts:
     - 'lib/tasks/mastodon.rake'
     - 'spec/helpers/flashes_helper_spec.rb'
 
-# Configuration parameters: IgnoreScopes, Include.
-# Include: app/models/**/*.rb
-Rails/InverseOf:
-  Exclude:
-    - 'app/models/appeal.rb'
-    - 'app/models/concerns/account_interactions.rb'
-    - 'app/models/custom_emoji.rb'
-    - 'app/models/domain_block.rb'
-    - 'app/models/follow_recommendation.rb'
-    - 'app/models/instance.rb'
-    - 'app/models/notification.rb'
-    - 'app/models/status.rb'
-
 # Configuration parameters: Include.
 # Include: app/controllers/**/*.rb, app/mailers/**/*.rb
 Rails/LexicallyScopedActionFilter:
diff --git a/app/models/appeal.rb b/app/models/appeal.rb
index 6fbf60b39e..f1290ad01a 100644
--- a/app/models/appeal.rb
+++ b/app/models/appeal.rb
@@ -19,7 +19,7 @@ class Appeal < ApplicationRecord
   MAX_STRIKE_AGE = 20.days
 
   belongs_to :account
-  belongs_to :strike, class_name: 'AccountWarning', foreign_key: 'account_warning_id'
+  belongs_to :strike, class_name: 'AccountWarning', foreign_key: 'account_warning_id', inverse_of: :appeal
   belongs_to :approved_by_account, class_name: 'Account', optional: true
   belongs_to :rejected_by_account, class_name: 'Account', optional: true
 
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb
index 48ab1349dd..b2ccddef32 100644
--- a/app/models/concerns/account_interactions.rb
+++ b/app/models/concerns/account_interactions.rb
@@ -81,8 +81,10 @@ module AccountInteractions
     # Follow relations
     has_many :follow_requests, dependent: :destroy
 
-    has_many :active_relationships,  class_name: 'Follow', foreign_key: 'account_id',        dependent: :destroy
-    has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_account_id', dependent: :destroy
+    with_options class_name: 'Follow', dependent: :destroy do
+      has_many :active_relationships,  foreign_key: 'account_id', inverse_of: :account
+      has_many :passive_relationships, foreign_key: 'target_account_id', inverse_of: :target_account
+    end
 
     has_many :following, -> { order('follows.id desc') }, through: :active_relationships,  source: :target_account
     has_many :followers, -> { order('follows.id desc') }, through: :passive_relationships, source: :account
@@ -91,15 +93,19 @@ module AccountInteractions
     has_many :account_notes, dependent: :destroy
 
     # Block relationships
-    has_many :block_relationships, class_name: 'Block', foreign_key: 'account_id', dependent: :destroy
+    with_options class_name: 'Block', dependent: :destroy do
+      has_many :block_relationships, foreign_key: 'account_id', inverse_of: :account
+      has_many :blocked_by_relationships, foreign_key: :target_account_id, inverse_of: :target_account
+    end
     has_many :blocking, -> { order('blocks.id desc') }, through: :block_relationships, source: :target_account
-    has_many :blocked_by_relationships, class_name: 'Block', foreign_key: :target_account_id, dependent: :destroy
     has_many :blocked_by, -> { order('blocks.id desc') }, through: :blocked_by_relationships, source: :account
 
     # Mute relationships
-    has_many :mute_relationships, class_name: 'Mute', foreign_key: 'account_id', dependent: :destroy
+    with_options class_name: 'Mute', dependent: :destroy do
+      has_many :mute_relationships, foreign_key: 'account_id', inverse_of: :account
+      has_many :muted_by_relationships, foreign_key: :target_account_id, inverse_of: :target_account
+    end
     has_many :muting, -> { order('mutes.id desc') }, through: :mute_relationships, source: :target_account
-    has_many :muted_by_relationships, class_name: 'Mute', foreign_key: :target_account_id, dependent: :destroy
     has_many :muted_by, -> { order('mutes.id desc') }, through: :muted_by_relationships, source: :account
     has_many :conversation_mutes, dependent: :destroy
     has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index 3d7900226d..f66353fadd 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -36,7 +36,8 @@ class CustomEmoji < ApplicationRecord
   IMAGE_MIME_TYPES = %w(image/png image/gif image/webp).freeze
 
   belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
-  has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
+
+  has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false
 
   has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set modify-date +set create-date' } }, validate_media_type: false
 
diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb
index fbb045416c..06d35d8bee 100644
--- a/app/models/domain_block.rb
+++ b/app/models/domain_block.rb
@@ -25,7 +25,7 @@ class DomainBlock < ApplicationRecord
 
   validates :domain, presence: true, uniqueness: true, domain: true
 
-  has_many :accounts, foreign_key: :domain, primary_key: :domain
+  has_many :accounts, foreign_key: :domain, primary_key: :domain, inverse_of: false
   delegate :count, to: :accounts, prefix: true
 
   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
diff --git a/app/models/follow_recommendation.rb b/app/models/follow_recommendation.rb
index 602d329858..123570b124 100644
--- a/app/models/follow_recommendation.rb
+++ b/app/models/follow_recommendation.rb
@@ -12,7 +12,7 @@
 class FollowRecommendation < ApplicationRecord
   self.primary_key = :account_id
 
-  belongs_to :account_summary, foreign_key: :account_id
+  belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
   belongs_to :account
 
   scope :localized, ->(locale) { joins(:account_summary).merge(AccountSummary.localized(locale)) }
diff --git a/app/models/instance.rb b/app/models/instance.rb
index 1f96d37286..95231c52a7 100644
--- a/app/models/instance.rb
+++ b/app/models/instance.rb
@@ -13,11 +13,13 @@ class Instance < ApplicationRecord
 
   attr_accessor :failure_days
 
-  has_many :accounts, foreign_key: :domain, primary_key: :domain
+  has_many :accounts, foreign_key: :domain, primary_key: :domain, inverse_of: false
 
-  belongs_to :domain_block, foreign_key: :domain, primary_key: :domain
-  belongs_to :domain_allow, foreign_key: :domain, primary_key: :domain
-  belongs_to :unavailable_domain, foreign_key: :domain, primary_key: :domain # skipcq: RB-RL1031
+  with_options foreign_key: :domain, primary_key: :domain, inverse_of: false do
+    belongs_to :domain_block
+    belongs_to :domain_allow
+    belongs_to :unavailable_domain # skipcq: RB-RL1031
+  end
 
   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
 
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 3eaf557b08..8ba506fa1b 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -55,13 +55,15 @@ class Notification < ApplicationRecord
   belongs_to :from_account, class_name: 'Account', optional: true
   belongs_to :activity, polymorphic: true, optional: true
 
-  belongs_to :mention,        foreign_key: 'activity_id', optional: true
-  belongs_to :status,         foreign_key: 'activity_id', optional: true
-  belongs_to :follow,         foreign_key: 'activity_id', optional: true
-  belongs_to :follow_request, foreign_key: 'activity_id', optional: true
-  belongs_to :favourite,      foreign_key: 'activity_id', optional: true
-  belongs_to :poll,           foreign_key: 'activity_id', optional: true
-  belongs_to :report,         foreign_key: 'activity_id', optional: true
+  with_options foreign_key: 'activity_id', optional: true do
+    belongs_to :mention, inverse_of: :notification
+    belongs_to :status, inverse_of: :notification
+    belongs_to :follow, inverse_of: :notification
+    belongs_to :follow_request, inverse_of: :notification
+    belongs_to :favourite, inverse_of: :notification
+    belongs_to :poll, inverse_of: false
+    belongs_to :report, inverse_of: false
+  end
 
   validates :type, inclusion: { in: TYPES }
 
diff --git a/app/models/status.rb b/app/models/status.rb
index 302049e20e..990d0fc29e 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -56,7 +56,7 @@ class Status < ApplicationRecord
   belongs_to :account, inverse_of: :statuses
   belongs_to :in_reply_to_account, class_name: 'Account', optional: true
   belongs_to :conversation, optional: true
-  belongs_to :preloadable_poll, class_name: 'Poll', foreign_key: 'poll_id', optional: true
+  belongs_to :preloadable_poll, class_name: 'Poll', foreign_key: 'poll_id', optional: true, inverse_of: false
 
   belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
   belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
-- 
GitLab