diff --git a/core/core.api.php b/core/core.api.php
index 552b9c9032..65a73d2e86 100644
--- a/core/core.api.php
+++ b/core/core.api.php
@@ -2426,7 +2426,8 @@ function hook_validation_constraint_alter(array &$definitions) {
  * markup or a set of Ajax commands. If you choose to return HTML markup, you
  * can return it as a string or a renderable array, and it will be placed in
  * the defined 'wrapper' element (see documentation above in @ref sub_form).
- * In addition, any messages returned by drupal_get_messages(), themed as in
+ * In addition, any messages returned by
+ * \Drupal\Core\Messenger\Messenger::all(), themed as in
  * status-messages.html.twig, will be prepended.
  *
  * To return commands, you need to set up an object of class
diff --git a/core/core.services.yml b/core/core.services.yml
index 3f68677200..498a87f162 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1004,7 +1004,7 @@ services:
       - { name: event_subscriber }
   form_ajax_subscriber:
     class: Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
-    arguments: ['@form_ajax_response_builder', '@string_translation']
+    arguments: ['@form_ajax_response_builder', '@string_translation', '@messenger']
     tags:
       - { name: event_subscriber }
   route_enhancer.param_conversion:
diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index ee9078335b..411c3ef448 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
 use Drupal\Core\Plugin\ContextAwarePluginBase;
 use Drupal\Component\Utility\Unicode;
@@ -26,6 +27,7 @@
 abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface, PluginWithFormsInterface {
 
   use ContextAwarePluginAssignmentTrait;
+  use MessengerTrait;
   use PluginWithFormsTrait;
 
   /**
diff --git a/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php
index 995f4b72e0..9248ac1080 100644
--- a/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php
+++ b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php
@@ -5,8 +5,7 @@
 /**
  * The interface for "messages" (#type => status_messages) blocks.
  *
- * @see drupal_set_message()
- * @see drupal_get_message()
+ * @see \Drupal\Core\Messenger\MessengerInterface
  * @see \Drupal\Core\Render\Element\StatusMessages
  * @see \Drupal\block\Plugin\DisplayVariant\BlockPageVariant
  *
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
index 32c48d8a03..c87c08dc0a 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
@@ -73,7 +73,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $form_state->setRedirectUrl($this->getRedirectUrl());
     }
 
-    drupal_set_message($this->getDeletionMessage());
+    $this->messenger()->addStatus($this->getDeletionMessage());
     $this->logDeletionMessage();
   }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
index 2faf45c44a..9761190572 100644
--- a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
+++ b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
@@ -120,7 +120,7 @@ protected function logDeletionMessage() {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->getEntity()->delete();
-    drupal_set_message($this->getDeletionMessage());
+    $this->messenger()->addStatus($this->getDeletionMessage());
     $form_state->setRedirectUrl($this->getCancelUrl());
     $this->logDeletionMessage();
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityListBuilder.php b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
index 91f4d21297..c7843517be 100644
--- a/core/lib/Drupal/Core/Entity/EntityListBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Routing\RedirectDestinationTrait;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -13,6 +14,7 @@
  */
 class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderInterface, EntityHandlerInterface {
 
+  use MessengerTrait;
   use RedirectDestinationTrait;
 
   /**
diff --git a/core/lib/Drupal/Core/FileTransfer/Form/FileTransferAuthorizeForm.php b/core/lib/Drupal/Core/FileTransfer/Form/FileTransferAuthorizeForm.php
index 3f8fd6d364..07b99b7d38 100644
--- a/core/lib/Drupal/Core/FileTransfer/Form/FileTransferAuthorizeForm.php
+++ b/core/lib/Drupal/Core/FileTransfer/Form/FileTransferAuthorizeForm.php
@@ -52,7 +52,7 @@ public function getFormId() {
   public function buildForm(array $form, FormStateInterface $form_state) {
     // Get all the available ways to transfer files.
     if (empty($_SESSION['authorize_filetransfer_info'])) {
-      drupal_set_message($this->t('Unable to continue, no available methods of file transfer'), 'error');
+      $this->messenger()->addError($this->t('Unable to continue, no available methods of file transfer'));
       return [];
     }
     $available_backends = $_SESSION['authorize_filetransfer_info'];
diff --git a/core/lib/Drupal/Core/Form/ConfigFormBase.php b/core/lib/Drupal/Core/Form/ConfigFormBase.php
index e7878a7db0..707e580d5a 100644
--- a/core/lib/Drupal/Core/Form/ConfigFormBase.php
+++ b/core/lib/Drupal/Core/Form/ConfigFormBase.php
@@ -51,7 +51,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    drupal_set_message($this->t('The configuration options have been saved.'));
+    $this->messenger()->addStatus($this->t('The configuration options have been saved.'));
   }
 
 }
diff --git a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
index 0e49c5bb08..c94a1bfa93 100644
--- a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
+++ b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
@@ -9,6 +9,7 @@
 use Drupal\Core\Form\FormAjaxException;
 use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
 use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -30,6 +31,13 @@ class FormAjaxSubscriber implements EventSubscriberInterface {
    */
   protected $formAjaxResponseBuilder;
 
+  /**
+   * The messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
   /**
    * Constructs a new FormAjaxSubscriber.
    *
@@ -37,10 +45,13 @@ class FormAjaxSubscriber implements EventSubscriberInterface {
    *   The form AJAX response builder.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    *   The string translation.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger.
    */
-  public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder, TranslationInterface $string_translation) {
+  public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder, TranslationInterface $string_translation, MessengerInterface $messenger) {
     $this->formAjaxResponseBuilder = $form_ajax_response_builder;
     $this->stringTranslation = $string_translation;
+    $this->messenger = $messenger;
   }
 
   /**
@@ -75,7 +86,7 @@ public function onException(GetResponseForExceptionEvent $event) {
     // Render a nice error message in case we have a file upload which exceeds
     // the configured upload limit.
     if ($exception instanceof BrokenPostRequestException && $request->query->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
-      $this->drupalSetMessage($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]), 'error');
+      $this->messenger->addError($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]));
       $response = new AjaxResponse(NULL, 200);
       $status_messages = ['#type' => 'status_messages'];
       $response->addCommand(new PrependCommand(NULL, $status_messages));
@@ -154,13 +165,4 @@ public static function getSubscribedEvents() {
     return $events;
   }
 
-  /**
-   * Wraps drupal_set_message().
-   *
-   * @codeCoverageIgnore
-   */
-  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
-    drupal_set_message($message, $type, $repeat);
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Form/FormErrorHandler.php b/core/lib/Drupal/Core/Form/FormErrorHandler.php
index 02457ce6d6..92f3a5480f 100644
--- a/core/lib/Drupal/Core/Form/FormErrorHandler.php
+++ b/core/lib/Drupal/Core/Form/FormErrorHandler.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Form;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Render\Element;
 
 /**
@@ -10,6 +11,8 @@
  */
 class FormErrorHandler implements FormErrorHandlerInterface {
 
+  use MessengerTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -39,7 +42,7 @@ protected function displayErrorMessages(array $form, FormStateInterface $form_st
 
     // Loop through all form errors and set an error message.
     foreach ($errors as $error) {
-      $this->drupalSetMessage($error, 'error');
+      $this->messenger()->addError($error);
     }
   }
 
@@ -160,13 +163,4 @@ protected function setElementErrorsFromFormState(array &$form, FormStateInterfac
     $elements['#errors'] = $form_state->getError($elements);
   }
 
-  /**
-   * Wraps drupal_set_message().
-   *
-   * @codeCoverageIgnore
-   */
-  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
-    drupal_set_message($message, $type, $repeat);
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Form/form.api.php b/core/lib/Drupal/Core/Form/form.api.php
index 554e2710d7..4154412eed 100644
--- a/core/lib/Drupal/Core/Form/form.api.php
+++ b/core/lib/Drupal/Core/Form/form.api.php
@@ -121,7 +121,7 @@ function callback_batch_finished($success, $results, $operations) {
       '#items' => $results,
     ];
     $message .= drupal_render($list);
-    drupal_set_message($message);
+    \Drupal::messenger()->addStatus($message);
   }
   else {
     // An error occurred.
@@ -131,7 +131,7 @@ function callback_batch_finished($success, $results, $operations) {
       '%error_operation' => $error_operation[0],
       '@arguments' => print_r($error_operation[1], TRUE)
     ]);
-    drupal_set_message($message, 'error');
+    \Drupal::messenger()->addError($message);
   }
 }
 
diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
index c4d7bc3a84..26e9b36966 100644
--- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
+++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Locale\CountryManagerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\State\StateInterface;
 use Drupal\user\UserStorageInterface;
@@ -76,13 +77,14 @@ class SiteConfigureForm extends ConfigFormBase {
    * @param \Drupal\Core\Locale\CountryManagerInterface $country_manager
    *   The country manager.
    */
-  public function __construct($root, $site_path, UserStorageInterface $user_storage, StateInterface $state, ModuleInstallerInterface $module_installer, CountryManagerInterface $country_manager) {
+  public function __construct($root, $site_path, UserStorageInterface $user_storage, StateInterface $state, ModuleInstallerInterface $module_installer, CountryManagerInterface $country_manager, MessengerInterface $messenger) {
     $this->root = $root;
     $this->sitePath = $site_path;
     $this->userStorage = $user_storage;
     $this->state = $state;
     $this->moduleInstaller = $module_installer;
     $this->countryManager = $country_manager;
+    $this->messenger = $messenger;
   }
 
   /**
@@ -95,7 +97,8 @@ public static function create(ContainerInterface $container) {
       $container->get('entity.manager')->getStorage('user'),
       $container->get('state'),
       $container->get('module_installer'),
-      $container->get('country_manager')
+      $container->get('country_manager'),
+      $container->get('messenger')
     );
   }
 
@@ -135,7 +138,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     // successfully.)
     $post_params = $this->getRequest()->request->all();
     if (empty($post_params) && (Settings::get('skip_permissions_hardening') || !drupal_verify_install_file($this->root . '/' . $settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE) || !drupal_verify_install_file($this->root . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) {
-      drupal_set_message(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href=":handbook_url">online handbook</a>.', ['%dir' => $settings_dir, '%file' => $settings_file, ':handbook_url' => 'https://www.drupal.org/server-permissions']), 'warning');
+      $this->messenger()->addWarning(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href=":handbook_url">online handbook</a>.', ['%dir' => $settings_dir, '%file' => $settings_file, ':handbook_url' => 'https://www.drupal.org/server-permissions']));
     }
 
     $form['#attached']['library'][] = 'system/drupal.system';
diff --git a/core/lib/Drupal/Core/Mail/MailManager.php b/core/lib/Drupal/Core/Mail/MailManager.php
index 348dceeb20..3a7a93f8b1 100644
--- a/core/lib/Drupal/Core/Mail/MailManager.php
+++ b/core/lib/Drupal/Core/Mail/MailManager.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -23,6 +24,7 @@
  */
 class MailManager extends DefaultPluginManager implements MailManagerInterface {
 
+  use MessengerTrait;
   use StringTranslationTrait;
 
   /**
@@ -303,7 +305,7 @@ public function doMail($module, $key, $to, $langcode, $params = [], $reply = NUL
             '%to' => $message['to'],
             '%reply' => $message['reply-to'] ? $message['reply-to'] : $this->t('not set'),
           ]);
-          drupal_set_message($this->t('Unable to send email. Contact the site administrator if the problem persists.'), 'error');
+          $this->messenger()->addError($this->t('Unable to send email. Contact the site administrator if the problem persists.'));
         }
       }
     }
diff --git a/core/lib/Drupal/Core/Plugin/PluginBase.php b/core/lib/Drupal/Core/Plugin/PluginBase.php
index 1148768e60..5407dc09b5 100644
--- a/core/lib/Drupal/Core/Plugin/PluginBase.php
+++ b/core/lib/Drupal/Core/Plugin/PluginBase.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Plugin;
 
 use Drupal\Component\Plugin\PluginBase as ComponentPluginBase;
+use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 
@@ -14,5 +15,6 @@
 abstract class PluginBase extends ComponentPluginBase {
   use StringTranslationTrait;
   use DependencySerializationTrait;
+  use MessengerTrait;
 
 }
diff --git a/core/lib/Drupal/Core/Render/Element/StatusMessages.php b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
index 530457fd90..3206830f10 100644
--- a/core/lib/Drupal/Core/Render/Element/StatusMessages.php
+++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
@@ -5,7 +5,7 @@
 /**
  * Provides a messages element.
  *
- * Used to display results of drupal_set_message() calls.
+ * Used to display results of \Drupal::messenger()->addMessage() calls.
  *
  * Usage example:
  * @code
@@ -61,7 +61,8 @@ public static function generatePlaceholder(array $element) {
    *
    * @param string|null $type
    *   Limit the messages returned by type. Defaults to NULL, meaning all types.
-   *   Passed on to drupal_get_messages(). These values are supported:
+   *   Passed on to \Drupal\Core\Messenger\Messenger::deleteByType(). These
+   *   values are supported:
    *   - NULL
    *   - 'status'
    *   - 'warning'
@@ -74,7 +75,13 @@ public static function generatePlaceholder(array $element) {
    */
   public static function renderMessages($type) {
     $render = [];
-    $messages = drupal_get_messages($type);
+    if (isset($type)) {
+      $messages = \Drupal::messenger()->deleteByType($type);
+    }
+    else {
+      $messages = \Drupal::messenger()->deleteAll();
+    }
+
     if ($messages) {
       // Render the messages.
       $render = [
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index d435798652..bae83de746 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -649,8 +649,8 @@ protected function replacePlaceholders(array &$elements) {
     // being rendered: any code can add messages to render.
     // This violates the principle that each lazy builder must be able to render
     // itself in isolation, and therefore in any order. However, we cannot
-    // change the way drupal_set_message() works in the Drupal 8 cycle. So we
-    // have to accommodate its special needs.
+    // change the way \Drupal\Core\Messenger\Messenger works in the Drupal 8
+    // cycle. So we have to accommodate its special needs.
     // Allowing placeholders to be rendered in a particular order (in this case:
     // last) would violate this isolation principle. Thus a monopoly is granted
     // to this one special case, with this hard-coded solution.
diff --git a/core/modules/aggregator/tests/src/Unit/Plugin/AggregatorPluginSettingsBaseTest.php b/core/modules/aggregator/tests/src/Unit/Plugin/AggregatorPluginSettingsBaseTest.php
index 56c0339bd6..f423730631 100644
--- a/core/modules/aggregator/tests/src/Unit/Plugin/AggregatorPluginSettingsBaseTest.php
+++ b/core/modules/aggregator/tests/src/Unit/Plugin/AggregatorPluginSettingsBaseTest.php
@@ -55,7 +55,7 @@ protected function setUp() {
         ->will($this->returnValue(['aggregator_test' => ['title' => '', 'description' => '']]));
     }
 
-    $this->settingsForm = new SettingsForm(
+    $this->settingsForm = new TestSettingsForm(
       $this->configFactory,
       $this->managers['fetcher'],
       $this->managers['parser'],
@@ -105,10 +105,13 @@ public function testSettingsForm() {
 
 }
 
-// @todo Delete after https://www.drupal.org/node/2278383 is in.
-namespace Drupal\Core\Form;
+class TestSettingsForm extends SettingsForm {
 
-if (!function_exists('drupal_set_message')) {
-  function drupal_set_message() {
+  public function messenger() {
+    return new class() {
+      public function addStatus() {
+      }
+    };
   }
+
 }
diff --git a/core/modules/inline_form_errors/src/FormErrorHandler.php b/core/modules/inline_form_errors/src/FormErrorHandler.php
index 2bcbfc60be..8652f3b4cc 100644
--- a/core/modules/inline_form_errors/src/FormErrorHandler.php
+++ b/core/modules/inline_form_errors/src/FormErrorHandler.php
@@ -96,7 +96,7 @@ protected function displayErrorMessages(array $form, FormStateInterface $form_st
 
     // Set normal error messages for all remaining errors.
     foreach ($errors as $error) {
-      $this->drupalSetMessage($error, 'error');
+      $this->messenger()->addError($error);
     }
 
     if (!empty($error_links)) {
@@ -111,7 +111,7 @@ protected function displayErrorMessages(array $form, FormStateInterface $form_st
         ],
       ];
       $message = $this->renderer->renderPlain($render_array);
-      $this->drupalSetMessage($message, 'error');
+      $this->messenger()->addError($message);
     }
   }
 
diff --git a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
index f110b36af9..922eb68418 100644
--- a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
+++ b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\inline_form_errors\Unit;
 
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Messenger\Messenger;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Utility\LinkGeneratorInterface;
 use Drupal\inline_form_errors\FormErrorHandler;
@@ -26,21 +27,30 @@ public function testDisplayErrorMessagesInline() {
     $renderer = $this->getMock(RendererInterface::class);
     $form_error_handler = $this->getMockBuilder(FormErrorHandler::class)
       ->setConstructorArgs([$this->getStringTranslationStub(), $link_generator, $renderer])
-      ->setMethods(['drupalSetMessage'])
+      ->setMethods(['messenger'])
       ->getMock();
 
-    $form_error_handler->expects($this->at(0))
-      ->method('drupalSetMessage')
-      ->with('no title given', 'error');
-    $form_error_handler->expects($this->at(1))
-      ->method('drupalSetMessage')
-      ->with('element is invisible', 'error');
-    $form_error_handler->expects($this->at(2))
-      ->method('drupalSetMessage')
-      ->with('this missing element is invalid', 'error');
-    $form_error_handler->expects($this->at(3))
-      ->method('drupalSetMessage')
-      ->with('3 errors have been found: <ul-comma-list-mock><li-mock>Test 1</li-mock><li-mock>Test 2 &amp; a half</li-mock><li-mock>Test 3</li-mock></ul-comma-list-mock>', 'error');
+    $messenger = $this->getMockBuilder(Messenger::class)
+      ->disableOriginalConstructor()
+      ->setMethods(['addError'])
+      ->getMock();
+
+    $form_error_handler->expects($this->exactly(4))
+      ->method('messenger')
+      ->willReturn($messenger);
+
+    $messenger->expects($this->at(0))
+      ->method('addError')
+      ->with('no title given');
+    $messenger->expects($this->at(1))
+      ->method('addError')
+      ->with('element is invisible');
+    $messenger->expects($this->at(2))
+      ->method('addError')
+      ->with('this missing element is invalid');
+    $messenger->expects($this->at(3))
+      ->method('addError')
+      ->with('3 errors have been found: <ul-comma-list-mock><li-mock>Test 1</li-mock><li-mock>Test 2 &amp; a half</li-mock><li-mock>Test 3</li-mock></ul-comma-list-mock>');
 
     $renderer->expects($this->any())
       ->method('renderPlain')
@@ -118,9 +128,17 @@ public function testDisplayErrorMessagesInline() {
   public function testSetElementErrorsFromFormState() {
     $form_error_handler = $this->getMockBuilder(FormErrorHandler::class)
       ->setConstructorArgs([$this->getStringTranslationStub(), $this->getMock(LinkGeneratorInterface::class), $this->getMock(RendererInterface::class)])
-      ->setMethods(['drupalSetMessage'])
+      ->setMethods(['messenger'])
+      ->getMock();
+
+    $messenger = $this->getMockBuilder(Messenger::class)
+      ->disableOriginalConstructor()
       ->getMock();
 
+    $form_error_handler->expects($this->at(0))
+      ->method('messenger')
+      ->willReturn($messenger);
+
     $form = [
       '#parents' => [],
       '#form_id' => 'test_form',
@@ -145,12 +163,21 @@ public function testSetElementErrorsFromFormState() {
   public function testDisplayErrorMessagesNotInline() {
     $form_error_handler = $this->getMockBuilder(FormErrorHandler::class)
       ->setConstructorArgs([$this->getStringTranslationStub(), $this->getMock(LinkGeneratorInterface::class), $this->getMock(RendererInterface::class)])
-      ->setMethods(['drupalSetMessage'])
+      ->setMethods(['messenger'])
       ->getMock();
 
+    $messenger = $this->getMockBuilder(Messenger::class)
+      ->disableOriginalConstructor()
+      ->setMethods(['addError'])
+      ->getMock();
+
+    $messenger->expects($this->once())
+      ->method('addError')
+      ->with('invalid');
+
     $form_error_handler->expects($this->at(0))
-      ->method('drupalSetMessage')
-      ->with('invalid', 'error');
+      ->method('messenger')
+      ->willReturn($messenger);
 
     $form = [
       '#parents' => [],
diff --git a/core/modules/menu_ui/src/MenuForm.php b/core/modules/menu_ui/src/MenuForm.php
index aec2c49eb0..0533f4715b 100644
--- a/core/modules/menu_ui/src/MenuForm.php
+++ b/core/modules/menu_ui/src/MenuForm.php
@@ -240,6 +240,10 @@ protected function buildOverviewForm(array &$form, FormStateInterface $form_stat
           'data' => $this->t('Enabled'),
           'class' => ['checkbox'],
         ],
+        [
+          'data' => $this->t('Expanded'),
+          'class' => ['checkbox', 'priority-medium'],
+        ],
         $this->t('Weight'),
         [
           'data' => $this->t('Operations'),
@@ -298,8 +302,19 @@ protected function buildOverviewForm(array &$form, FormStateInterface $form_stat
           ],
           $element['title'],
         ];
+
         $form['links'][$id]['enabled'] = $element['enabled'];
-        $form['links'][$id]['enabled']['#wrapper_attributes']['class'] = ['checkbox', 'menu-enabled'];
+        $form['links'][$id]['enabled']['#wrapper_attributes']['class'] = [
+          'checkbox',
+          'menu-enabled',
+        ];
+
+        $form['links'][$id]['expanded'] = $element['expanded'];
+        $form['links'][$id]['expanded']['#wrapper_attributes']['class'] = [
+          'checkbox',
+          'menu-expanded',
+          'priority-medium',
+        ];
 
         $form['links'][$id]['weight'] = $element['weight'];
 
@@ -361,6 +376,12 @@ protected function buildOverviewTreeForm($tree, $delta) {
           '#title_display' => 'invisible',
           '#default_value' => $link->isEnabled(),
         ];
+        $form[$id]['expanded'] = [
+          '#type' => 'checkbox',
+          '#title' => $this->t('Show @title as expanded', ['@title' => $link->getTitle()]),
+          '#title_display' => 'invisible',
+          '#default_value' => $link->isExpanded(),
+        ];
         $form[$id]['weight'] = [
           '#type' => 'weight',
           '#delta' => $delta,
@@ -455,7 +476,7 @@ protected function submitOverviewForm(array $complete_form, FormStateInterface $
     // Update our original form with the new order.
     $form = array_intersect_key(array_merge($order, $form), $form);
 
-    $fields = ['weight', 'parent', 'enabled'];
+    $fields = ['weight', 'parent', 'enabled', 'expanded'];
     $form_links = $form['links'];
     foreach (Element::children($form_links) as $id) {
       if (isset($form_links[$id]['#item'])) {
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 6a8eaca824..3462fcfc7c 100644
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -125,8 +125,8 @@
   echo "\nEnvironment cleaned.\n";
 
   // Get the status messages and print them.
-  $messages = drupal_get_messages('status');
-  foreach ($messages['status'] as $text) {
+  $messages = \Drupal::messenger()->messagesByType('status');
+  foreach ($messages as $text) {
     echo " - " . $text . "\n";
   }
   exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS);
diff --git a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
index 7a15fc3e04..c26d451fbd 100644
--- a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
@@ -6,7 +6,8 @@
 
 /**
  * @covers ::drupal_set_message
- * @group PHPUnit
+ * @group Common
+ * @group legacy
  */
 class DrupalSetMessageTest extends KernelTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php b/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
index 2e5d09473a..3cf351b031 100644
--- a/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
+++ b/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
@@ -38,7 +38,8 @@ public function testMunging() {
     // Disable insecure uploads.
     $this->config('system.file')->set('allow_insecure_uploads', 0)->save();
     $munged_name = file_munge_filename($this->name, '', TRUE);
-    $messages = drupal_get_messages();
+    $messages = \Drupal::messenger()->all();
+    \Drupal::messenger()->deleteAll();
     $this->assertTrue(in_array(strtr('For security reasons, your upload has been renamed to <em class="placeholder">%filename</em>.', ['%filename' => $munged_name]), $messages['status']), 'Alert properly set when a file is renamed.');
     $this->assertNotEqual($munged_name, $this->name, format_string('The new filename (%munged) has been modified from the original (%original)', ['%munged' => $munged_name, '%original' => $this->name]));
   }
diff --git a/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php b/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php
index 1d20340819..4e056cdd8b 100644
--- a/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php
@@ -80,7 +80,8 @@ public function testLimitValidationErrors() {
     $form_builder = $this->container->get('form_builder');
     $form_builder->submitForm($this, $form_state);
 
-    $messages = drupal_get_messages();
+    $messages = \Drupal::messenger()->all();
+    \Drupal::messenger()->deleteAll();
     $this->assertTrue(isset($messages['error']));
     $error_messages = $messages['error'];
     $this->assertEqual($error_messages[0], 'Three field is required.');
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/MessageTest.php b/core/tests/Drupal/KernelTests/Core/Theme/MessageTest.php
index 294c8dce81..1b3796eff4 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/MessageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/MessageTest.php
@@ -24,8 +24,8 @@ public function testMessages() {
     \Drupal::service('theme_handler')->install(['classy']);
     $this->config('system.theme')->set('default', 'classy')->save();
 
-    drupal_set_message('An error occurred', 'error');
-    drupal_set_message('But then something nice happened');
+    \Drupal::messenger()->addError('An error occurred');
+    \Drupal::messenger()->addStatus('But then something nice happened');
     $messages = [
       '#type' => 'status_messages',
     ];
diff --git a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
index aa69ad0f0b..669f0fa0de 100644
--- a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Form\FormAjaxException;
 use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
@@ -43,6 +44,13 @@ class FormAjaxSubscriberTest extends UnitTestCase {
    */
   protected $stringTranslation;
 
+  /**
+   * The mocked messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $messenger;
+
   /**
    * {@inheritdoc}
    */
@@ -52,7 +60,8 @@ protected function setUp() {
     $this->httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
     $this->formAjaxResponseBuilder = $this->getMock('Drupal\Core\Form\FormAjaxResponseBuilderInterface');
     $this->stringTranslation = $this->getStringTranslationStub();
-    $this->subscriber = new FormAjaxSubscriber($this->formAjaxResponseBuilder, $this->stringTranslation);
+    $this->messenger = $this->createMock(MessengerInterface::class);
+    $this->subscriber = new FormAjaxSubscriber($this->formAjaxResponseBuilder, $this->stringTranslation, $this->messenger);
   }
 
   /**
@@ -147,13 +156,19 @@ public function testOnExceptionResponseBuilderException() {
   public function testOnExceptionBrokenPostRequest() {
     $this->formAjaxResponseBuilder->expects($this->never())
       ->method('buildResponse');
+
+    $this->messenger->expects($this->once())
+      ->method('addError');
+
     $this->subscriber = $this->getMockBuilder('\Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber')
-      ->setConstructorArgs([$this->formAjaxResponseBuilder, $this->getStringTranslationStub()])
-      ->setMethods(['drupalSetMessage', 'formatSize'])
+      ->setConstructorArgs([
+        $this->formAjaxResponseBuilder,
+        $this->getStringTranslationStub(),
+        $this->messenger,
+      ])
+      ->setMethods(['formatSize'])
       ->getMock();
-    $this->subscriber->expects($this->once())
-      ->method('drupalSetMessage')
-      ->willReturn('asdf');
+
     $this->subscriber->expects($this->once())
       ->method('formatSize')
       ->with(32 * 1e6)
diff --git a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
index 8e2fc0d2b7..39d2cf846f 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\Tests\Core\Form;
 
+use Drupal\Core\Form\FormErrorHandler;
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -16,28 +18,31 @@ class FormErrorHandlerTest extends UnitTestCase {
    * @covers ::displayErrorMessages
    */
   public function testDisplayErrorMessages() {
-    $form_error_handler = $this->getMockBuilder('Drupal\Core\Form\FormErrorHandler')
-      ->setMethods(['drupalSetMessage'])
-      ->getMock();
 
-    $form_error_handler->expects($this->at(0))
-      ->method('drupalSetMessage')
-      ->with('invalid', 'error');
-    $form_error_handler->expects($this->at(1))
-      ->method('drupalSetMessage')
-      ->with('invalid', 'error');
-    $form_error_handler->expects($this->at(2))
-      ->method('drupalSetMessage')
-      ->with('invalid', 'error');
-    $form_error_handler->expects($this->at(3))
-      ->method('drupalSetMessage')
-      ->with('no title given', 'error');
-    $form_error_handler->expects($this->at(4))
-      ->method('drupalSetMessage')
-      ->with('element is invisible', 'error');
-    $form_error_handler->expects($this->at(5))
-      ->method('drupalSetMessage')
-      ->with('this missing element is invalid', 'error');
+    /** @var \Drupal\Core\Messenger\MessengerInterface|\PHPUnit_Framework_MockObject_MockObject $messenger */
+    $messenger = $this->createMock(MessengerInterface::class);
+
+    $form_error_handler = new FormErrorHandler();
+    $form_error_handler->setMessenger($messenger);
+
+    $messenger->expects($this->at(0))
+      ->method('addError')
+      ->with('invalid');
+    $messenger->expects($this->at(1))
+      ->method('addError')
+      ->with('invalid');
+    $messenger->expects($this->at(2))
+      ->method('addError')
+      ->with('invalid');
+    $messenger->expects($this->at(3))
+      ->method('addError')
+      ->with('no title given');
+    $messenger->expects($this->at(4))
+      ->method('addError')
+      ->with('element is invisible');
+    $messenger->expects($this->at(5))
+      ->method('addError')
+      ->with('this missing element is invalid');
 
     $form = [
       '#parents' => [],
@@ -97,9 +102,11 @@ public function testDisplayErrorMessages() {
    * @covers ::setElementErrorsFromFormState
    */
   public function testSetElementErrorsFromFormState() {
-    $form_error_handler = $this->getMockBuilder('Drupal\Core\Form\FormErrorHandler')
-      ->setMethods(['drupalSetMessage'])
-      ->getMock();
+    /** @var \Drupal\Core\Messenger\MessengerInterface|\PHPUnit_Framework_MockObject_MockObject $messenger */
+    $messenger = $this->createMock(MessengerInterface::class);
+
+    $form_error_handler = new FormErrorHandler();
+    $form_error_handler->setMessenger($messenger);
 
     $form = [
       '#parents' => [],
