diff --git a/docs/rbtools/_ext/rbt_commands.py b/docs/rbtools/_ext/rbt_commands.py
index 080179bf61a8da5f081b7c2c87490c5ba01d8874..f951dc45c1166694b8807171be7f940a152895eb 100644
--- a/docs/rbtools/_ext/rbt_commands.py
+++ b/docs/rbtools/_ext/rbt_commands.py
@@ -1,3 +1,7 @@
+"""Documentation utils for RBTools commands."""
+
+from __future__ import annotations
+
 import re
 import sys
 
@@ -282,13 +286,26 @@ class CommandOptionsDirective(Directive):
 
         return [section]
 
-    def output_option(self, option):
+    def output_option(
+        self,
+        option: Option,
+    ) -> list[nodes.paragraph]:
+        """Output the documentation for an option of a command.
+
+        Args:
+            option (rbtools.commands.base.options.Option):
+                The option.
+
+        Returns:
+            list of nodes.paragraph:
+            The documentation for the option.
+        """
         default_text = ''
 
         content = [self.format_content(option.attrs['help'])]
 
-        if 'extended_help' in option.attrs:
-            content.append(self.format_content(option.attrs['extended_help']))
+        if extended_help := option.extended_help:
+            content.append(self.format_content(extended_help))
 
         if 'default' in option.attrs:
             action = option.attrs.get('action', 'store')
@@ -302,18 +319,16 @@ class CommandOptionsDirective(Directive):
                 default_text = ('If not specified, ``%s`` is used by default.'
                                 % default)
 
-        if 'config_key' in option.attrs:
+        if config_key := option.config_key:
             if default_text:
                 default_text += (
-                    ' The default can be changed by setting :rbtconfig:`%s` '
-                    'in :ref:`rbtools-reviewboardrc`.'
-                    % option.attrs['config_key']
+                    f' The default can be changed by setting :rbtconfig:'
+                    f'`{config_key}` in :ref:`rbtools-reviewboardrc`.'
                 )
             else:
                 default_text = (
-                    'The default can be set in :rbtconfig:`%s` in '
-                    ':ref:`rbtools-reviewboardrc`.'
-                    % option.attrs['config_key']
+                    f'The default can be set in :rbtconfig:`{config_key}` in '
+                    f':ref:`rbtools-reviewboardrc`.'
                 )
 
         if default_text:
@@ -341,23 +356,35 @@ class CommandOptionsDirective(Directive):
         else:
             option_args = ', '.join(option.opts)
 
-        if 'deprecated_in' in option.attrs:
-            content.append('.. deprecated:: %s'
-                           % option.attrs['deprecated_in'])
+        if deprecated_in := option.deprecated_in:
+            depr_dir = f'.. deprecated:: {deprecated_in}'
+            depr_desc_parts: list[str] = []
+
+            if removed_in := option.removed_in:
+                depr_desc_parts.append(f'Will be removed in {removed_in}.')
+
+            if replacement := option.replacement:
+                depr_desc_parts.append(f'Use :option:`{replacement}` instead.')
+
+            depr_desc = ' '.join(depr_desc_parts)
+
+            if depr_desc:
+                depr_final_str = '\n'.join([depr_dir, depr_desc])
+            else:
+                depr_final_str = depr_dir
 
-        if 'added_in' in option.attrs:
-            content.append('.. versionadded:: %s' % option.attrs['added_in'])
+            content.append(depr_final_str)
 
-        if 'versions_changed' in option.attrs:
-            versions_changed = option.attrs['versions_changed']
+        if added_in := option.added_in:
+            content.append(f'.. versionadded:: {added_in}')
 
+        if versions_changed := option.versions_changed:
             for version in sorted(versions_changed, reverse=True):
+                version_content = self.indent_content(
+                    self.format_content(versions_changed[version]),
+                    indent_level=2)
                 content.append(
-                    '.. versionchanged:: %s\n%s' % (
-                        version,
-                        self.indent_content(
-                            self.format_content(versions_changed[version]),
-                            indent_level=2)))
+                    f'.. versionchanged:: {version}\n{version_content}')
 
         node = parse_text(
             self,
diff --git a/rbtools/commands/base/commands.py b/rbtools/commands/base/commands.py
index f2c49bc17f3d092bcbd77941babcf2e603fe6b86..98fecfe84c7a8ffe8f62714522c444e1ca4ad53f 100644
--- a/rbtools/commands/base/commands.py
+++ b/rbtools/commands/base/commands.py
@@ -44,6 +44,8 @@ from rbtools.utils.users import credentials_prompt
 from rbtools.utils.web_login import attempt_web_login
 
 if TYPE_CHECKING:
+    from typing import Sequence
+
     from rbtools.api.resource import Resource, RootResource
     from rbtools.api.transport import Transport
     from rbtools.clients.base.repository import RepositoryInfo
@@ -1137,6 +1139,8 @@ class BaseCommand:
             self._init_logging()
             logging.debug('Command line: %s', subprocess.list2cmdline(argv))
 
+            self._check_deprecated_args()
+
             try:
                 self.initialize()
             except NeedsReinitialize:
@@ -1549,6 +1553,69 @@ class BaseCommand:
         """
         raise NotImplementedError()
 
+    def get_options_flat(self) -> Sequence[Option]:
+        """Return a list of all options for the command.
+
+        This returns a flat list of options, pulling out the options that
+        are inside any option groups set on the command.
+
+        Version Added:
+            5.3.1
+
+        Returns:
+            list of rbtools.commands.base.options.Option:
+            The list of options for the command.
+        """
+        all_options: list[Option] = []
+
+        for item in self.option_list + self._global_options:
+            if isinstance(item, OptionGroup):
+                for option in item.option_list:
+                    all_options.append(option)
+            else:
+                all_options.append(item)
+
+        return all_options
+
+    def _check_deprecated_args(self) -> None:
+        """Check if any deprecated arguments have been passed to the command.
+
+        If a deprecated argument is present, a warning will be emitted for it.
+
+        Version Added:
+            5.3.1
+        """
+        deprecated_options: set[Option] = set()
+
+        for option in self.get_options_flat():
+            if option.deprecated_in:
+                deprecated_options.add(option)
+
+        for option in deprecated_options:
+            option_value = getattr(self.options, option.attrs['dest'])
+
+            if option_value != option.attrs['default']:
+                # A deprecated option has been passed.
+                assert option.deprecated_in is not None
+
+                option_name = option.opts[-1]
+                deprecated_str = 'Option %s is deprecated as of RBTools %s.'
+                deprecated_format_args: list[str] = [
+                    option_name,
+                    option.deprecated_in]
+
+                if removed_in := option.removed_in:
+                    deprecated_str = (
+                        'Option %s is deprecated as of RBTools %s and will '
+                        'be removed in %s.')
+                    deprecated_format_args.append(removed_in)
+
+                if replacement := option.replacement:
+                    deprecated_str += ' Use %s instead.'
+                    deprecated_format_args.append(replacement)
+
+                logging.warning(deprecated_str, *deprecated_format_args)
+
     def _init_logging(self) -> None:
         """Initialize logging for the command.
 
@@ -1746,6 +1813,39 @@ class BaseMultiCommand(BaseCommand):
     #: A mapping of subcommand names to argument parsers.
     subcommand_parsers: dict[str, argparse.ArgumentParser]
 
+    def get_options_flat(self) -> Sequence[Option]:
+        """Return a list of all options for the command.
+
+        This returns a flat list of options, pulling out the options that
+        are inside any option groups set on the command, and in any
+        subcommands.
+
+        Version Added:
+            5.3.1
+
+        Returns:
+            list of rbtools.commands.base.options.Option:
+            The list of options for the command.
+        """
+        all_options = list(super().get_options_flat())
+
+        for item in self.common_subcommand_option_list:
+            if isinstance(item, OptionGroup):
+                for option in item.option_list:
+                    all_options.append(option)
+            else:
+                all_options.append(item)
+
+        for command_cls in self.subcommands:
+            for item in command_cls.option_list:
+                if isinstance(item, OptionGroup):
+                    for option in item.option_list:
+                        all_options.append(option)
+                else:
+                    all_options.append(item)
+
+        return all_options
+
     def usage(
         self,
         command_cls: Optional[type[BaseSubCommand]] = None,
diff --git a/rbtools/commands/base/options.py b/rbtools/commands/base/options.py
index b92c9dcc5cd3a1d9f7fa7d8d6f128a24f734bacf..671e59fde172b52ca6b45befabfd3d1542a35c1d 100644
--- a/rbtools/commands/base/options.py
+++ b/rbtools/commands/base/options.py
@@ -10,6 +10,7 @@ from typing import Any, Optional, TYPE_CHECKING
 
 if TYPE_CHECKING:
     import argparse
+    from collections.abc import Mapping
 
     from rbtools.config import RBToolsConfig
 
@@ -17,18 +18,19 @@ if TYPE_CHECKING:
 class Option:
     """Represents an option for a command.
 
-    The arguments to the constructor should be treated like those
-    to argparse's add_argument, with the exception that the keyword
-    argument 'config_key' is also valid. If config_key is provided
-    it will be used to retrieve the config value as a default if the
-    option is not specified. This will take precedence over the
-    default argument.
+    This serves as a wrapper around the ArgumentParser options, allowing us
+    to specify additional attributes that are specific to RBTools, such as
+    defaults which will be grabbed from the configuration after it is loaded.
 
-    Serves as a wrapper around the ArgumentParser options, allowing us
-    to specify defaults which will be grabbed from the configuration
-    after it is loaded.
+    The arguments to the constructor should be treated like those
+    to argparse's :py:meth:`ArgumentParser.add_argument`, with the exception
+    of the custom arguments that are defined in the constructor.
 
     Version Added:
+        5.3.1:
+        The optional RBTools specific attributes are now stored on the class
+        instead of in ``attrs``.
+
         5.0:
         This is the new location for the old
         :py:class:`rbtools.commands.Option` class.
@@ -53,19 +55,90 @@ class Option:
     def __init__(
         self,
         *opts: str,
+        added_in: (str | None) = None,
+        config_key: (str | None) = None,
+        deprecated_in: (str | None) = None,
+        extended_help: (str | None) = None,
+        removed_in: (str | None) = None,
+        replacement: (str | None) = None,
+        versions_changed: (Mapping[str, str] | None) = None,
         **attrs,
     ) -> None:
         """Initialize the option.
 
+        Version Changed:
+            5.3.1:
+            Added explicit arguments for the optional RBTools specific
+            arguments that may be set on an option, pulling them out of
+            ``**attrs``.
+
         Args:
             *opts (tuple of str):
                 The long and short form option names.
 
+            added_in (str, optional):
+                The version the option was added in.
+
+                Version Added:
+                    5.3.1
+
+            config_key (str, optional):
+                A config key to retrieve a default value from RBTools config
+                when the option is not explicitly provided. This will take
+                precedence over any ``default`` in ``attrs``.
+
+                Version Added:
+                    5.3.1
+
+            deprecated_in (str, optional):
+                The version the option was deprecated in.
+
+                Version Added:
+                    5.3.1
+
+            extended_help (str, optional):
+                Extended help message.
+
+                Version Added:
+                    5.3.1
+
+            removed_in (str, optional):
+                The version in which the option will be removed.
+
+                Version Added:
+                    5.3.1
+
+            replacement (str, optional):
+                The new option to use instead of the deprecated option, if any.
+
+                This should be the longest form name for the option,
+                including any preceding hyphens (e.g. ``--my-option``).
+
+                Version Added:
+                    5.3.1
+
+            versions_changed (dict[str, str], optional):
+                A dict of versions in which the option changed. The keys are
+                version strings and values are change description strings.
+
+                Version Added:
+                    5.3.1
+
             **attrs (dict):
-                The attributes for the option.
+                The argparse attributes for the option.
+
+                These should be valid arguments that can be passed to
+                :py:meth:`ArgumentParser.add_argument`.
         """
         self.opts = opts
         self.attrs = attrs
+        self.added_in = added_in
+        self.config_key = config_key
+        self.deprecated_in = deprecated_in
+        self.extended_help = extended_help
+        self.removed_in = removed_in
+        self.replacement = replacement
+        self.versions_changed = versions_changed
 
     def add_to(
         self,
@@ -93,24 +166,21 @@ class Option:
         """
         attrs = self.attrs.copy()
 
-        if config is not None and 'config_key' in attrs:
-            config_key = attrs.pop('config_key')
-
+        if config is not None and (config_key := self.config_key):
             if config_key in config:
                 attrs['default'] = config[config_key]
 
-        if 'deprecated_in' in attrs:
-            attrs['help'] = (
-                '%(help)s\n[Deprecated since %(deprecated_in)s]'
-                % attrs)
-
-        # These are used for other purposes, and are not supported by
-        # argparse.
-        for attr in ('added_in',
-                     'deprecated_in',
-                     'extended_help',
-                     'versions_changed'):
-            attrs.pop(attr, None)
+        if deprecated_in := self.deprecated_in:
+            if removed_in := self.removed_in:
+                deprecated_str = (f'Deprecated since {deprecated_in} and will '
+                                  f'be removed in {removed_in}.')
+            else:
+                deprecated_str = f'Deprecated since {deprecated_in}.'
+
+            if replacement := self.replacement:
+                deprecated_str += f' Use {replacement} instead.'
+
+            attrs['help'] += f'\n[{deprecated_str}]'
 
         parent.add_argument(*self.opts, **attrs)
 
diff --git a/rbtools/commands/login.py b/rbtools/commands/login.py
index 23c4f3222e96b490b8230d26e2f855e0f9ae3353..db0992247b722269957c3b3bd88c51f8b9dcec4b 100644
--- a/rbtools/commands/login.py
+++ b/rbtools/commands/login.py
@@ -49,9 +49,11 @@ class Login(BaseCommand):
                dest='enable_logging',
                action='store_true',
                default=False,
-               help='Use --debug instead.',
+               help='With web-based login, display the login server logs.',
                added_in='5.0',
-               deprecated_in='5.3.1'),
+               deprecated_in='5.3.1',
+               removed_in='6.0',
+               replacement='--debug'),
         BaseCommand.server_options,
     ]
 
diff --git a/rbtools/commands/tests/test_command_initialization.py b/rbtools/commands/tests/test_command_initialization.py
index 66084312a47b69400dc0100fba720ffc378fe922..e8dc8005cedd399ef9efdee9e60245b0e4f831a2 100644
--- a/rbtools/commands/tests/test_command_initialization.py
+++ b/rbtools/commands/tests/test_command_initialization.py
@@ -6,17 +6,26 @@ Version Added:
 
 from __future__ import annotations
 
+import argparse
 from typing import Any
 
 from rbtools.clients import RepositoryInfo
 from rbtools.clients.git import GitClient
-from rbtools.commands.base.commands import BaseCommand
+from rbtools.commands.base.commands import (BaseCommand,
+                                            BaseMultiCommand,
+                                            BaseSubCommand)
+from rbtools.commands.base.options import Option
 from rbtools.testing import CommandTestsMixin, TestCase
 
 
-class TestCommand(BaseCommand):
+class _TestCommand(BaseCommand):
     """Command class that does nothing.
 
+    Version Changed:
+        5.3.1:
+        Renamed to ``_TestCommand`` to make sure it doesn't get picked up
+        by the test runner.
+
     Version Added:
         5.1
     """
@@ -43,14 +52,86 @@ class TestCommand(BaseCommand):
         return 0
 
 
-class CommandInitializationTests(CommandTestsMixin[TestCommand], TestCase):
+class _TestSubCommand(BaseSubCommand):
+    """Sub Command class that does nothing.
+
+    Version Added:
+        5.3.1
+    """
+
+    name = 'sub'
+    needs_api = True
+
+    option_list = [
+        BaseCommand.server_options,
+    ]
+
+    def main(self, *args) -> int:
+        """Run the command.
+
+        Args:
+            *args (tuple):
+                Positional arguments for the command.
+
+        Returns:
+            int:
+            The return code for the process.
+        """
+        return 0
+
+
+class _TestMultiCommand(BaseMultiCommand):
+    """Multi command class that does nothing.
+
+    Version Added:
+        5.1
+    """
+    subcommands = [_TestSubCommand]
+
+    common_subcommand_option_list = [
+        Option('--bar',
+               dest='bar',
+               action='store_true',
+               default=False,
+               help='Test bar description.'),
+    ]
+
+    def main(self, *args) -> int:
+        """Run the command.
+
+        Args:
+            *args (tuple):
+                Positional arguments for the command.
+
+        Returns:
+            int:
+            The return code for the process.
+        """
+        return 0
+
+
+class CommandInitializationTests(CommandTestsMixin[_TestCommand], TestCase):
     """Unit tests for command initialization.
 
     Version Added:
         5.1
     """
 
-    command_cls = TestCommand
+    command_cls = _TestCommand
+
+    def setUp(self) -> None:
+        """Set up the test case."""
+        super().setUp()
+
+        # Store _TestCommand's option list so that we can modify it in
+        # tests and then restore it afterwards.
+        self.original_option_list = self.command_cls.option_list.copy()
+
+    def tearDown(self):
+        """Tear down the test case."""
+        self.command_cls.option_list = self.original_option_list
+
+        super().tearDown()
 
     def test_no_server_url(self) -> None:
         """Testing with no configured server URL"""
@@ -109,3 +190,313 @@ class CommandInitializationTests(CommandTestsMixin[TestCommand], TestCase):
         self.assertEqual(result['exit_code'], 0)
         self.assertEqual(command.server_url,
                          'http://reviews2.example.com/')
+
+    def test_with_deprecated_option(self) -> None:
+        """Testing warning and help output when passing a deprecated option"""
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2.]'))
+
+    def test_with_deprecated_and_removed_in_option(self) -> None:
+        """Testing warning and help output when passing a deprecated option
+        that also has a removal version
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   removed_in='6.0'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2 '
+            'and will be removed in 6.0.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2 and will be removed in 6.0.]'))
+
+    def test_with_deprecated_and_replacement_option(self) -> None:
+        """Testing warning and help output when passing a deprecated option
+        that also has a replacement option
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   replacement='--debug'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2. '
+            'Use --debug instead.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2. Use --debug instead.]'))
+
+    def test_with_deprecated_full_option(self) -> None:
+        """Testing warning and help output when passing a deprecated option
+        that also has a replacement option and a removal version
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   removed_in='6.0',
+                   replacement='--debug'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2 '
+            'and will be removed in 6.0. Use --debug instead.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2 and will be removed in 6.0. '
+                  'Use --debug instead.]'))
+
+
+class MultiCommandInitializationTests(CommandTestsMixin[_TestMultiCommand],
+                                      TestCase):
+    """Unit tests for multi command initialization.
+
+    Version Added:
+        5.3.1
+    """
+
+    command_cls = _TestMultiCommand
+
+    def setUp(self) -> None:
+        """Set up the test case."""
+        super().setUp()
+
+        # Store the multi command and all sub commands' option lists so that
+        # we can modify it in tests and then restore it afterwards.
+        self.original_common_option_list = \
+            self.command_cls.common_subcommand_option_list.copy()
+
+        self.original_subcommand_option_lists = [
+            subcommand.option_list.copy()
+            for subcommand in self.command_cls.subcommands
+        ]
+
+    def tearDown(self):
+        """Tear down the test case."""
+        self.command_cls.common_subcommand_option_list = \
+            self.original_common_option_list
+
+        for i, subcommand in enumerate(self.command_cls.subcommands):
+            subcommand.option_list = self.original_subcommand_option_lists[i]
+
+        super().tearDown()
+
+    def test_with_common_deprecated_option(self) -> None:
+        """Testing warning and help output when passing a common deprecated
+        option
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.common_subcommand_option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['sub', '--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2.]'))
+
+    def test_with_common_deprecated_and_removed_in_option(self) -> None:
+        """Testing warning and help output when passing a common deprecated
+        option that also has a removal version
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.common_subcommand_option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   removed_in='6.0'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['sub', '--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2 '
+            'and will be removed in 6.0.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2 and will be removed in 6.0.]'))
+
+    def test_with_common_deprecated_and_replacement_option(self) -> None:
+        """Testing warning and help output when passing a common deprecated
+        option that also has a replacement option
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.common_subcommand_option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   replacement='--debug'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['sub', '--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2. '
+            'Use --debug instead.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2. Use --debug instead.]'))
+
+    def test_with_common_deprecated_full_option(self) -> None:
+        """Testing warning and help output when passing a common deprecated
+        option that also has a replacement option and a removal version
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.common_subcommand_option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2',
+                   removed_in='6.0',
+                   replacement='--debug'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['sub', '--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2 '
+            'and will be removed in 6.0. Use --debug instead.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2 and will be removed in 6.0. '
+                  'Use --debug instead.]'))
+
+    def test_with_sub_deprecated_option(self) -> None:
+        """Testing warning and help output when passing a sub command's
+        deprecated option
+        """
+        self.spy_on(argparse.ArgumentParser.add_argument,
+                    owner=argparse.ArgumentParser)
+        self.command_cls.subcommands[0].option_list.append(
+            Option('--foo',
+                   dest='foo',
+                   action='store_true',
+                   default=False,
+                   help='Test foo description.',
+                   deprecated_in='5.2'))
+
+        with self.assertLogs(level='WARNING') as ctx:
+            self.run_command(args=['sub', '--foo'])
+
+        self.assertEqual(
+            ctx.output[0],
+            'WARNING:root:Option --foo is deprecated as of RBTools 5.2.')
+        self.assert_spy_called_with(
+            argparse.ArgumentParser.add_argument,
+            '--foo',
+            action='store_true',
+            default=False,
+            dest='foo',
+            help=('Test foo description.\n'
+                  '[Deprecated since 5.2.]'))
