diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index 8d43a857dd..f3bbf4724a 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -416,6 +416,7 @@ public function invokeAll($hook, array $args = []) {
    * {@inheritdoc}
    */
   public function invokeDeprecated($description, $module, $hook, array $args = []) {
+    @trigger_error(sprintf('%s::invokeDeprecated() has been deprecated since Drupal 8.7. Declare hook_%s() deprecated by implementing hook_hook_info() instead.', ModuleHandlerInterface::class, $hook), E_USER_DEPRECATED);
     $result = $this->invoke($module, $hook, $args);
     $this->triggerDeprecationError($description, $hook);
     return $result;
@@ -425,6 +426,7 @@ public function invokeDeprecated($description, $module, $hook, array $args = [])
    * {@inheritdoc}
    */
   public function invokeAllDeprecated($description, $hook, array $args = []) {
+    @trigger_error(sprintf('%s::invokeAllDeprecated() has been deprecated since Drupal 8.7. Declare hook_%s() deprecated by implementing hook_hook_info() instead.', ModuleHandlerInterface::class, $hook), E_USER_DEPRECATED);
     $result = $this->invokeAll($hook, $args);
     $this->triggerDeprecationError($description, $hook);
     return $result;
@@ -544,6 +546,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
    * {@inheritdoc}
    */
   public function alterDeprecated($description, $type, &$data, &$context1 = NULL, &$context2 = NULL) {
+    @trigger_error(sprintf('%s::alterDeprecated() has been deprecated since Drupal 8.7. Declare hook_%s() deprecated by implementing hook_hook_info() instead.', ModuleHandlerInterface::class, is_array($type) ? array_values($type)[0] : $type), E_USER_DEPRECATED);
     // Invoke the alter hook. This has the side effect of populating
     // $this->alterFunctions.
     $this->alter($type, $data, $context1, $context2);
@@ -652,6 +655,9 @@ protected function buildImplementationInfo($hook) {
         }
       }
     }
+    if (isset($hook_info[$hook]['deprecated'])) {
+      trigger_error(sprintf('hook_%s() is deprecated: %s. However, the following modules still implement it: %s', $hook, $hook_info[$hook]['deprecated'], implode(', ', array_keys($implementations))), E_USER_DEPRECATED);
+    }
     return $implementations;
   }
 
diff --git a/core/modules/hal/hal.module b/core/modules/hal/hal.module
index 0833e47758..54baf2fde1 100644
--- a/core/modules/hal/hal.module
+++ b/core/modules/hal/hal.module
@@ -7,6 +7,20 @@
 
 use Drupal\Core\Routing\RouteMatchInterface;
 
+/**
+ * Implements hook_hook_info().
+ */
+function hal_hook_info() {
+  return [
+    'rest_relation_uri_alter' => [
+      'deprecated' => 'This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_relation_uri_alter() instead.',
+    ],
+    'rest_type_uri_alter' => [
+      'deprecated' => 'This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_type_uri_alter() instead.',
+    ],
+  ];
+}
+
 /**
  * Implements hook_help().
  */
diff --git a/core/modules/hal/src/LinkManager/RelationLinkManager.php b/core/modules/hal/src/LinkManager/RelationLinkManager.php
index cf858c7008..502193c695 100644
--- a/core/modules/hal/src/LinkManager/RelationLinkManager.php
+++ b/core/modules/hal/src/LinkManager/RelationLinkManager.php
@@ -70,7 +70,7 @@ public function getRelationUri($entity_type, $bundle, $field_name, $context = []
     // override the RelationLinkManager class/service to return the desired URL.
     $uri = $this->getLinkDomain($context) . "/rest/relation/$entity_type/$bundle/$field_name";
     $this->moduleHandler->alter('hal_relation_uri', $uri, $context);
-    $this->moduleHandler->alterDeprecated('This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_relation_uri_alter() instead.', 'rest_relation_uri', $uri, $context);
+    $this->moduleHandler->alter('rest_relation_uri', $uri, $context);
     return $uri;
   }
 
diff --git a/core/modules/hal/src/LinkManager/TypeLinkManager.php b/core/modules/hal/src/LinkManager/TypeLinkManager.php
index 62be23d2bc..4191242551 100644
--- a/core/modules/hal/src/LinkManager/TypeLinkManager.php
+++ b/core/modules/hal/src/LinkManager/TypeLinkManager.php
@@ -71,7 +71,7 @@ public function getTypeUri($entity_type, $bundle, $context = []) {
     // TypeLinkManager class/service to return the desired URL.
     $uri = $this->getLinkDomain($context) . "/rest/type/$entity_type/$bundle";
     $this->moduleHandler->alter('hal_type_uri', $uri, $context);
-    $this->moduleHandler->alterDeprecated('This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_type_uri_alter() instead.', 'rest_type_uri', $uri, $context);
+    $this->moduleHandler->alter('rest_type_uri', $uri, $context);
     return $uri;
   }
 
diff --git a/core/modules/hal/tests/src/Kernel/HalLinkManagerTest.php b/core/modules/hal/tests/src/Kernel/HalLinkManagerTest.php
index 05a6b39f1e..a043a5592c 100644
--- a/core/modules/hal/tests/src/Kernel/HalLinkManagerTest.php
+++ b/core/modules/hal/tests/src/Kernel/HalLinkManagerTest.php
@@ -50,7 +50,8 @@ protected function setUp() {
   /**
    * @covers ::getTypeUri
    * @dataProvider providerTestGetTypeUri
-   * @expectedDeprecation The deprecated alter hook hook_rest_type_uri_alter() is implemented in these functions: hal_test_rest_type_uri_alter. This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_type_uri_alter() instead.
+   *
+   * @expectedDeprecation %sThis hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_type_uri_alter() instead.%s
    */
   public function testGetTypeUri($link_domain, $entity_type, $bundle, array $context, $expected_return, array $expected_context) {
     $hal_settings = \Drupal::configFactory()->getEditable('hal.settings');
@@ -144,7 +145,8 @@ public function providerTestGetTypeUri() {
   /**
    * @covers ::getRelationUri
    * @dataProvider providerTestGetRelationUri
-   * @expectedDeprecation The deprecated alter hook hook_rest_relation_uri_alter() is implemented in these functions: hal_test_rest_relation_uri_alter. This hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_relation_uri_alter() instead.
+   *
+   * @expectedDeprecation %sThis hook is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Implement hook_hal_relation_uri_alter() instead.%s
    */
   public function testGetRelationUri($link_domain, $entity_type, $bundle, $field_name, array $context, $expected_return, array $expected_context) {
     $hal_settings = \Drupal::configFactory()->getEditable('hal.settings');
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 0a9c8e524a..cefe6f2836 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -17,6 +17,17 @@
 use Symfony\Component\Process\PhpExecutableFinder;
 use Drupal\Core\Test\TestStatus;
 
+/**
+ * Implements hook_hook_info().
+ */
+function simpletest_hook_info() {
+  return [
+    'simpletest' => [
+      'deprecated' => 'Convert your test to a PHPUnit-based one and implement test listeners. See: https://www.drupal.org/node/2939892',
+    ],
+  ];
+}
+
 /**
  * Implements hook_help().
  */
diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php
index 92c12fe609..1d1863458e 100644
--- a/core/modules/simpletest/src/TestDiscovery.php
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -209,7 +209,7 @@ public function getTestClasses($extension = NULL, array $types = []) {
     }
 
     // Allow modules extending core tests to disable originals.
-    $this->moduleHandler->alterDeprecated('Convert your test to a PHPUnit-based one and implement test listeners. See: https://www.drupal.org/node/2939892', 'simpletest', $list);
+    $this->moduleHandler->alter('simpletest', $list);
 
     if (!isset($extension) && empty($types)) {
       $this->testClasses = $list;
diff --git a/core/modules/simpletest/tests/src/Kernel/TestDiscoveryDeprecationTest.php b/core/modules/simpletest/tests/src/Kernel/TestDiscoveryDeprecationTest.php
index 6d4f44888a..50a995ae11 100644
--- a/core/modules/simpletest/tests/src/Kernel/TestDiscoveryDeprecationTest.php
+++ b/core/modules/simpletest/tests/src/Kernel/TestDiscoveryDeprecationTest.php
@@ -18,7 +18,7 @@ class TestDiscoveryDeprecationTest extends KernelTestBase {
   public static $modules = ['simpletest', 'simpletest_deprecation_test'];
 
   /**
-   * @expectedDeprecation The deprecated alter hook hook_simpletest_alter() is implemented in these functions: simpletest_deprecation_test_simpletest_alter. Convert your test to a PHPUnit-based one and implement test listeners. See: https://www.drupal.org/node/2939892
+   * @expectedDeprecation %sConvert your test to a PHPUnit-based one and implement test listeners. See: https://www.drupal.org/node/2939892%s
    * @covers ::getTestClasses
    */
   public function testHookSimpletestAlter() {
diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ModuleHandlerDeprecatedHookUnimplementedTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ModuleHandlerDeprecatedHookUnimplementedTest.php
index 1d4e833e82..dc96dc0ac3 100644
--- a/core/tests/Drupal/KernelTests/Core/Extension/ModuleHandlerDeprecatedHookUnimplementedTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleHandlerDeprecatedHookUnimplementedTest.php
@@ -8,6 +8,7 @@
  * Test whether unimplemented deprecated hook invocations trigger errors.
  *
  * @group Extension
+ * @group legacy
  *
  * @coversDefaultClass Drupal\Core\Extension\ModuleHandler
  */
@@ -17,6 +18,10 @@ class ModuleHandlerDeprecatedHookUnimplementedTest extends KernelTestBase {
    * @covers ::alterDeprecated
    * @covers ::invokeAllDeprecated
    * @covers ::invokeDeprecated
+   *
+   * @expectedDeprecation Drupal\Core\Extension\ModuleHandlerInterface::invokeDeprecated() has been deprecated since Drupal 8.7. Declare hook_unimplemented_hook_name() deprecated by implementing hook_hook_info() instead.
+   * @expectedDeprecation Drupal\Core\Extension\ModuleHandlerInterface::invokeAllDeprecated() has been deprecated since Drupal 8.7. Declare hook_unimplemented_hook_name() deprecated by implementing hook_hook_info() instead.
+   * @expectedDeprecation Drupal\Core\Extension\ModuleHandlerInterface::alterDeprecated() has been deprecated since Drupal 8.7. Declare hook_unimplemented_hook_name() deprecated by implementing hook_hook_info() instead.
    */
   public function testUnimplementedHooks() {
     $unimplemented_hook_name = 'unimplemented_hook_name';
