diff --git a/core/core.api.php b/core/core.api.php
index 4fc3ea2983b9c3be189ce52eb2357acb8be709bc..98d06576875199742a558940a38b0151a476670f 100644
--- a/core/core.api.php
+++ b/core/core.api.php
@@ -2139,7 +2139,7 @@ function hook_mail($key, &$message, $params) {
     $node = $params['node'];
     $variables += [
       '%uid' => $node->getOwnerId(),
-      '%url' => $node->url('canonical', ['absolute' => TRUE]),
+      '%url' => $node->toUrl('canonical', ['absolute' => TRUE])->toString(),
       '%node_type' => node_get_type_label($node),
       '%title' => $node->getTitle(),
       '%teaser' => $node->teaser,
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
index 27dd2b7d79dba656ba265c3d56ba02701504820c..5ef20387e8eaf5d27fa9eab92b094de1e1f784ac 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
@@ -67,7 +67,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $untranslated_entity = $entity->getUntranslated();
       $untranslated_entity->removeTranslation($entity->language()->getId());
       $untranslated_entity->save();
-      $form_state->setRedirectUrl($untranslated_entity->urlInfo('canonical'));
+      $form_state->setRedirectUrl($untranslated_entity->toUrl('canonical'));
     }
     else {
       $entity->delete();
@@ -84,7 +84,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   public function getCancelUrl() {
     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     $entity = $this->getEntity();
-    return $entity->isDefaultTranslation() ? $this->traitGetCancelUrl() : $entity->urlInfo('canonical');
+    return $entity->isDefaultTranslation() ? $this->traitGetCancelUrl() : $entity->toUrl('canonical');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 84991bc60935ccd993c313b3ac512d6b90344a3e..eedfe6f0805a6dab3af9fb47513be021fe7c3f20 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -178,6 +178,7 @@ public function label() {
    * {@inheritdoc}
    */
   public function urlInfo($rel = 'canonical', array $options = []) {
+    @trigger_error('EntityInterface::urlInfo() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. See https://www.drupal.org/node/2614344', E_USER_DEPRECATED);
     return $this->toUrl($rel, $options);
   }
 
@@ -266,6 +267,7 @@ protected function linkTemplates() {
    * {@inheritdoc}
    */
   public function link($text = NULL, $rel = 'canonical', array $options = []) {
+    @trigger_error("EntityInterface::link() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toLink() instead. Note, the default relationship for configuration entities changes from 'edit-form' to 'canonical'. See https://www.drupal.org/node/2614344", E_USER_DEPRECATED);
     return $this->toLink($text, $rel, $options)->toString();
   }
 
@@ -286,6 +288,7 @@ public function toLink($text = NULL, $rel = 'canonical', array $options = []) {
    * {@inheritdoc}
    */
   public function url($rel = 'canonical', $options = []) {
+    @trigger_error('EntityInterface::url() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. Note, a \Drupal\Core\Url object is returned. See https://www.drupal.org/node/2614344', E_USER_DEPRECATED);
     // While self::toUrl() will throw an exception if the entity has no id,
     // the expected result for a URL is always a string.
     if ($this->id() === NULL || !$this->hasLinkTemplate($rel)) {
diff --git a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
index 976119057286b80a15765ec75020b9417a4ff24c..4800f70cb365067b2ff5ef09757de16403e7d92e 100644
--- a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
+++ b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
@@ -78,11 +78,11 @@ public function getCancelUrl() {
     $entity = $this->getEntity();
     if ($entity->hasLinkTemplate('collection')) {
       // If available, return the collection URL.
-      return $entity->urlInfo('collection');
+      return $entity->toUrl('collection');
     }
     else {
       // Otherwise fall back to the default link template.
-      return $entity->urlInfo();
+      return $entity->toUrl();
     }
   }
 
@@ -96,7 +96,7 @@ protected function getRedirectUrl() {
     $entity = $this->getEntity();
     if ($entity->hasLinkTemplate('collection')) {
       // If available, return the collection URL.
-      return $entity->urlInfo('collection');
+      return $entity->toUrl('collection');
     }
     else {
       // Otherwise fall back to the front page.
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 796502861cae0a900ea63bae3e9205f8e3fa48ba..b90d1d209dc36a8faa009fb58be9d23f5280a195 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -246,7 +246,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
     ];
 
     if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('delete-form')) {
-      $route_info = $this->entity->urlInfo('delete-form');
+      $route_info = $this->entity->toUrl('delete-form');
       if ($this->getRequest()->query->has('destination')) {
         $query = $route_info->getOption('query');
         $query['destination'] = $this->getRequest()->query->get('destination');
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
index 011e3051eb830240422ebac31d89436c6a6a3bc5..ed9a6596d83e309faac7400bfad1c64f2e7e5412 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
@@ -65,7 +65,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
       // link.
       if ($output_as_link && !$entity->isNew()) {
         try {
-          $uri = $entity->urlInfo();
+          $uri = $entity->toUrl();
         }
         catch (UndefinedLinkTemplateException $e) {
           // This exception is thrown by \Drupal\Core\Entity\Entity::urlInfo()
diff --git a/core/lib/Drupal/Core/Utility/token.api.php b/core/lib/Drupal/Core/Utility/token.api.php
index 5916752d5948e7bd506b24a47f59d7af5567de0d..de876cfb7b71253ad125b054e36c41c75c23504e 100644
--- a/core/lib/Drupal/Core/Utility/token.api.php
+++ b/core/lib/Drupal/Core/Utility/token.api.php
@@ -100,7 +100,7 @@ function hook_tokens($type, $tokens, array $data, array $options, \Drupal\Core\R
           break;
 
         case 'edit-url':
-          $replacements[$original] = $node->url('edit-form', $url_options);
+          $replacements[$original] = $node->toUrl('edit-form', $url_options)->toString();
           break;
 
         // Default values for the chained tokens handled below.
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index 47827b904aeb50201f43a86992880bb97b08d31b..78a7a9e8ac6f9744886e70ed7c6c07da2824f04c 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -115,7 +115,7 @@ public function adminOverview() {
     /** @var \Drupal\aggregator\FeedInterface[] $feeds */
     foreach ($feeds as $feed) {
       $row = [];
-      $row[] = $feed->link();
+      $row[] = $feed->toLink()->toString();
       $row[] = $this->formatPlural($entity_manager->getStorage('aggregator_item')->getItemCount($feed), '1 item', '@count items');
       $last_checked = $feed->getLastCheckedTime();
       $refresh_rate = $feed->getRefreshRate();
diff --git a/core/modules/aggregator/src/FeedForm.php b/core/modules/aggregator/src/FeedForm.php
index 7fc254780bcf191ef9bbea9ad2ae85b9b5f99aa1..5077e7d8d53d3eacc61b9a1e4c61e57db0415e7e 100644
--- a/core/modules/aggregator/src/FeedForm.php
+++ b/core/modules/aggregator/src/FeedForm.php
@@ -20,10 +20,10 @@ public function save(array $form, FormStateInterface $form_state) {
     $feed = $this->entity;
     $status = $feed->save();
     $label = $feed->label();
-    $view_link = $feed->link($label, 'canonical');
+    $view_link = $feed->toLink($label, 'canonical')->toString();
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus($this->t('The feed %feed has been updated.', ['%feed' => $view_link]));
-      $form_state->setRedirectUrl($feed->urlInfo('canonical'));
+      $form_state->setRedirectUrl($feed->toUrl('canonical'));
     }
     else {
       $this->logger('aggregator')->notice('Feed %feed added.', ['%feed' => $feed->label(), 'link' => $this->l($this->t('View'), new Url('aggregator.admin_overview'))]);
diff --git a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
index 18390c73ff7f4335a599734c7dc96a3f56e2019e..8c5ed01882bb14e448d438a33ccd406e713eb9c1 100644
--- a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
+++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
@@ -146,13 +146,13 @@ public function build() {
         foreach ($items as $item) {
           $build['list']['#items'][$item->id()] = [
             '#type' => 'link',
-            '#url' => $item->urlInfo(),
+            '#url' => $item->toUrl(),
             '#title' => $item->label(),
           ];
         }
         $build['more_link'] = [
           '#type' => 'more_link',
-          '#url' => $feed->urlInfo(),
+          '#url' => $feed->toUrl(),
           '#attributes' => ['title' => $this->t("View this feed's recent news.")],
         ];
         return $build;
diff --git a/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php b/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
index b03ddf88db2db9adb1f44e2b2b5bfe3f1cc301f2..7c7a1fb050638c045c4dc979d17d1353ae4e94f1 100644
--- a/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
+++ b/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
@@ -60,7 +60,7 @@ public function testBlockLinks() {
     $this->assert(isset($links[0]), 'Item link found.');
 
     // Find the expected read_more link.
-    $href = $feed->url();
+    $href = $feed->toUrl()->toString();
     $links = $this->xpath('//a[@href = :href]', [':href' => $href]);
     $this->assert(isset($links[0]), format_string('Link to href %href found.', ['%href' => $href]));
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
@@ -68,7 +68,7 @@ public function testBlockLinks() {
     $this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
 
     // Visit that page.
-    $this->drupalGet($feed->urlInfo()->getInternalPath());
+    $this->drupalGet($feed->toUrl()->getInternalPath());
     $correct_titles = $this->xpath('//h1[normalize-space(text())=:title]', [':title' => $feed->label()]);
     $this->assertFalse(empty($correct_titles), 'Aggregator feed page is available and has the correct title.');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
@@ -112,7 +112,7 @@ public function testFeedPage() {
     $this->assertTrue(!empty($titles), 'Source page contains correct title.');
 
     // Find the expected read_more link on the sources page.
-    $href = $feed->url();
+    $href = $feed->toUrl()->toString();
     $links = $this->xpath('//a[@href = :href]', [':href' => $href]);
     $this->assertTrue(isset($links[0]), new FormattableMarkup('Link to href %href found.', ['%href' => $href]));
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
diff --git a/core/modules/aggregator/tests/src/Functional/UpdateFeedTest.php b/core/modules/aggregator/tests/src/Functional/UpdateFeedTest.php
index 0c76f978a981c0ddf28ed83f63c0c4288fc66b54..408da55337aae50072eba87dba719de33e478ed4 100644
--- a/core/modules/aggregator/tests/src/Functional/UpdateFeedTest.php
+++ b/core/modules/aggregator/tests/src/Functional/UpdateFeedTest.php
@@ -32,7 +32,7 @@ public function testUpdateFeed() {
       $this->assert(isset($view_link), 'The message area contains a link to a feed');
 
       // Check feed data.
-      $this->assertUrl($feed->url('canonical', ['absolute' => TRUE]));
+      $this->assertUrl($feed->toUrl('canonical', ['absolute' => TRUE])->toString());
       $this->assertTrue($this->uniqueFeed($edit['title[0][value]'], $edit['url[0][value]']), 'The feed is unique.');
 
       // Check feed source.
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
index 9df2a87d732ba519e3b97ef5fb5f53cc48a0eb5c..f87d6217bc9936cb169725a7bf2d617f53b35a00 100644
--- a/core/modules/block_content/src/BlockContentForm.php
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -77,7 +77,7 @@ public function save(array $form, FormStateInterface $form_state) {
         );
       }
       else {
-        $form_state->setRedirectUrl($block->urlInfo('collection'));
+        $form_state->setRedirectUrl($block->toUrl('collection'));
       }
     }
     else {
diff --git a/core/modules/block_content/src/BlockContentTypeForm.php b/core/modules/block_content/src/BlockContentTypeForm.php
index 711cda678b7e1452491ca65328b7b079279ab525..ac7bf47b816de5c73e0a7e89c1a395c699e2978a 100644
--- a/core/modules/block_content/src/BlockContentTypeForm.php
+++ b/core/modules/block_content/src/BlockContentTypeForm.php
@@ -97,7 +97,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $block_type = $this->entity;
     $status = $block_type->save();
 
-    $edit_link = $this->entity->link($this->t('Edit'));
+    $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString();
     $logger = $this->logger('block_content');
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus(t('Custom block type %label has been updated.', ['%label' => $block_type->label()]));
@@ -109,7 +109,7 @@ public function save(array $form, FormStateInterface $form_state) {
       $logger->notice('Custom block type %label has been added.', ['%label' => $block_type->label(), 'link' => $edit_link]);
     }
 
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/block_content/src/BlockContentTypeListBuilder.php b/core/modules/block_content/src/BlockContentTypeListBuilder.php
index 43ee3382629d7f9d30243b93f28c1bc3bf2e2581..3cce3f307c7b37bff78e0f079c092c3cac96ecd7 100644
--- a/core/modules/block_content/src/BlockContentTypeListBuilder.php
+++ b/core/modules/block_content/src/BlockContentTypeListBuilder.php
@@ -38,7 +38,7 @@ public function buildHeader() {
    * {@inheritdoc}
    */
   public function buildRow(EntityInterface $entity) {
-    $row['type'] = $entity->link();
+    $row['type'] = $entity->toLink(NULL, 'edit-form')->toString();
     $row['description']['data']['#markup'] = $entity->getDescription();
     return $row + parent::buildRow($entity);
   }
diff --git a/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php b/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php
index 6fc5f1af01324ce87c2f053e0cd0f6a0e3d2d83c..9c2c1b44b795ae7024b2f584b24952b1f0e956e7 100644
--- a/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php
+++ b/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php
@@ -188,7 +188,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('<em>Edit @type</em> @title [%language translation]', [
diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php
index 3db359b2c5cc12a15d71ee8f20bbb2ef5d31b3df..9b6ab1d37002256df02d4431c9b448be9f7ecf16 100644
--- a/core/modules/book/src/BookManager.php
+++ b/core/modules/book/src/BookManager.php
@@ -104,7 +104,7 @@ protected function loadBooks() {
       foreach ($book_links as $link) {
         $nid = $link['nid'];
         if (isset($nodes[$nid]) && $nodes[$nid]->status) {
-          $link['url'] = $nodes[$nid]->urlInfo();
+          $link['url'] = $nodes[$nid]->toUrl();
           $link['title'] = $nodes[$nid]->label();
           $link['type'] = $nodes[$nid]->bundle();
           $this->books[$link['bid']] = $link;
@@ -577,7 +577,7 @@ protected function buildItems(array $tree) {
       $element['attributes'] = new Attribute();
       $element['title'] = $data['link']['title'];
       $node = $this->entityManager->getStorage('node')->load($data['link']['nid']);
-      $element['url'] = $node->urlInfo();
+      $element['url'] = $node->toUrl();
       $element['localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : [];
       $element['localized_options']['set_active_class'] = TRUE;
       $element['below'] = $data['below'] ? $this->buildItems($data['below']) : [];
diff --git a/core/modules/book/src/Form/BookAdminEditForm.php b/core/modules/book/src/Form/BookAdminEditForm.php
index e052b7cff1d9aff2fc0cd8a37b92666bc4ba3796..8ec9d64a167f54adaf4d8d16a3261d161d687312 100644
--- a/core/modules/book/src/Form/BookAdminEditForm.php
+++ b/core/modules/book/src/Form/BookAdminEditForm.php
@@ -121,7 +121,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
             $node->book['link_title'] = $values['title'];
             $node->setNewRevision();
             $node->save();
-            $this->logger('content')->notice('book: updated %title.', ['%title' => $node->label(), 'link' => $node->link($this->t('View'))]);
+            $this->logger('content')->notice('book: updated %title.', ['%title' => $node->label(), 'link' => $node->toLink($this->t('View'))->toString()]);
           }
         }
       }
diff --git a/core/modules/book/src/Form/BookOutlineForm.php b/core/modules/book/src/Form/BookOutlineForm.php
index a305681d5349622ebf0ddaa14fb7c72a05e1d8ae..0bf60353ce73ec0d6b747a72b15ec3af9352658d 100644
--- a/core/modules/book/src/Form/BookOutlineForm.php
+++ b/core/modules/book/src/Form/BookOutlineForm.php
@@ -122,7 +122,7 @@ public function save(array $form, FormStateInterface $form_state) {
       if (isset($this->entity->book['parent_mismatch']) && $this->entity->book['parent_mismatch']) {
         // This will usually only happen when JS is disabled.
         $this->messenger()->addStatus($this->t('The post has been added to the selected book. You may now position it relative to other pages.'));
-        $form_state->setRedirectUrl($this->entity->urlInfo('book-outline-form'));
+        $form_state->setRedirectUrl($this->entity->toUrl('book-outline-form'));
       }
       else {
         $this->messenger()->addStatus($this->t('The book outline has been updated.'));
diff --git a/core/modules/book/src/Form/BookRemoveForm.php b/core/modules/book/src/Form/BookRemoveForm.php
index 2d5a91982f7c6dd5c77393db371aff1401f25153..5a3ef8c74f52d0f29d72a23b0b95f3cfbf11dfeb 100644
--- a/core/modules/book/src/Form/BookRemoveForm.php
+++ b/core/modules/book/src/Form/BookRemoveForm.php
@@ -94,7 +94,7 @@ public function getQuestion() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->node->urlInfo();
+    return $this->node->toUrl();
   }
 
   /**
diff --git a/core/modules/book/tests/src/Functional/BookTest.php b/core/modules/book/tests/src/Functional/BookTest.php
index 6fe03dec877da42f0dc6856aa25078d4c0e67744..39c856c7e2b6bb2b9b9a683d01de4e48d9dc46b3 100644
--- a/core/modules/book/tests/src/Functional/BookTest.php
+++ b/core/modules/book/tests/src/Functional/BookTest.php
@@ -81,23 +81,23 @@ public function testBookNavigationCacheContext() {
     $this->drupalLogin($this->bookAuthor);
 
     // On non-node route.
-    $this->drupalGet($this->adminUser->urlInfo());
+    $this->drupalGet($this->adminUser->toUrl());
     $this->assertRaw('[route.book_navigation]=book.none');
 
     // On non-book node route.
-    $this->drupalGet($page->urlInfo());
+    $this->drupalGet($page->toUrl());
     $this->assertRaw('[route.book_navigation]=book.none');
 
     // On book node route.
-    $this->drupalGet($book_nodes[0]->urlInfo());
+    $this->drupalGet($book_nodes[0]->toUrl());
     $this->assertRaw('[route.book_navigation]=0|2|3');
-    $this->drupalGet($book_nodes[1]->urlInfo());
+    $this->drupalGet($book_nodes[1]->toUrl());
     $this->assertRaw('[route.book_navigation]=0|2|3|4');
-    $this->drupalGet($book_nodes[2]->urlInfo());
+    $this->drupalGet($book_nodes[2]->toUrl());
     $this->assertRaw('[route.book_navigation]=0|2|3|5');
-    $this->drupalGet($book_nodes[3]->urlInfo());
+    $this->drupalGet($book_nodes[3]->toUrl());
     $this->assertRaw('[route.book_navigation]=0|2|6');
-    $this->drupalGet($book_nodes[4]->urlInfo());
+    $this->drupalGet($book_nodes[4]->toUrl());
     $this->assertRaw('[route.book_navigation]=0|2|7');
   }
 
@@ -366,11 +366,11 @@ public function testBookDelete() {
     // Tests directly deleting a book parent.
     $nodes = $this->createBook();
     $this->drupalLogin($this->adminUser);
-    $this->drupalGet($this->book->urlInfo('delete-form'));
+    $this->drupalGet($this->book->toUrl('delete-form'));
     $this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
     // Delete parent, and visit a child page.
-    $this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
-    $this->drupalGet($nodes[0]->urlInfo());
+    $this->drupalPostForm($this->book->toUrl('delete-form'), [], t('Delete'));
+    $this->drupalGet($nodes[0]->toUrl());
     $this->assertResponse(200);
     $this->assertText($nodes[0]->label());
     // The book parents should be updated.
diff --git a/core/modules/book/tests/src/Functional/BookTestTrait.php b/core/modules/book/tests/src/Functional/BookTestTrait.php
index 0998cb0d36d4edc3f11f07d9b9e6246e2755f19b..221ed1fcf56a69db8137ccdd8864f840f8976960 100644
--- a/core/modules/book/tests/src/Functional/BookTestTrait.php
+++ b/core/modules/book/tests/src/Functional/BookTestTrait.php
@@ -102,7 +102,7 @@ public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $ne
     // Check previous, up, and next links.
     if ($previous) {
       /** @var \Drupal\Core\Url $url */
-      $url = $previous->urlInfo();
+      $url = $previous->toUrl();
       $url->setOptions(['attributes' => ['rel' => ['prev'], 'title' => t('Go to previous page')]]);
       $text = new FormattableMarkup('<b>‹</b> @label', ['@label' => $previous->label()]);
       $this->assertRaw(\Drupal::l($text, $url), 'Previous page link found.');
@@ -110,14 +110,14 @@ public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $ne
 
     if ($up) {
       /** @var \Drupal\Core\Url $url */
-      $url = $up->urlInfo();
+      $url = $up->toUrl();
       $url->setOptions(['attributes' => ['title' => t('Go to parent page')]]);
       $this->assertRaw(\Drupal::l('Up', $url), 'Up page link found.');
     }
 
     if ($next) {
       /** @var \Drupal\Core\Url $url */
-      $url = $next->urlInfo();
+      $url = $next->toUrl();
       $url->setOptions(['attributes' => ['rel' => ['next'], 'title' => t('Go to next page')]]);
       $text = new FormattableMarkup('@label <b>›</b>', ['@label' => $next->label()]);
       $this->assertRaw(\Drupal::l($text, $url), 'Next page link found.');
@@ -127,7 +127,7 @@ public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $ne
     $expected_breadcrumb = [];
     $expected_breadcrumb[] = \Drupal::url('<front>');
     foreach ($breadcrumb as $a_node) {
-      $expected_breadcrumb[] = $a_node->url();
+      $expected_breadcrumb[] = $a_node->toUrl()->toString();
     }
 
     // Fetch links in the current breadcrumb.
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index de891c7a0ee6ed7fbc604a86d1a563b819d9618c..dd0acb35658f6abb97b376368e6da6ab74865eac 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -240,7 +240,7 @@ function comment_entity_view(array &$build, EntityInterface $entity, EntityViewD
         ];
         $entity->rss_elements[] = [
           'key' => 'comments',
-          'value' => $entity->url('canonical', $options),
+          'value' => $entity->toUrl('canonical', $options)->toString(),
         ];
       }
     }
diff --git a/core/modules/comment/comment.tokens.inc b/core/modules/comment/comment.tokens.inc
index e29872fe9150cf490ff62c7e1700d88020b395e5..a9de4d6c38a64d28ba6de12471a2bffee45d8d87 100644
--- a/core/modules/comment/comment.tokens.inc
+++ b/core/modules/comment/comment.tokens.inc
@@ -180,12 +180,12 @@ function comment_tokens($type, $tokens, array $data, array $options, BubbleableM
         // Comment related URLs.
         case 'url':
           $url_options['fragment'] = 'comment-' . $comment->id();
-          $replacements[$original] = $comment->url('canonical', $url_options);
+          $replacements[$original] = $comment->toUrl('canonical', $url_options)->toString();
           break;
 
         case 'edit-url':
           $url_options['fragment'] = NULL;
-          $replacements[$original] = $comment->url('edit-form', $url_options);
+          $replacements[$original] = $comment->toUrl('edit-form', $url_options)->toString();
           break;
 
         case 'author':
diff --git a/core/modules/comment/src/CommentBreadcrumbBuilder.php b/core/modules/comment/src/CommentBreadcrumbBuilder.php
index 1ea4616bb98bb6e663fbc37f3dce437142c7ac9a..ed67197f055ef8ed1904c7a53da2251207e8d42b 100644
--- a/core/modules/comment/src/CommentBreadcrumbBuilder.php
+++ b/core/modules/comment/src/CommentBreadcrumbBuilder.php
@@ -48,7 +48,7 @@ public function build(RouteMatchInterface $route_match) {
     $breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
 
     $entity = $route_match->getParameter('entity');
-    $breadcrumb->addLink(new Link($entity->label(), $entity->urlInfo()));
+    $breadcrumb->addLink(new Link($entity->label(), $entity->toUrl()));
     $breadcrumb->addCacheableDependency($entity);
 
     if (($pid = $route_match->getParameter('pid')) && ($comment = $this->storage->load($pid))) {
@@ -56,7 +56,7 @@ public function build(RouteMatchInterface $route_match) {
       $breadcrumb->addCacheableDependency($comment);
       // Display link to parent comment.
       // @todo Clean-up permalink in https://www.drupal.org/node/2198041
-      $breadcrumb->addLink(new Link($comment->getSubject(), $comment->urlInfo()));
+      $breadcrumb->addLink(new Link($comment->getSubject(), $comment->toUrl()));
     }
 
     return $breadcrumb;
diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index c3ade475992950ba7c1ae68e59334852c430c0bb..57e59d1b41db71e6a7134254a9ff1efed1b317cd 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -369,7 +369,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $comment = $this->entity;
     $entity = $comment->getCommentedEntity();
     $field_name = $comment->getFieldName();
-    $uri = $entity->urlInfo();
+    $uri = $entity->toUrl();
     $logger = $this->logger('comment');
 
     if ($this->currentUser->hasPermission('post comments') && ($this->currentUser->hasPermission('administer comments') || $entity->{$field_name}->status == CommentItemInterface::OPEN)) {
@@ -379,7 +379,7 @@ public function save(array $form, FormStateInterface $form_state) {
       // Add a log entry.
       $logger->notice('Comment posted: %subject.', [
           '%subject' => $comment->getSubject(),
-          'link' => $this->l(t('View'), $comment->urlInfo()->setOption('fragment', 'comment-' . $comment->id())),
+          'link' => $this->l(t('View'), $comment->toUrl()->setOption('fragment', 'comment-' . $comment->id())),
         ]);
 
       // Explain the approval queue if necessary.
diff --git a/core/modules/comment/src/CommentLazyBuilders.php b/core/modules/comment/src/CommentLazyBuilders.php
index 730c840897f25d2125a304e8e49f921a9616e7ff..a2c1338c10d1d5f76cc9e3604039fee828a1153e 100644
--- a/core/modules/comment/src/CommentLazyBuilders.php
+++ b/core/modules/comment/src/CommentLazyBuilders.php
@@ -169,14 +169,14 @@ protected function buildLinks(CommentInterface $entity, EntityInterface $comment
       if ($entity->access('delete')) {
         $links['comment-delete'] = [
           'title' => t('Delete'),
-          'url' => $entity->urlInfo('delete-form'),
+          'url' => $entity->toUrl('delete-form'),
         ];
       }
 
       if ($entity->access('update')) {
         $links['comment-edit'] = [
           'title' => t('Edit'),
-          'url' => $entity->urlInfo('edit-form'),
+          'url' => $entity->toUrl('edit-form'),
         ];
       }
       if ($entity->access('create')) {
@@ -205,7 +205,7 @@ protected function buildLinks(CommentInterface $entity, EntityInterface $comment
     if ($this->moduleHandler->moduleExists('content_translation') && $this->access($entity)->isAllowed()) {
       $links['comment-translations'] = [
         'title' => t('Translate'),
-        'url' => $entity->urlInfo('drupal:content-translation-overview'),
+        'url' => $entity->toUrl('drupal:content-translation-overview'),
       ];
     }
 
diff --git a/core/modules/comment/src/CommentLinkBuilder.php b/core/modules/comment/src/CommentLinkBuilder.php
index 60be27082ab3989cb392cc03a13eb451b1b3f1a9..434e1b4156cc378482c426b1a7633c5e41316765 100644
--- a/core/modules/comment/src/CommentLinkBuilder.php
+++ b/core/modules/comment/src/CommentLinkBuilder.php
@@ -207,10 +207,10 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra
             $query = $page_number ? ['page' => $page_number] : NULL;
             $value = [
               'new_comment_count' => (int) $new_comments,
-              'first_new_comment_link' => $entity->url('canonical', [
+              'first_new_comment_link' => $entity->toUrl('canonical', [
                 'query' => $query,
                 'fragment' => 'new',
-              ]),
+              ])->toString(),
             ];
             $parents = ['comment', 'newCommentsLinks', $entity->getEntityTypeId(), $field_name, $entity->id()];
             NestedArray::setValue($entity_links['comment__' . $field_name]['#attached']['drupalSettings'], $parents, $value);
diff --git a/core/modules/comment/src/CommentManager.php b/core/modules/comment/src/CommentManager.php
index 25e53e70c42c5aebcdb86b48de84049c380680cb..9e1b4e883762c37c37302e0b89f5822de4587e1f 100644
--- a/core/modules/comment/src/CommentManager.php
+++ b/core/modules/comment/src/CommentManager.php
@@ -149,7 +149,7 @@ public function forbiddenMessage(EntityInterface $entity, $field_name) {
         $destination = ['destination' => Url::fromRoute('comment.reply', $comment_reply_parameters, ['fragment' => 'comment-form'])->toString()];
       }
       else {
-        $destination = ['destination' => $entity->url('canonical', ['fragment' => 'comment-form'])];
+        $destination = ['destination' => $entity->toUrl('canonical', ['fragment' => 'comment-form'])->toString()];
       }
 
       if ($this->userConfig->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
diff --git a/core/modules/comment/src/CommentTypeForm.php b/core/modules/comment/src/CommentTypeForm.php
index fd7125846c6a7d46287705fb5828cefc510bd3b3..2df24bc9b3f35c21e41da01a638ab06821a48cc0 100644
--- a/core/modules/comment/src/CommentTypeForm.php
+++ b/core/modules/comment/src/CommentTypeForm.php
@@ -158,7 +158,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $comment_type = $this->entity;
     $status = $comment_type->save();
 
-    $edit_link = $this->entity->link($this->t('Edit'));
+    $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString();
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus(t('Comment type %label has been updated.', ['%label' => $comment_type->label()]));
       $this->logger->notice('Comment type %label has been updated.', ['%label' => $comment_type->label(), 'link' => $edit_link]);
@@ -169,7 +169,7 @@ public function save(array $form, FormStateInterface $form_state) {
       $this->logger->notice('Comment type %label has been added.', ['%label' => $comment_type->label(), 'link' => $edit_link]);
     }
 
-    $form_state->setRedirectUrl($comment_type->urlInfo('collection'));
+    $form_state->setRedirectUrl($comment_type->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php
index ad821e333df1da4eb354e6918cade34d274f6158..be11d80a30de5e624dda14fca08b4f25bd07e002 100644
--- a/core/modules/comment/src/Controller/CommentController.php
+++ b/core/modules/comment/src/Controller/CommentController.php
@@ -124,7 +124,7 @@ public function commentPermalink(Request $request, CommentInterface $comment) {
       // Find the current display page for this comment.
       $page = $this->entityManager()->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page'));
       // @todo: Cleaner sub request handling.
-      $subrequest_url = $entity->urlInfo()->setOption('query', ['page' => $page])->toString(TRUE);
+      $subrequest_url = $entity->toUrl()->setOption('query', ['page' => $page])->toString(TRUE);
       $redirect_request = Request::create($subrequest_url->getGeneratedUrl(), 'GET', $request->query->all(), $request->cookies->all(), [], $request->server->all());
       // Carry over the session to the subrequest.
       if ($session = $request->getSession()) {
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 222cdae65ad431fb594045a5ff3f98b752dbcade..bd225d23c10960634784c1563f4c713809f48972 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -211,7 +211,7 @@ public function referencedEntities() {
    * {@inheritdoc}
    */
   public function permalink() {
-    $uri = $this->urlInfo();
+    $uri = $this->toUrl();
     $uri->setOption('fragment', 'comment-' . $this->id());
     return $uri;
   }
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index 3b97d0d1c48b511ca2bf14df19c2da998f59649e..42c76e485bf9097b0d55b160e263e351a8225f34 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -215,21 +215,21 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
             '#type' => 'link',
             '#title' => $commented_entity->label(),
             '#access' => $commented_entity->access('view'),
-            '#url' => $commented_entity->urlInfo(),
+            '#url' => $commented_entity->toUrl(),
           ],
         ],
         'changed' => $this->dateFormatter->format($comment->getChangedTimeAcrossTranslations(), 'short'),
       ];
-      $comment_uri_options = $comment->urlInfo()->getOptions() + ['query' => $destination];
+      $comment_uri_options = $comment->toUrl()->getOptions() + ['query' => $destination];
       $links = [];
       $links['edit'] = [
         'title' => $this->t('Edit'),
-        'url' => $comment->urlInfo('edit-form', $comment_uri_options),
+        'url' => $comment->toUrl('edit-form', $comment_uri_options),
       ];
       if ($this->moduleHandler->moduleExists('content_translation') && $this->moduleHandler->invoke('content_translation', 'translate_access', [$comment])->isAllowed()) {
         $links['translate'] = [
           'title' => $this->t('Translate'),
-          'url' => $comment->urlInfo('drupal:content-translation-overview', $comment_uri_options),
+          'url' => $comment->toUrl('drupal:content-translation-overview', $comment_uri_options),
         ];
       }
       $options[$comment->id()]['operations']['data'] = [
diff --git a/core/modules/comment/src/Form/DeleteForm.php b/core/modules/comment/src/Form/DeleteForm.php
index 3b2028b0902fcf40187375977e5a9412438abc17..84ff1f999d503f7112c43d7b2c2caeaae1c8cb13 100644
--- a/core/modules/comment/src/Form/DeleteForm.php
+++ b/core/modules/comment/src/Form/DeleteForm.php
@@ -16,7 +16,7 @@ class DeleteForm extends ContentEntityDeleteForm {
    */
   public function getCancelUrl() {
     // Point to the entity of which this comment is a reply.
-    return $this->entity->get('entity_id')->entity->urlInfo();
+    return $this->entity->get('entity_id')->entity->toUrl();
   }
 
   /**
diff --git a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php
index bfb0b5cbd8273cb27c358ffc0db1219ecff43f57..a1efc1da103aa5432248d14240f805f65e0a2836 100644
--- a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php
+++ b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php
@@ -180,7 +180,7 @@ protected function renderLink($data, ResultRow $values) {
       $page_number = $entity_manager->getStorage('comment')
         ->getNewCommentPageNumber($this->getValue($values, 'comment_count'), $this->getValue($values), $node, $comment_field_name);
       $this->options['alter']['make_link'] = TRUE;
-      $this->options['alter']['url'] = $node->urlInfo();
+      $this->options['alter']['url'] = $node->toUrl();
       $this->options['alter']['query'] = $page_number ? ['page' => $page_number] : NULL;
       $this->options['alter']['fragment'] = 'new';
     }
diff --git a/core/modules/comment/src/Plugin/views/row/Rss.php b/core/modules/comment/src/Plugin/views/row/Rss.php
index cb9c1046d1a4bfbe3401f9c59065fecbbb611baa..023e45df8448784100e150094afe31816c0656e6 100644
--- a/core/modules/comment/src/Plugin/views/row/Rss.php
+++ b/core/modules/comment/src/Plugin/views/row/Rss.php
@@ -79,7 +79,7 @@ public function render($row) {
       return;
     }
 
-    $comment->link = $comment->url('canonical', ['absolute' => TRUE]);
+    $comment->link = $comment->toUrl('canonical', ['absolute' => TRUE])->toString();
     $comment->rss_namespaces = [];
     $comment->rss_elements = [
       [
diff --git a/core/modules/comment/tests/src/Functional/CommentAnonymousTest.php b/core/modules/comment/tests/src/Functional/CommentAnonymousTest.php
index 090b995a947ce549a88ab1b8bfe14561db1cd42b..95e1d11ae8b01a05d499c12a9e52715d789dc6d7 100644
--- a/core/modules/comment/tests/src/Functional/CommentAnonymousTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentAnonymousTest.php
@@ -41,7 +41,7 @@ public function testAnonymous() {
     $body = 'comment body with skip comment approval';
     $edit['subject[0][value]'] = $title;
     $edit['comment_body[0][value]'] = $body;
-    $this->drupalPostForm($this->node->urlInfo(), $edit, t('Preview'));
+    $this->drupalPostForm($this->node->toUrl(), $edit, t('Preview'));
     // Cannot use assertRaw here since both title and body are in the form.
     $preview = (string) $this->cssSelect('.preview')[0]->getHtml();
     $this->assertTrue(strpos($preview, $title) !== FALSE, 'Anonymous user can preview comment title.');
@@ -54,7 +54,7 @@ public function testAnonymous() {
     $body = 'comment body without skip comment approval';
     $edit['subject[0][value]'] = $title;
     $edit['comment_body[0][value]'] = $body;
-    $this->drupalPostForm($this->node->urlInfo(), $edit, t('Preview'));
+    $this->drupalPostForm($this->node->toUrl(), $edit, t('Preview'));
     // Cannot use assertRaw here since both title and body are in the form.
     $preview = (string) $this->cssSelect('.preview')[0]->getHtml();
     $this->assertTrue(strpos($preview, $title) !== FALSE, 'Anonymous user can preview comment title.');
diff --git a/core/modules/comment/tests/src/Functional/CommentCacheTagsTest.php b/core/modules/comment/tests/src/Functional/CommentCacheTagsTest.php
index 0b0f4863afe72a9bde70d168fa087694afd558a7..9db678f31a35274cbe350b9c41bde41d51be7b99 100644
--- a/core/modules/comment/tests/src/Functional/CommentCacheTagsTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentCacheTagsTest.php
@@ -94,8 +94,8 @@ protected function createEntity() {
    * Test that comments correctly invalidate the cache tag of their host entity.
    */
   public function testCommentEntity() {
-    $this->verifyPageCache($this->entityTestCamelid->urlInfo(), 'MISS');
-    $this->verifyPageCache($this->entityTestCamelid->urlInfo(), 'HIT');
+    $this->verifyPageCache($this->entityTestCamelid->toUrl(), 'MISS');
+    $this->verifyPageCache($this->entityTestCamelid->toUrl(), 'HIT');
 
     // Create a "Hippopotamus" comment.
     $this->entityTestHippopotamidae = EntityTest::create([
@@ -104,8 +104,8 @@ public function testCommentEntity() {
     ]);
     $this->entityTestHippopotamidae->save();
 
-    $this->verifyPageCache($this->entityTestHippopotamidae->urlInfo(), 'MISS');
-    $this->verifyPageCache($this->entityTestHippopotamidae->urlInfo(), 'HIT');
+    $this->verifyPageCache($this->entityTestHippopotamidae->toUrl(), 'MISS');
+    $this->verifyPageCache($this->entityTestHippopotamidae->toUrl(), 'HIT');
 
     $hippo_comment = Comment::create([
       'subject' => 'Hippopotamus',
@@ -121,15 +121,15 @@ public function testCommentEntity() {
     $hippo_comment->save();
 
     // Ensure that a new comment only invalidates the commented entity.
-    $this->verifyPageCache($this->entityTestCamelid->urlInfo(), 'HIT');
-    $this->verifyPageCache($this->entityTestHippopotamidae->urlInfo(), 'MISS');
+    $this->verifyPageCache($this->entityTestCamelid->toUrl(), 'HIT');
+    $this->verifyPageCache($this->entityTestHippopotamidae->toUrl(), 'MISS');
     $this->assertText($hippo_comment->getSubject());
 
     // Ensure that updating an existing comment only invalidates the commented
     // entity.
     $this->entity->save();
-    $this->verifyPageCache($this->entityTestCamelid->urlInfo(), 'MISS');
-    $this->verifyPageCache($this->entityTestHippopotamidae->urlInfo(), 'HIT');
+    $this->verifyPageCache($this->entityTestCamelid->toUrl(), 'MISS');
+    $this->verifyPageCache($this->entityTestHippopotamidae->toUrl(), 'HIT');
   }
 
   /**
diff --git a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
index 2df997334839b347c74e81ffc7eab02daf9c5d0f..2e64fed31c66fad7f24f8f36ff5ed6a137836bd3 100644
--- a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
@@ -113,7 +113,7 @@ public function testCommentFieldLinksNonDefaultName() {
 
     // Go to the node first so that webuser2 see new comments.
     $this->drupalLogin($web_user2);
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $this->drupalLogout();
 
     // Test that buildCommentedEntityLinks() does not break when the 'comment'
@@ -135,7 +135,7 @@ public function testCommentFieldLinksNonDefaultName() {
 
     $link_info = $this->getDrupalSettings()['comment']['newCommentsLinks']['node']['comment2']['2'];
     $this->assertIdentical($link_info['new_comment_count'], 1);
-    $this->assertIdentical($link_info['first_new_comment_link'], $node->url('canonical', ['fragment' => 'new']));
+    $this->assertIdentical($link_info['first_new_comment_link'], $node->toUrl('canonical', ['fragment' => 'new'])->toString());
   }
 
   /**
diff --git a/core/modules/comment/tests/src/Functional/CommentInterfaceTest.php b/core/modules/comment/tests/src/Functional/CommentInterfaceTest.php
index bee8fec423494f05a578d8851a0460cf2357c77e..e81d3fb3d1ad059c38e6ed91816288b8d4b7ad2e 100644
--- a/core/modules/comment/tests/src/Functional/CommentInterfaceTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentInterfaceTest.php
@@ -25,7 +25,7 @@ protected function setUp() {
     $this->drupalLogin($this->adminUser);
     // Make sure that comment field title is not displayed when there's no
     // comments posted.
-    $this->drupalGet($this->node->urlInfo());
+    $this->drupalGet($this->node->toUrl());
     $this->assertSession()->responseNotMatches('@<h2[^>]*>Comments</h2>@', 'Comments title is not displayed.');
 
     // Set comments to have subject and preview disabled.
@@ -48,7 +48,7 @@ public function testCommentInterface() {
     $this->assertTrue($this->commentExists($comment), 'Comment found.');
 
     // Test the comment field title is displayed when there's comments.
-    $this->drupalGet($this->node->urlInfo());
+    $this->drupalGet($this->node->toUrl());
     $this->assertPattern('@<h2[^>]*>Comments</h2>@', 'Comments title is displayed.');
 
     // Set comments to have subject and preview to required.
diff --git a/core/modules/comment/tests/src/Functional/CommentLinksTest.php b/core/modules/comment/tests/src/Functional/CommentLinksTest.php
index 3d79d966420e36913ff0d1951d4bd0ef4dc10c13..20876a28c42f28b873e0ae3ca07028020e2b43dc 100644
--- a/core/modules/comment/tests/src/Functional/CommentLinksTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentLinksTest.php
@@ -105,7 +105,7 @@ public function testCommentLinks() {
     entity_get_display('comment', 'comment', 'default')
       ->setComponent('links', ['weight' => -100])
       ->save();
-    $this->drupalGet($this->node->urlInfo());
+    $this->drupalGet($this->node->toUrl());
     $element = $this->cssSelect('article.js-comment > div');
     // Get last child element.
     $element = end($element);
@@ -115,7 +115,7 @@ public function testCommentLinks() {
     entity_get_display('comment', 'comment', 'default')
       ->setComponent('links', ['weight' => 100])
       ->save();
-    $this->drupalGet($this->node->urlInfo());
+    $this->drupalGet($this->node->toUrl());
     $element = $this->cssSelect('article.js-comment > div');
     // Get last child element.
     $element = end($element);
@@ -125,7 +125,7 @@ public function testCommentLinks() {
     entity_get_display('node', $this->node->bundle(), 'default')
       ->removeComponent('links')
       ->save();
-    $this->drupalGet($this->node->urlInfo());
+    $this->drupalGet($this->node->toUrl());
     $this->assertNoLink('1 comment');
     $this->assertNoLink('Add new comment');
 
diff --git a/core/modules/comment/tests/src/Functional/CommentNewIndicatorTest.php b/core/modules/comment/tests/src/Functional/CommentNewIndicatorTest.php
index 69f7bdeeed55ceaebc946e991dde4efb9de02504..b7426ecdad4c37e3099a82f3462b599343671974 100644
--- a/core/modules/comment/tests/src/Functional/CommentNewIndicatorTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentNewIndicatorTest.php
@@ -117,7 +117,7 @@ public function testCommentNewCommentsIndicator() {
     $expected = [
       $this->node->id() => [
         'new_comment_count' => 1,
-        'first_new_comment_link' => $this->node->url('canonical', ['fragment' => 'new']),
+        'first_new_comment_link' => $this->node->toUrl('canonical', ['fragment' => 'new'])->toString(),
       ],
     ];
     $this->assertIdentical($expected, $json);
diff --git a/core/modules/comment/tests/src/Functional/CommentRssTest.php b/core/modules/comment/tests/src/Functional/CommentRssTest.php
index 230120d7674bd766184c3b24fd820168fbd5a41b..33122dd2cd72b53f20d7aa8c4d4c74beb3f9ee75 100644
--- a/core/modules/comment/tests/src/Functional/CommentRssTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentRssTest.php
@@ -66,7 +66,7 @@ public function testCommentRss() {
       'user:3',
     ]));
 
-    $raw = '<comments>' . $this->node->url('canonical', ['fragment' => 'comments', 'absolute' => TRUE]) . '</comments>';
+    $raw = '<comments>' . $this->node->toUrl('canonical', ['fragment' => 'comments', 'absolute' => TRUE])->toString() . '</comments>';
     $this->assertRaw($raw, 'Comments as part of RSS feed.');
 
     // Hide comments from RSS feed and check presence.
diff --git a/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php b/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
index 47c980418ad5903b165a0634435f269c24546b62..cc0cc1df9cb622762694cc4f65b6fdad0c63c7c4 100644
--- a/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
@@ -80,8 +80,8 @@ public function testCommentTokenReplacement() {
     $tests['[comment:title]'] = Html::escape($comment->getSubject());
     $tests['[comment:body]'] = $comment->comment_body->processed;
     $tests['[comment:langcode]'] = $comment->language()->getId();
-    $tests['[comment:url]'] = $comment->url('canonical', $url_options + ['fragment' => 'comment-' . $comment->id()]);
-    $tests['[comment:edit-url]'] = $comment->url('edit-form', $url_options);
+    $tests['[comment:url]'] = $comment->toUrl('canonical', $url_options + ['fragment' => 'comment-' . $comment->id()])->toString();
+    $tests['[comment:edit-url]'] = $comment->toUrl('edit-form', $url_options)->toString();
     $tests['[comment:created]'] = \Drupal::service('date.formatter')->format($comment->getCreatedTime(), 'medium', ['langcode' => $language_interface->getId()]);
     $tests['[comment:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getCreatedTime(), ['langcode' => $language_interface->getId()]);
     $tests['[comment:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getChangedTimeAcrossTranslations(), ['langcode' => $language_interface->getId()]);
diff --git a/core/modules/comment/tests/src/Functional/CommentTranslationUITest.php b/core/modules/comment/tests/src/Functional/CommentTranslationUITest.php
index 3830e2fde195943a2ab90612e6baac7b4f6d970a..daf673f10ecf552309f480b5e006379589bb0e7a 100644
--- a/core/modules/comment/tests/src/Functional/CommentTranslationUITest.php
+++ b/core/modules/comment/tests/src/Functional/CommentTranslationUITest.php
@@ -134,7 +134,7 @@ protected function doTestPublishedStatus() {
     foreach ($this->langcodes as $index => $langcode) {
       if ($index > 0) {
         $edit = ['status' => 0];
-        $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
+        $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
         $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
         $storage->resetCache();
         $entity = $storage->load($this->entityId);
@@ -156,7 +156,7 @@ protected function doTestAuthoringInfo() {
 
     // Post different authoring information for each translation.
     foreach ($this->langcodes as $langcode) {
-      $url = $entity->urlInfo('edit-form', ['language' => $languages[$langcode]]);
+      $url = $entity->toUrl('edit-form', ['language' => $languages[$langcode]]);
       $user = $this->drupalCreateUser();
       $values[$langcode] = [
         'uid' => $user->id(),
@@ -210,7 +210,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('Edit @type @title [%language translation]', [
diff --git a/core/modules/config/tests/config_test/src/ConfigTestController.php b/core/modules/config/tests/config_test/src/ConfigTestController.php
index d352eca1641ef4d62319710d5349e5e68686cd4d..3a1b5f53e1ee0891b94b2acb0d87477ed8d3ad87 100644
--- a/core/modules/config/tests/config_test/src/ConfigTestController.php
+++ b/core/modules/config/tests/config_test/src/ConfigTestController.php
@@ -35,7 +35,7 @@ public function editTitle(ConfigTest $config_test) {
    */
   public function enable(ConfigTest $config_test) {
     $config_test->enable()->save();
-    return new RedirectResponse($config_test->url('collection', ['absolute' => TRUE]));
+    return new RedirectResponse($config_test->toUrl('collection', ['absolute' => TRUE])->toString());
   }
 
   /**
@@ -49,7 +49,7 @@ public function enable(ConfigTest $config_test) {
    */
   public function disable(ConfigTest $config_test) {
     $config_test->disable()->save();
-    return new RedirectResponse($config_test->url('collection', ['absolute' => TRUE]));
+    return new RedirectResponse($config_test->toUrl('collection', ['absolute' => TRUE])->toString());
   }
 
 }
diff --git a/core/modules/config/tests/config_test/src/ConfigTestForm.php b/core/modules/config/tests/config_test/src/ConfigTestForm.php
index 86e021e38c2cd11ba79c93bb3dcf3841c8c50173..163359613d01daa2e5c7362602c4f7fb3586198e 100644
--- a/core/modules/config/tests/config_test/src/ConfigTestForm.php
+++ b/core/modules/config/tests/config_test/src/ConfigTestForm.php
@@ -144,7 +144,7 @@ public function save(array $form, FormStateInterface $form_state) {
       $this->messenger()->addStatus(format_string('%label configuration has been created.', ['%label' => $entity->label()]));
     }
 
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
   /**
diff --git a/core/modules/config/tests/src/Functional/ConfigDependencyWebTest.php b/core/modules/config/tests/src/Functional/ConfigDependencyWebTest.php
index 696a36e8cb2916c80f0a159abd7f2d4dd8eea5e1..f8569d0d794904a5894719563462569e8987d2a9 100644
--- a/core/modules/config/tests/src/Functional/ConfigDependencyWebTest.php
+++ b/core/modules/config/tests/src/Functional/ConfigDependencyWebTest.php
@@ -58,14 +58,14 @@ public function testConfigDependencyDeleteFormTrait() {
     );
     $entity2->save();
 
-    $this->drupalGet($entity2->urlInfo('delete-form'));
+    $this->drupalGet($entity2->toUrl('delete-form'));
     $this->assertNoText(t('Configuration updates'), 'No configuration updates found.');
     $this->assertNoText(t('Configuration deletions'), 'No configuration deletes found.');
-    $this->drupalGet($entity1->urlInfo('delete-form'));
+    $this->drupalGet($entity1->toUrl('delete-form'));
     $this->assertNoText(t('Configuration updates'), 'No configuration updates found.');
     $this->assertText(t('Configuration deletions'), 'Configuration deletions found.');
     $this->assertText($entity2->id(), 'Entity2 id found');
-    $this->drupalPostForm($entity1->urlInfo('delete-form'), [], 'Delete');
+    $this->drupalPostForm($entity1->toUrl('delete-form'), [], 'Delete');
     $storage->resetCache();
     $this->assertFalse($storage->loadMultiple([$entity1->id(), $entity2->id()]), 'Test entities deleted');
 
@@ -109,13 +109,13 @@ public function testConfigDependencyDeleteFormTrait() {
     );
     $entity3->save();
 
-    $this->drupalGet($entity1->urlInfo('delete-form'));
+    $this->drupalGet($entity1->toUrl('delete-form'));
     $this->assertText(t('Configuration updates'), 'Configuration updates found.');
     $this->assertNoText(t('Configuration deletions'), 'No configuration deletions found.');
     $this->assertNoText($entity2->id(), 'Entity2 id not found');
     $this->assertText($entity2->label(), 'Entity2 label not found');
     $this->assertNoText($entity3->id(), 'Entity3 id not found');
-    $this->drupalPostForm($entity1->urlInfo('delete-form'), [], 'Delete');
+    $this->drupalPostForm($entity1->toUrl('delete-form'), [], 'Delete');
     $storage->resetCache();
     $this->assertFalse($storage->load('entity1'), 'Test entity 1 deleted');
     $entity2 = $storage->load('entity2');
diff --git a/core/modules/config/tests/src/Functional/ConfigEntityStatusUITest.php b/core/modules/config/tests/src/Functional/ConfigEntityStatusUITest.php
index af68f33bdc20e11522cd131c67222d83a9f16c0e..3db476bb3e1b8f121e5cd29596cd111873d3c2a7 100644
--- a/core/modules/config/tests/src/Functional/ConfigEntityStatusUITest.php
+++ b/core/modules/config/tests/src/Functional/ConfigEntityStatusUITest.php
@@ -34,14 +34,14 @@ public function testCRUD() {
     $entity = entity_load('config_test', $id);
 
     // Disable an entity.
-    $disable_url = $entity->urlInfo('disable');
+    $disable_url = $entity->toUrl('disable');
     $this->assertLinkByHref($disable_url->toString());
     $this->drupalGet($disable_url);
     $this->assertResponse(200);
     $this->assertNoLinkByHref($disable_url->toString());
 
     // Enable an entity.
-    $enable_url = $entity->urlInfo('enable');
+    $enable_url = $entity->toUrl('enable');
     $this->assertLinkByHref($enable_url->toString());
     $this->drupalGet($enable_url);
     $this->assertResponse(200);
diff --git a/core/modules/config/tests/src/Functional/ConfigEntityTest.php b/core/modules/config/tests/src/Functional/ConfigEntityTest.php
index 61669d9379e450f38b07bcaf61c0d52e4e062d51..94b9be764b0523b90ba1057c1a9646d73998c0b2 100644
--- a/core/modules/config/tests/src/Functional/ConfigEntityTest.php
+++ b/core/modules/config/tests/src/Functional/ConfigEntityTest.php
@@ -60,7 +60,7 @@ public function testCRUD() {
     $this->assertIdentical($empty->getEntityTypeId(), 'config_test');
     // The URI can only be checked after saving.
     try {
-      $empty->urlInfo();
+      $empty->toUrl();
       $this->fail('EntityMalformedException was thrown.');
     }
     catch (EntityMalformedException $e) {
@@ -119,7 +119,7 @@ public function testCRUD() {
     }
 
     // The entity path can only be checked after saving.
-    $this->assertIdentical($config_test->url(), Url::fromRoute('entity.config_test.edit_form', ['config_test' => $expected['id']])->toString());
+    $this->assertIdentical($config_test->toUrl()->toString(), Url::fromRoute('entity.config_test.edit_form', ['config_test' => $expected['id']])->toString());
 
     // Verify that the correct status is returned and properties did not change.
     $this->assertIdentical($status, SAVED_NEW);
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index 344d90e27b10e83ba6f28148c23691fa62279820..e6750ed4dfc10d9433258e734daf8d6158b80324 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -167,7 +167,7 @@ function config_translation_entity_operation(EntityInterface $entity) {
     $operations['translate'] = [
       'title' => t('Translate'),
       'weight' => 50,
-      'url' => $entity->urlInfo($link_template),
+      'url' => $entity->toUrl($link_template),
     ];
   }
 
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 5234949fb229340efc2be390c7914069e5aff341..2115fc76f67fca15eeb46a12bdbe914fc1adc3fb 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -127,7 +127,7 @@ function contact_mail($key, &$message, $params) {
     '@sender-name' => $sender->getDisplayName(),
   ];
   if ($sender->isAuthenticated()) {
-    $variables['@sender-url'] = $sender->url('canonical', ['absolute' => TRUE, 'language' => $language]);
+    $variables['@sender-url'] = $sender->toUrl('canonical', ['absolute' => TRUE, 'language' => $language])->toString();
   }
   else {
     $variables['@sender-url'] = $params['sender']->getEmail();
@@ -153,7 +153,7 @@ function contact_mail($key, &$message, $params) {
     case 'user_copy':
       $variables += [
         '@recipient-name' => $params['recipient']->getDisplayName(),
-        '@recipient-edit-url' => $params['recipient']->url('edit-form', ['absolute' => TRUE, 'language' => $language]),
+        '@recipient-edit-url' => $params['recipient']->toUrl('edit-form', ['absolute' => TRUE, 'language' => $language])->toString(),
       ];
       $message['subject'] .= t('[@site-name] @subject', $variables, $options);
       $message['body'][] = t('Hello @recipient-name,', $variables, $options);
diff --git a/core/modules/contact/src/ContactFormEditForm.php b/core/modules/contact/src/ContactFormEditForm.php
index 5ca2fc49d1adb8643acd13713945bb1d36fa1286..48b29928d054165f4b549e4ac2bb6bba4ebcb8f8 100644
--- a/core/modules/contact/src/ContactFormEditForm.php
+++ b/core/modules/contact/src/ContactFormEditForm.php
@@ -163,8 +163,8 @@ public function save(array $form, FormStateInterface $form_state) {
     $status = $contact_form->save();
     $contact_settings = $this->config('contact.settings');
 
-    $edit_link = $this->entity->link($this->t('Edit'));
-    $view_link = $contact_form->link($contact_form->label(), 'canonical');
+    $edit_link = $this->entity->toLink($this->t('Edit'))->toString();
+    $view_link = $contact_form->toLink($contact_form->label(), 'canonical')->toString();
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus($this->t('Contact form %label has been updated.', ['%label' => $view_link]));
       $this->logger('contact')->notice('Contact form %label has been updated.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
@@ -187,7 +187,7 @@ public function save(array $form, FormStateInterface $form_state) {
         ->save();
     }
 
-    $form_state->setRedirectUrl($contact_form->urlInfo('collection'));
+    $form_state->setRedirectUrl($contact_form->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/contact/src/ContactFormListBuilder.php b/core/modules/contact/src/ContactFormListBuilder.php
index 0cea280f5d2d79eb159b7782752bd659d7ebf714..9a4d6cc32773f17c64ea2b4f2ae3e4d07952f651 100644
--- a/core/modules/contact/src/ContactFormListBuilder.php
+++ b/core/modules/contact/src/ContactFormListBuilder.php
@@ -33,7 +33,7 @@ public function buildRow(EntityInterface $entity) {
       $row['selected'] = t('No');
     }
     else {
-      $row['form'] = $entity->link(NULL, 'canonical');
+      $row['form'] = $entity->toLink(NULL, 'canonical')->toString();
       $row['recipients']['data'] = [
         '#theme' => 'item_list',
         '#items' => $entity->getRecipients(),
diff --git a/core/modules/contact/src/MessageForm.php b/core/modules/contact/src/MessageForm.php
index f273e52cf07a6ce97b60a5df232415d535c902d0..e59208376a5db2d9447484cdcfe1057b51139750 100644
--- a/core/modules/contact/src/MessageForm.php
+++ b/core/modules/contact/src/MessageForm.php
@@ -230,7 +230,7 @@ public function save(array $form, FormStateInterface $form_state) {
     // To avoid false error messages caused by flood control, redirect away from
     // the contact form; either to the contacted user account or the front page.
     if ($message->isPersonal() && $user->hasPermission('access user profiles')) {
-      $form_state->setRedirectUrl($message->getPersonalRecipient()->urlInfo());
+      $form_state->setRedirectUrl($message->getPersonalRecipient()->toUrl());
     }
     else {
       $form_state->setRedirectUrl($contact_form->getRedirectUrl());
diff --git a/core/modules/contact/tests/src/Functional/Views/ContactLinkTest.php b/core/modules/contact/tests/src/Functional/Views/ContactLinkTest.php
index c3b68f067a6baa9c02d091c93a5580a0a32abf58..edad89cda44579d14cff40a8c4ceb4cbbd50599c 100644
--- a/core/modules/contact/tests/src/Functional/Views/ContactLinkTest.php
+++ b/core/modules/contact/tests/src/Functional/Views/ContactLinkTest.php
@@ -100,7 +100,7 @@ public function assertContactLinks(array $accounts, array $names) {
     foreach ($names as $name) {
       $account = $accounts[$name];
 
-      $result = $this->xpath('//div[contains(@class, "views-field-contact")]//a[contains(@href, :url)]', [':url' => $account->url('contact-form')]);
+      $result = $this->xpath('//div[contains(@class, "views-field-contact")]//a[contains(@href, :url)]', [':url' => $account->toUrl('contact-form')->toString()]);
       $this->assertTrue(count($result));
     }
   }
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index ebd9ca87e532a486a58c9d51a4a6c487121f95d7..2fb8a8637399ce221309e14700e48f8c3229e5c5 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -274,7 +274,7 @@ function content_translation_entity_operation(EntityInterface $entity) {
   if ($entity->hasLinkTemplate('drupal:content-translation-overview') && content_translation_translate_access($entity)->isAllowed()) {
     $operations['translate'] = [
       'title' => t('Translate'),
-      'url' => $entity->urlInfo('drupal:content-translation-overview'),
+      'url' => $entity->toUrl('drupal:content-translation-overview'),
       'weight' => 50,
     ];
   }
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index 586c4245f52ed5fb86400d0df80887407ab81f22..1767dcdb0670c2ce488ba7520dcd93f1dd26910b 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -758,7 +758,7 @@ public function entityFormDeleteTranslation($form, FormStateInterface $form_stat
     $entity = $form_object->getEntity();
     $entity_type_id = $entity->getEntityTypeId();
     if ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form')) {
-      $form_state->setRedirectUrl($entity->urlInfo('delete-form'));
+      $form_state->setRedirectUrl($entity->toUrl('delete-form'));
     }
     else {
       $form_state->setRedirect("entity.$entity_type_id.content_translation_delete", [
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index e5af1c31f43c0b309c600abcc26eba34b84c76ed..94603231a5908e5df03d26451c37bd2ed34ef765 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -168,7 +168,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
           $source = $metadata->getSource() ?: LanguageInterface::LANGCODE_NOT_SPECIFIED;
           $is_original = $langcode == $original;
           $label = $entity->getTranslation($langcode)->label();
-          $link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : ['url' => $entity->urlInfo()];
+          $link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : ['url' => $entity->toUrl()];
           if (!empty($link['url'])) {
             $link['url']->setOption('language', $language);
             $row_title = $this->l($label, $link['url']);
@@ -187,7 +187,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
             ->merge(CacheableMetadata::createFromObject($update_access))
             ->merge(CacheableMetadata::createFromObject($translation_access));
           if ($update_access->isAllowed() && $entity_type->hasLinkTemplate('edit-form')) {
-            $links['edit']['url'] = $entity->urlInfo('edit-form');
+            $links['edit']['url'] = $entity->toUrl('edit-form');
             $links['edit']['language'] = $language;
           }
           elseif (!$is_original && $translation_access->isAllowed()) {
@@ -228,7 +228,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
               if ($delete_access->isAllowed() && $entity_type->hasLinkTemplate('delete-form')) {
                 $links['delete'] = [
                   'title' => $this->t('Delete'),
-                  'url' => $entity->urlInfo('delete-form'),
+                  'url' => $entity->toUrl('delete-form'),
                   'language' => $language,
                 ];
               }
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index 47f2f65a66dea494ff6e5b774e3c19882ed4fe53..e63086a77a7bd59abb0e9c6f6422708b13992cc2 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -89,14 +89,14 @@ protected function doTestBasicTranslation() {
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
     $this->assertTrue($entity, 'Entity found in the database.');
-    $this->drupalGet($entity->urlInfo());
+    $this->drupalGet($entity->toUrl());
     $this->assertResponse(200, 'Entity URL is valid.');
 
     // Ensure that the content language cache context is not yet added to the
     // page.
     $this->assertCacheContexts($this->defaultCacheContexts);
 
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertNoText('Source language', 'Source language column correctly hidden.');
 
     $translation = $this->getTranslation($entity, $default_langcode);
@@ -132,7 +132,7 @@ protected function doTestBasicTranslation() {
       ->getStorage($this->entityTypeId);
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo());
+    $this->drupalGet($entity->toUrl());
     $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
 
     // Reset the cache of the entity, so that the new translation gets the
@@ -168,7 +168,7 @@ protected function doTestBasicTranslation() {
     }
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertNoText('Source language', 'Source language column correctly hidden.');
 
     // Switch the source language.
@@ -199,7 +199,7 @@ protected function doTestBasicTranslation() {
     $this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertText('Source language', 'Source language column correctly shown.');
 
     // Check that the entered values have been correctly stored.
@@ -222,17 +222,17 @@ protected function doTestTranslationOverview() {
       ->getStorage($this->entityTypeId);
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $translate_url = $entity->urlInfo('drupal:content-translation-overview');
+    $translate_url = $entity->toUrl('drupal:content-translation-overview');
     $this->drupalGet($translate_url);
     $translate_url->setAbsolute(FALSE);
 
     foreach ($this->langcodes as $langcode) {
       if ($entity->hasTranslation($langcode)) {
         $language = new Language(['id' => $langcode]);
-        $view_url = $entity->url('canonical', ['language' => $language]);
+        $view_url = $entity->toUrl('canonical', ['language' => $language])->toString();
         $elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
         $this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', ['%language' => $langcode]));
-        $edit_path = $entity->url('edit-form', ['language' => $language]);
+        $edit_path = $entity->toUrl('edit-form', ['language' => $language])->toString();
         $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
         $this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', ['%language' => $langcode]));
       }
@@ -252,7 +252,7 @@ protected function doTestOutdatedStatus() {
 
     // Mark translations as outdated.
     $edit = ['content_translation[retranslate]' => TRUE];
-    $edit_path = $entity->urlInfo('edit-form', ['language' => $languages[$langcode]]);
+    $edit_path = $entity->toUrl('edit-form', ['language' => $languages[$langcode]]);
     $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
@@ -260,7 +260,7 @@ protected function doTestOutdatedStatus() {
     // Check that every translation has the correct "outdated" status, and that
     // the Translation fieldset is open if the translation is "outdated".
     foreach ($this->langcodes as $added_langcode) {
-      $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
+      $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
       $this->drupalGet($url);
       if ($added_langcode == $langcode) {
         $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
@@ -294,7 +294,7 @@ protected function doTestPublishedStatus() {
     // Unpublish translations.
     foreach ($this->langcodes as $index => $langcode) {
       if ($index > 0) {
-        $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
+        $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
         $edit = ['content_translation[status]' => FALSE];
         $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
         $storage = $this->container->get('entity_type.manager')
@@ -306,7 +306,7 @@ protected function doTestPublishedStatus() {
     }
 
     // Check that the last published translation cannot be unpublished.
-    $this->drupalGet($entity->urlInfo('edit-form'));
+    $this->drupalGet($entity->toUrl('edit-form'));
     $this->assertFieldByXPath('//input[@name="content_translation[status]" and @disabled="disabled"]', TRUE, 'The last translation is published and cannot be unpublished.');
   }
 
@@ -331,7 +331,7 @@ protected function doTestAuthoringInfo() {
         'content_translation[uid]' => $user->getAccountName(),
         'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
       ];
-      $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
+      $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
       $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
     }
 
@@ -352,7 +352,7 @@ protected function doTestAuthoringInfo() {
       'content_translation[uid]' => $this->randomMachineName(12),
       'content_translation[created]' => '19/11/1978',
     ];
-    $this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
+    $this->drupalPostForm($entity->toUrl('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
     $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
     $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
     $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
@@ -371,7 +371,7 @@ protected function doTestTranslationDeletion() {
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
     $language = ConfigurableLanguage::load($langcode);
-    $url = $entity->urlInfo('edit-form', ['language' => $language]);
+    $url = $entity->toUrl('edit-form', ['language' => $language]);
     $this->drupalPostForm($url, [], t('Delete translation'));
     $this->drupalPostForm(NULL, [], t('Delete @language translation', ['@language' => $language->getName()]));
     $storage->resetCache([$this->entityId]);
@@ -515,7 +515,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $this->assertRaw($entity->getTranslation($langcode)->label());
@@ -560,7 +560,7 @@ protected function doTestTranslationChanged() {
         $edit = [
           $this->fieldName . '[0][value]' => $this->randomString(),
         ];
-        $edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
+        $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
         $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
 
         $storage = $this->container->get('entity_type.manager')
@@ -620,7 +620,7 @@ public function doTestChangedTimeAfterSaveWithoutChanges() {
 
       // Save the entity on the regular edit form.
       $language = $entity->language();
-      $edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
+      $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
       $this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
 
       $storage->resetCache([$this->entityId]);
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationOperationsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationOperationsTest.php
index 6eb3deb1c8e2f79641507e116c3450be681d36cf..1a0f2f54fcf6dec258b126afb13d993463141d35 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationOperationsTest.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationOperationsTest.php
@@ -86,7 +86,7 @@ public function testOperationTranslateLink() {
       ]
     );
     $this->drupalLogin($this->baseUser1);
-    $this->drupalGet($node->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($node->toUrl('drupal:content-translation-overview'));
     $this->assertResponse(403);
 
     // Ensure that the translation overview is also not accessible when the user
@@ -99,7 +99,7 @@ public function testOperationTranslateLink() {
       ]
     );
     $node->setUnpublished()->save();
-    $this->drupalGet($node->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($node->toUrl('drupal:content-translation-overview'));
     $this->assertResponse(403);
     $this->drupalLogout();
 
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
index 6e2a2d79aa116bd2d8ee7be302583ccda6e965c2..2f6487819ca724f2a7d27db6bf624486a7bded62 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
@@ -82,14 +82,14 @@ protected function doTestBasicTranslation() {
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
     $this->assertTrue($entity, 'Entity found in the database.');
-    $this->drupalGet($entity->urlInfo());
+    $this->drupalGet($entity->toUrl());
     $this->assertResponse(200, 'Entity URL is valid.');
 
     // Ensure that the content language cache context is not yet added to the
     // page.
     $this->assertCacheContexts($this->defaultCacheContexts);
 
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertNoText('Source language', 'Source language column correctly hidden.');
 
     $translation = $this->getTranslation($entity, $default_langcode);
@@ -125,7 +125,7 @@ protected function doTestBasicTranslation() {
       ->getStorage($this->entityTypeId);
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo());
+    $this->drupalGet($entity->toUrl());
     $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
 
     // Reset the cache of the entity, so that the new translation gets the
@@ -161,7 +161,7 @@ protected function doTestBasicTranslation() {
     }
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertNoText('Source language', 'Source language column correctly hidden.');
 
     // Switch the source language.
@@ -192,7 +192,7 @@ protected function doTestBasicTranslation() {
     $this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
     $this->assertText('Source language', 'Source language column correctly shown.');
 
     // Check that the entered values have been correctly stored.
@@ -215,17 +215,17 @@ protected function doTestTranslationOverview() {
       ->getStorage($this->entityTypeId);
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
-    $translate_url = $entity->urlInfo('drupal:content-translation-overview');
+    $translate_url = $entity->toUrl('drupal:content-translation-overview');
     $this->drupalGet($translate_url);
     $translate_url->setAbsolute(FALSE);
 
     foreach ($this->langcodes as $langcode) {
       if ($entity->hasTranslation($langcode)) {
         $language = new Language(['id' => $langcode]);
-        $view_url = $entity->url('canonical', ['language' => $language]);
+        $view_url = $entity->toUrl('canonical', ['language' => $language])->toString();
         $elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
         $this->assertEqual($elements[0]->getText(), $entity->getTranslation($langcode)->label(), new FormattableMarkup('Label correctly shown for %language translation.', ['%language' => $langcode]));
-        $edit_path = $entity->url('edit-form', ['language' => $language]);
+        $edit_path = $entity->toUrl('edit-form', ['language' => $language])->toString();
         $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
         $this->assertEqual($elements[0]->getText(), t('Edit'), new FormattableMarkup('Edit link correct for %language translation.', ['%language' => $langcode]));
       }
@@ -245,7 +245,7 @@ protected function doTestOutdatedStatus() {
 
     // Mark translations as outdated.
     $edit = ['content_translation[retranslate]' => TRUE];
-    $edit_path = $entity->urlInfo('edit-form', ['language' => $languages[$langcode]]);
+    $edit_path = $entity->toUrl('edit-form', ['language' => $languages[$langcode]]);
     $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
@@ -253,7 +253,7 @@ protected function doTestOutdatedStatus() {
     // Check that every translation has the correct "outdated" status, and that
     // the Translation fieldset is open if the translation is "outdated".
     foreach ($this->langcodes as $added_langcode) {
-      $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
+      $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
       $this->drupalGet($url);
       if ($added_langcode == $langcode) {
         $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
@@ -287,7 +287,7 @@ protected function doTestPublishedStatus() {
     // Unpublish translations.
     foreach ($this->langcodes as $index => $langcode) {
       if ($index > 0) {
-        $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
+        $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
         $edit = ['content_translation[status]' => FALSE];
         $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
         $storage = $this->container->get('entity_type.manager')
@@ -299,7 +299,7 @@ protected function doTestPublishedStatus() {
     }
 
     // Check that the last published translation cannot be unpublished.
-    $this->drupalGet($entity->urlInfo('edit-form'));
+    $this->drupalGet($entity->toUrl('edit-form'));
     $this->assertFieldByXPath('//input[@name="content_translation[status]" and @disabled="disabled"]', TRUE, 'The last translation is published and cannot be unpublished.');
   }
 
@@ -324,7 +324,7 @@ protected function doTestAuthoringInfo() {
         'content_translation[uid]' => $user->getAccountName(),
         'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
       ];
-      $url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
+      $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
       $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
     }
 
@@ -345,7 +345,7 @@ protected function doTestAuthoringInfo() {
       'content_translation[uid]' => $this->randomMachineName(12),
       'content_translation[created]' => '19/11/1978',
     ];
-    $this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
+    $this->drupalPostForm($entity->toUrl('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
     $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
     $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
     $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
@@ -364,7 +364,7 @@ protected function doTestTranslationDeletion() {
     $storage->resetCache([$this->entityId]);
     $entity = $storage->load($this->entityId);
     $language = ConfigurableLanguage::load($langcode);
-    $url = $entity->urlInfo('edit-form', ['language' => $language]);
+    $url = $entity->toUrl('edit-form', ['language' => $language]);
     $this->drupalPostForm($url, [], t('Delete translation'));
     $this->drupalPostForm(NULL, [], t('Delete @language translation', ['@language' => $language->getName()]));
     $storage->resetCache([$this->entityId]);
@@ -508,7 +508,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $this->assertRaw($entity->getTranslation($langcode)->label());
@@ -553,7 +553,7 @@ protected function doTestTranslationChanged() {
         $edit = [
           $this->fieldName . '[0][value]' => $this->randomString(),
         ];
-        $edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
+        $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
         $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
 
         $storage = $this->container->get('entity_type.manager')
@@ -613,7 +613,7 @@ public function doTestChangedTimeAfterSaveWithoutChanges() {
 
       // Save the entity on the regular edit form.
       $language = $entity->language();
-      $edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
+      $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
       $this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
 
       $storage->resetCache([$this->entityId]);
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php
index eaa638efcb88e5533dfb0a96431b0b7ccbda5177..04dbb9e0b4c297e46043eef55a977a34dd00f168 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php
@@ -118,7 +118,7 @@ public function testWorkflows() {
 
     // Check that translation permissions allow the associated operations.
     $ops = ['create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete')];
-    $translations_url = $this->entity->urlInfo('drupal:content-translation-overview');
+    $translations_url = $this->entity->toUrl('drupal:content-translation-overview');
     foreach ($ops as $current_op => $item) {
       $user = $this->drupalCreateUser([$this->getTranslatePermission(), "$current_op content translations", 'view test entity']);
       $this->drupalLogin($user);
@@ -159,19 +159,19 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
     $this->drupalLogin($user);
 
     // Check whether the user is allowed to access the entity form in edit mode.
-    $edit_url = $this->entity->urlInfo('edit-form', $options);
+    $edit_url = $this->entity->toUrl('edit-form', $options);
     $this->drupalGet($edit_url, $options);
     $this->assertResponse($expected_status['edit'], new FormattableMarkup('The @user_label has the expected edit access.', $args));
 
     // Check whether the user is allowed to access the entity delete form.
-    $delete_url = $this->entity->urlInfo('delete-form', $options);
+    $delete_url = $this->entity->toUrl('delete-form', $options);
     $this->drupalGet($delete_url, $options);
     $this->assertResponse($expected_status['delete'], new FormattableMarkup('The @user_label has the expected delete access.', $args));
 
     // Check whether the user is allowed to access the translation overview.
     $langcode = $this->langcodes[1];
     $options['language'] = $languages[$langcode];
-    $translations_url = $this->entity->url('drupal:content-translation-overview', $options);
+    $translations_url = $this->entity->toUrl('drupal:content-translation-overview', $options)->toString();
     $this->drupalGet($translations_url);
     $this->assertResponse($expected_status['overview'], new FormattableMarkup('The @user_label has the expected translation overview access.', $args));
 
@@ -203,7 +203,7 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
         $this->clickLink('Edit', 2);
         // An editor should be pointed to the entity form in multilingual mode.
         // We need a new expected edit path with a new language.
-        $expected_edit_path = $this->entity->url('edit-form', $options);
+        $expected_edit_path = $this->entity->toUrl('edit-form', $options)->toString();
         $this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.');
       }
       else {
@@ -232,7 +232,7 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
         // An editor should be pointed to the entity deletion form in
         // multilingual mode. We need a new expected delete path with a new
         // language.
-        $expected_delete_path = $this->entity->url('delete-form', $options);
+        $expected_delete_path = $this->entity->toUrl('delete-form', $options)->toString();
         $this->assertUrl($expected_delete_path, [], 'The translation overview points to the delete form for editors when deleting translations.');
       }
       else {
diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceFieldTranslatedReferenceViewTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceFieldTranslatedReferenceViewTest.php
index cf53a7402a3e7282d7b2162415730a5d77c1b99f..fb0ee0733d047f98d16e078921c89602d990aaab 100644
--- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceFieldTranslatedReferenceViewTest.php
+++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceFieldTranslatedReferenceViewTest.php
@@ -169,8 +169,8 @@ public function testEntityReferenceDisplay() {
    * Assert entity reference display.
    */
   protected function assertEntityReferenceDisplay() {
-    $url = $this->referrerEntity->urlInfo();
-    $translation_url = $this->referrerEntity->urlInfo('canonical', ['language' => ConfigurableLanguage::load($this->translateToLangcode)]);
+    $url = $this->referrerEntity->toUrl();
+    $translation_url = $this->referrerEntity->toUrl('canonical', ['language' => ConfigurableLanguage::load($this->translateToLangcode)]);
 
     $this->drupalGet($url);
     $this->assertText($this->labelOfNotTranslatedReference, 'The label of not translated reference is displayed.');
@@ -187,8 +187,8 @@ protected function assertEntityReferenceDisplay() {
    */
   protected function assertEntityReferenceFormDisplay() {
     $this->drupalLogin($this->webUser);
-    $url = $this->referrerEntity->urlInfo('edit-form');
-    $translation_url = $this->referrerEntity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($this->translateToLangcode)]);
+    $url = $this->referrerEntity->toUrl('edit-form');
+    $translation_url = $this->referrerEntity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($this->translateToLangcode)]);
 
     $this->drupalGet($url);
     $this->assertSession()->fieldValueEquals('test_reference_field[0][target_id]', $this->originalLabel . ' (1)');
diff --git a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
index 88a693277c8be11503e0e136b90e380c25060fab..e7558803edda778c677eaed463980f29c3fcf78c 100644
--- a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
+++ b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
@@ -353,8 +353,8 @@ public function testLabelFormatter() {
     $expected_item_1 = [
       '#type' => 'link',
       '#title' => $this->referencedEntity->label(),
-      '#url' => $this->referencedEntity->urlInfo(),
-      '#options' => $this->referencedEntity->urlInfo()->getOptions(),
+      '#url' => $this->referencedEntity->toUrl(),
+      '#options' => $this->referencedEntity->toUrl()->getOptions(),
       '#cache' => [
         'contexts' => [
           'user.permissions',
diff --git a/core/modules/field/tests/src/Kernel/String/StringFormatterTest.php b/core/modules/field/tests/src/Kernel/String/StringFormatterTest.php
index 74f16d582a514e7b3e0a581aab770ea75e99abf6..f6087013c18c327c5c1b4be331930ce5c3f461a5 100644
--- a/core/modules/field/tests/src/Kernel/String/StringFormatterTest.php
+++ b/core/modules/field/tests/src/Kernel/String/StringFormatterTest.php
@@ -142,11 +142,11 @@ public function testStringFormatter() {
 
     $this->renderEntityFields($entity, $this->display);
     $this->assertLink($value, 0);
-    $this->assertLinkByHref($entity->url());
+    $this->assertLinkByHref($entity->toUrl()->toString());
 
-    // $entity->url('revision') falls back to the canonical URL if this is no
+    // $entity->toUrl('revision') falls back to the canonical URL if this is no
     // revision.
-    $this->assertLinkByHref($entity->url('revision'));
+    $this->assertLinkByHref($entity->toUrl('revision')->toString());
 
     // Make the entity a new revision.
     $old_revision_id = $entity->getRevisionId();
@@ -158,7 +158,7 @@ public function testStringFormatter() {
 
     $this->renderEntityFields($entity, $this->display);
     $this->assertLink($value2, 0);
-    $this->assertLinkByHref($entity->url('revision'));
+    $this->assertLinkByHref($entity->toUrl('revision')->toString());
 
     $this->renderEntityFields($entity_new_revision, $this->display);
     $this->assertLink($value, 0);
@@ -175,7 +175,7 @@ public function testStringFormatter() {
 
     $this->renderEntityFields($entity_new_revision, $this->display);
     $this->assertLink($value, 0);
-    $this->assertLinkByHref($entity->url('canonical'));
+    $this->assertLinkByHref($entity->toUrl('canonical')->toString());
   }
 
 }
diff --git a/core/modules/field/tests/src/Kernel/String/UuidFormatterTest.php b/core/modules/field/tests/src/Kernel/String/UuidFormatterTest.php
index 0eeb7780764bd1b0f43d1245ae0c9402d8238712..a942eab7637ad810cbdcdf8ddb3a12edac079430 100644
--- a/core/modules/field/tests/src/Kernel/String/UuidFormatterTest.php
+++ b/core/modules/field/tests/src/Kernel/String/UuidFormatterTest.php
@@ -49,10 +49,10 @@ public function testUuidStringFormatter() {
     $render_array = $uuid_field->view(['settings' => ['link_to_entity' => TRUE]]);
     $this->assertIdentical($render_array[0]['#type'], 'link');
     $this->assertIdentical($render_array[0]['#title']['#context']['value'], $entity->uuid());
-    $this->assertIdentical($render_array[0]['#url']->toString(), $entity->url());
+    $this->assertIdentical($render_array[0]['#url']->toString(), $entity->toUrl()->toString());
     $rendered = $this->render($render_array);
     $this->assertTrue(strpos($rendered, $entity->uuid()), 'The rendered UUID found.');
-    $this->assertTrue(strpos($rendered, $entity->url()), 'The rendered entity URL found.');
+    $this->assertTrue(strpos($rendered, $entity->toUrl()->toString()), 'The rendered entity URL found.');
   }
 
 }
diff --git a/core/modules/field_ui/src/FieldConfigListBuilder.php b/core/modules/field_ui/src/FieldConfigListBuilder.php
index b32b21db873b933f7a9dccd60b9b5e0b7c73825b..a6363aa0ee6e4ff9147bf709a2f82728b542d81d 100644
--- a/core/modules/field_ui/src/FieldConfigListBuilder.php
+++ b/core/modules/field_ui/src/FieldConfigListBuilder.php
@@ -160,7 +160,7 @@ public function getDefaultOperations(EntityInterface $entity) {
       $operations['edit'] = [
         'title' => $this->t('Edit'),
         'weight' => 10,
-        'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-edit-form"),
+        'url' => $entity->toUrl("{$entity->getTargetEntityTypeId()}-field-edit-form"),
         'attributes' => [
           'title' => $this->t('Edit field settings.'),
         ],
@@ -170,7 +170,7 @@ public function getDefaultOperations(EntityInterface $entity) {
       $operations['delete'] = [
         'title' => $this->t('Delete'),
         'weight' => 100,
-        'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-delete-form"),
+        'url' => $entity->toUrl("{$entity->getTargetEntityTypeId()}-field-delete-form"),
         'attributes' => [
           'title' => $this->t('Delete field.'),
         ],
@@ -181,7 +181,7 @@ public function getDefaultOperations(EntityInterface $entity) {
       'title' => $this->t('Storage settings'),
       'weight' => 20,
       'attributes' => ['title' => $this->t('Edit storage settings.')],
-      'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-storage-edit-form"),
+      'url' => $entity->toUrl("{$entity->getTargetEntityTypeId()}-storage-edit-form"),
     ];
 
     return $operations;
diff --git a/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php b/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php
index 0e502f71b71a84149502b3ed370ba9b79e732207..2a5ed1686852d05d3a88d5728e5ef15aa7f6fc14 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php
@@ -81,7 +81,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $this->messenger()->addStatus($this->t('Saved the %label @entity-type.', ['%label' => $this->entity->label(), '@entity-type' => $this->entityType->getLowercaseLabel()]));
     $this->entity->save();
     \Drupal::entityManager()->clearCachedFieldDefinitions();
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/filter/src/FilterPermissions.php b/core/modules/filter/src/FilterPermissions.php
index bdad0e9ca54a575b9e08f5069c0e24ee644cdf2d..7002bb27c117fd4f53432f0e8d5156b75ae58a00 100644
--- a/core/modules/filter/src/FilterPermissions.php
+++ b/core/modules/filter/src/FilterPermissions.php
@@ -53,7 +53,7 @@ public function permissions() {
     foreach ($formats as $format) {
       if ($permission = $format->getPermissionName()) {
         $permissions[$permission] = [
-          'title' => $this->t('Use the <a href=":url">@label</a> text format', [':url' => $format->url(), '@label' => $format->label()]),
+          'title' => $this->t('Use the <a href=":url">@label</a> text format', [':url' => $format->toUrl()->toString(), '@label' => $format->label()]),
           'description' => [
             '#prefix' => '<em>',
             '#markup' => $this->t('Warning: This permission may have security implications depending on how the text format is configured.'),
diff --git a/core/modules/filter/tests/src/Functional/FilterAdminTest.php b/core/modules/filter/tests/src/Functional/FilterAdminTest.php
index e00ca6df1f0192a79691b9c72a7d6b686b93d6f8..73289e4cd563e1d4d8324964547a48966c5ff4ec 100644
--- a/core/modules/filter/tests/src/Functional/FilterAdminTest.php
+++ b/core/modules/filter/tests/src/Functional/FilterAdminTest.php
@@ -412,13 +412,13 @@ public function testDisabledFormat() {
 
     // The format is used and we should see the static text instead of the body
     // value.
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $this->assertText('filtered text');
 
     // Disable the format.
     $format->disable()->save();
 
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
 
     // The format is not used anymore.
     $this->assertNoText('filtered text');
@@ -437,7 +437,7 @@ public function testDisabledFormat() {
     $format_id = $this->randomMachineName();
     $node->body->format = $format_id;
     $node->save();
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     // The text is not displayed unfiltered or escaped.
     $this->assertNoRaw($body_value);
     $this->assertNoEscaped($body_value);
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 2833779d96d63e9a118c0d9681e81c3e69bd1074..4af6116cfc438ddf73fbe9999d812813c00c0d1b 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -452,7 +452,7 @@ function template_preprocess_forums(&$variables) {
         }
         else {
           $variables['topics'][$id]->moved = FALSE;
-          $variables['topics'][$id]->title_link = \Drupal::l($topic->getTitle(), $topic->urlInfo());
+          $variables['topics'][$id]->title_link = \Drupal::l($topic->getTitle(), $topic->toUrl());
           $variables['topics'][$id]->message = '';
         }
         $forum_submitted = [
diff --git a/core/modules/forum/src/Form/ForumForm.php b/core/modules/forum/src/Form/ForumForm.php
index fdee13a6e897ff064aa7bdff677061cbd43204da..46f7f8b4965c2097534e28ed8bec004cc7c77d38 100644
--- a/core/modules/forum/src/Form/ForumForm.php
+++ b/core/modules/forum/src/Form/ForumForm.php
@@ -79,7 +79,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $route_name = $this->urlStub == 'container' ? 'entity.taxonomy_term.forum_edit_container_form' : 'entity.taxonomy_term.forum_edit_form';
     $route_parameters = ['taxonomy_term' => $term->id()];
     $link = $this->l($this->t('Edit'), new Url($route_name, $route_parameters));
-    $view_link = $term->link($term->getName());
+    $view_link = $term->toLink($term->getName())->toString();
     switch ($status) {
       case SAVED_NEW:
         $this->messenger()->addStatus($this->t('Created new @type %term.', ['%term' => $view_link, '@type' => $this->forumFormType]));
@@ -104,7 +104,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
     $actions = parent::actions($form, $form_state);
 
     if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('forum-delete-form')) {
-      $actions['delete']['#url'] = $this->entity->urlInfo('forum-delete-form');
+      $actions['delete']['#url'] = $this->entity->toUrl('forum-delete-form');
     }
     else {
       unset($actions['delete']);
diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
index 18f54eb40f03bd695aa87b488397967417f74a9b..c8034ab98f5ddcec4e9233de15b23bb9a7f7efa0 100644
--- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
@@ -186,10 +186,9 @@ public function denormalize($data, $class, $format = NULL, array $context = [])
    *   The entity URI.
    */
   protected function getEntityUri(EntityInterface $entity, array $context = []) {
-    // Some entity types don't provide a canonical link template, at least call
-    // out to ->url().
+    // Some entity types don't provide a canonical link template.
     if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) {
-      return $entity->url('canonical', []);
+      return '';
     }
     $url = $entity->toUrl('canonical', ['absolute' => TRUE]);
     if (!$url->isExternal()) {
diff --git a/core/modules/hal/tests/src/Kernel/NormalizeTest.php b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
index 6c93f23607af33e14560eef7ab6f864780278694..1f3283e3fc2dca9ec7408d47b8ca4c45e5382a36 100644
--- a/core/modules/hal/tests/src/Kernel/NormalizeTest.php
+++ b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
@@ -201,7 +201,7 @@ public function testNormalize() {
    *   The entity URI.
    */
   protected function getEntityUri(EntityInterface $entity) {
-    $url = $entity->urlInfo('canonical', ['absolute' => TRUE]);
+    $url = $entity->toUrl('canonical', ['absolute' => TRUE]);
     return $url->setRouteParameter('_format', 'hal_json')->toString();
   }
 
diff --git a/core/modules/image/src/Form/ImageEffectDeleteForm.php b/core/modules/image/src/Form/ImageEffectDeleteForm.php
index ae167fa6ab41beae7fa7af7cc7306d2f3dbd67d7..cc8dbb6c9b3a15846a53bc71ae66e5c412142ecf 100644
--- a/core/modules/image/src/Form/ImageEffectDeleteForm.php
+++ b/core/modules/image/src/Form/ImageEffectDeleteForm.php
@@ -45,7 +45,7 @@ public function getConfirmText() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->imageStyle->urlInfo('edit-form');
+    return $this->imageStyle->toUrl('edit-form');
   }
 
   /**
@@ -71,7 +71,7 @@ public function buildForm(array $form, FormStateInterface $form_state, ImageStyl
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->imageStyle->deleteImageEffect($this->imageEffect);
     $this->messenger()->addStatus($this->t('The image effect %name has been deleted.', ['%name' => $this->imageEffect->label()]));
-    $form_state->setRedirectUrl($this->imageStyle->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->imageStyle->toUrl('edit-form'));
   }
 
 }
diff --git a/core/modules/image/src/Form/ImageEffectFormBase.php b/core/modules/image/src/Form/ImageEffectFormBase.php
index d62baba4a080ef96104fdaa8232d898d5996ebb6..ccf03c2253bd64491cc8a4e7d01c17bc8387e7e2 100644
--- a/core/modules/image/src/Form/ImageEffectFormBase.php
+++ b/core/modules/image/src/Form/ImageEffectFormBase.php
@@ -92,7 +92,7 @@ public function buildForm(array $form, FormStateInterface $form_state, ImageStyl
     $form['actions']['cancel'] = [
       '#type' => 'link',
       '#title' => $this->t('Cancel'),
-      '#url' => $this->imageStyle->urlInfo('edit-form'),
+      '#url' => $this->imageStyle->toUrl('edit-form'),
       '#attributes' => ['class' => ['button']],
     ];
     return $form;
@@ -124,7 +124,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->imageStyle->save();
 
     $this->messenger()->addStatus($this->t('The image effect was successfully applied.'));
-    $form_state->setRedirectUrl($this->imageStyle->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->imageStyle->toUrl('edit-form'));
   }
 
   /**
diff --git a/core/modules/image/src/Form/ImageStyleFlushForm.php b/core/modules/image/src/Form/ImageStyleFlushForm.php
index f07213fb604972bfe4991f89b3cd22113576ed84..9f796ab8ceecafc54168dc5b2a115f373c02a005 100644
--- a/core/modules/image/src/Form/ImageStyleFlushForm.php
+++ b/core/modules/image/src/Form/ImageStyleFlushForm.php
@@ -37,7 +37,7 @@ public function getConfirmText() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->entity->urlInfo('collection');
+    return $this->entity->toUrl('collection');
   }
 
   /**
diff --git a/core/modules/image/src/Form/ImageStyleFormBase.php b/core/modules/image/src/Form/ImageStyleFormBase.php
index bf3210a1d8ec3584f283a7b2db989b505a323a0d..e446f69afefade6ee6e56d4e476072ae020f56cc 100644
--- a/core/modules/image/src/Form/ImageStyleFormBase.php
+++ b/core/modules/image/src/Form/ImageStyleFormBase.php
@@ -73,7 +73,7 @@ public function form(array $form, FormStateInterface $form_state) {
    */
   public function save(array $form, FormStateInterface $form_state) {
     parent::save($form, $form_state);
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('edit-form'));
   }
 
 }
diff --git a/core/modules/image/src/ImageStyleListBuilder.php b/core/modules/image/src/ImageStyleListBuilder.php
index 2dcb800afae76b5fcb1b0a4bc777ebc9b8ace2f5..41d9220b899a951ef0647d22f4f2bd325ed2cd8e 100644
--- a/core/modules/image/src/ImageStyleListBuilder.php
+++ b/core/modules/image/src/ImageStyleListBuilder.php
@@ -36,7 +36,7 @@ public function getDefaultOperations(EntityInterface $entity) {
     $flush = [
       'title' => t('Flush'),
       'weight' => 200,
-      'url' => $entity->urlInfo('flush-form'),
+      'url' => $entity->toUrl('flush-form'),
     ];
 
     $operations = parent::getDefaultOperations($entity) + [
diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php
index 77714d2388332196a2cf5dfd3c8f51a86aa478ac..b48f0d14aa56f3028057cbbd396a8fc4d44c2cbd 100644
--- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php
+++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php
@@ -183,7 +183,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
     if ($image_link_setting == 'content') {
       $entity = $items->getEntity();
       if (!$entity->isNew()) {
-        $url = $entity->urlInfo();
+        $url = $entity->toUrl();
       }
     }
     elseif ($image_link_setting == 'file') {
diff --git a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php
index f026807656d5ff1dfea023463041fe8f7ae4878a..144d03ee179682e6af363663d4dc17eabfd0a37d 100644
--- a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php
+++ b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php
@@ -141,7 +141,7 @@ public function testStyle() {
     $this->assertEqual('bar', $style->getThirdPartySetting('image_module_test', 'foo'), 'Third party settings were added to the image style.');
 
     // Ensure that the image style URI matches our expected path.
-    $style_uri_path = $style->url();
+    $style_uri_path = $style->toUrl()->toString();
     $this->assertTrue(strpos($style_uri_path, $style_path) !== FALSE, 'The image style URI is correct.');
 
     // Confirm that all effects on the image style have settings that match
diff --git a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
index b0f38e88ddc6fce61d8b7974769e413d9e5e80b9..4014fc837817b4ad4189bef798c4a8594f31787e 100644
--- a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
+++ b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
@@ -169,7 +169,7 @@ public function _testImageFieldFormatters($scheme) {
     $elements = $this->xpath(
       '//a[@href=:path]/img[@src=:url and @alt=:alt and @width=:width and @height=:height]',
       [
-        ':path' => $node->url(),
+        ':path' => $node->toUrl()->toString(),
         ':url' => file_url_transform_relative(file_create_url($image['#uri'])),
         ':width' => $image['#width'],
         ':height' => $image['#height'],
diff --git a/core/modules/language/src/Form/LanguageAddForm.php b/core/modules/language/src/Form/LanguageAddForm.php
index c0ef22b22d8af92824c8618b643f71c35c2f234a..54faca91454d8842233be5b83383ea4f151fcced 100644
--- a/core/modules/language/src/Form/LanguageAddForm.php
+++ b/core/modules/language/src/Form/LanguageAddForm.php
@@ -95,7 +95,7 @@ public function save(array $form, FormStateInterface $form_state) {
       // to their theme so they can switch between the languages.
       $this->messenger()->addStatus($this->t('Use one of the language switcher blocks to allow site visitors to switch between languages. You can enable these blocks on the <a href=":block-admin">block administration page</a>.', [':block-admin' => $this->url('block.admin_display')]));
     }
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
   /**
diff --git a/core/modules/language/src/Form/LanguageEditForm.php b/core/modules/language/src/Form/LanguageEditForm.php
index 7930fbd14b81d1d85f252ed8352c8caad9c7ff52..80b6a2145021e237441652d7e785a204401f38b7 100644
--- a/core/modules/language/src/Form/LanguageEditForm.php
+++ b/core/modules/language/src/Form/LanguageEditForm.php
@@ -45,7 +45,7 @@ public function actions(array $form, FormStateInterface $form_state) {
    */
   public function save(array $form, FormStateInterface $form_state) {
     parent::save($form, $form_state);
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
     $this->logger('language')->notice('The %language (%langcode) language has been updated.', ['%language' => $this->entity->label(), '%langcode' => $this->entity->id()]);
   }
 
diff --git a/core/modules/language/src/LanguageListBuilder.php b/core/modules/language/src/LanguageListBuilder.php
index 21ef6ff5977a33f9ffb91819e25ecd08b198dd05..caf1d9187d43df182aa7737bc1b7ae9bf49ddb67 100644
--- a/core/modules/language/src/LanguageListBuilder.php
+++ b/core/modules/language/src/LanguageListBuilder.php
@@ -169,7 +169,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->messenger->addStatus($this->t('Configuration saved.'));
     // Force the redirection to the page with the language we have just
     // selected as default.
-    $form_state->setRedirectUrl($this->entities[$new_id]->urlInfo('collection', ['language' => $this->entities[$new_id]]));
+    $form_state->setRedirectUrl($this->entities[$new_id]->toUrl('collection', ['language' => $this->entities[$new_id]]));
   }
 
 }
diff --git a/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php b/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php
index 3ac9406252392ec6c07233f2e8164ac5092b7f4b..b158078e7a0262f6033146cb1cdc02fba86b295e 100644
--- a/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php
+++ b/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php
@@ -58,21 +58,21 @@ protected function setUp() {
    */
   public function testDefaultConfiguration() {
     $translation = $this->entity;
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
     $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
 
     $translation = $this->entity->getTranslation('es');
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
     $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
 
     $translation = $this->entity->getTranslation('fr');
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
@@ -123,7 +123,7 @@ public function testEnabledLanguageContentNegotiator() {
     $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
 
     $translation = $this->entity;
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
@@ -132,7 +132,7 @@ public function testEnabledLanguageContentNegotiator() {
     $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
 
     $translation = $this->entity->getTranslation('es');
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
@@ -140,7 +140,7 @@ public function testEnabledLanguageContentNegotiator() {
     $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
 
     $translation = $this->entity->getTranslation('fr');
-    $this->drupalGet($translation->urlInfo());
+    $this->drupalGet($translation->toUrl());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
diff --git a/core/modules/language/tests/src/Kernel/EntityUrlLanguageTest.php b/core/modules/language/tests/src/Kernel/EntityUrlLanguageTest.php
index 8154e42a46affdb45b2e73c489a79685e648abc0..802d1a243119f12da879b371f4937080f982760d 100644
--- a/core/modules/language/tests/src/Kernel/EntityUrlLanguageTest.php
+++ b/core/modules/language/tests/src/Kernel/EntityUrlLanguageTest.php
@@ -56,9 +56,9 @@ protected function setUp() {
    * Ensures that entity URLs in a language have the right language prefix.
    */
   public function testEntityUrlLanguage() {
-    $this->assertTrue(strpos($this->entity->urlInfo()->toString(), '/en/entity_test/' . $this->entity->id()) !== FALSE);
-    $this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo()->toString(), '/es/entity_test/' . $this->entity->id()) !== FALSE);
-    $this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo()->toString(), '/fr/entity_test/' . $this->entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->toUrl()->toString(), '/en/entity_test/' . $this->entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('es')->toUrl()->toString(), '/es/entity_test/' . $this->entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('fr')->toUrl()->toString(), '/fr/entity_test/' . $this->entity->id()) !== FALSE);
   }
 
   /**
@@ -89,9 +89,9 @@ public function testEntityUrlLanguageWithLanguageContentEnabled() {
     // The method language-content-entity should run before language-url and
     // append query parameter for the content language and prevent language-url
     // from overwriting the url.
-    $this->assertTrue(strpos($this->entity->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=en') !== FALSE);
-    $this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=es') !== FALSE);
-    $this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=fr') !== FALSE);
+    $this->assertTrue(strpos($this->entity->toUrl('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=en') !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('es')->toUrl('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=es') !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('fr')->toUrl('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=fr') !== FALSE);
 
     // Define the method language-url with a higher priority than
     // language-content-entity. This configuration should match the default one,
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
index e9f953850bd62cc304961c473f08da0e07c4f635..745958f0f9c1d41dd9c87fd1245527c244b90b7e 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
@@ -19,7 +19,7 @@ public function getCancelUrl() {
     if ($this->moduleHandler->moduleExists('menu_ui')) {
       return new Url('entity.menu.edit_form', ['menu' => $this->entity->getMenuName()]);
     }
-    return $this->entity->urlInfo();
+    return $this->entity->toUrl();
   }
 
   /**
diff --git a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
index 1888eb12110c01226e49e3af1d2c92379ef151b5..0c606d84988de91b5c4bbf4d7bc663730d0a7cd8 100644
--- a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
@@ -174,21 +174,21 @@ public function getDescription() {
    * {@inheritdoc}
    */
   public function getDeleteRoute() {
-    return $this->getEntity()->urlInfo('delete-form');
+    return $this->getEntity()->toUrl('delete-form');
   }
 
   /**
    * {@inheritdoc}
    */
   public function getEditRoute() {
-    return $this->getEntity()->urlInfo();
+    return $this->getEntity()->toUrl();
   }
 
   /**
    * {@inheritdoc}
    */
   public function getTranslateRoute() {
-    return $this->getEntity()->urlInfo('drupal:content-translation-overview');
+    return $this->getEntity()->toUrl('drupal:content-translation-overview');
   }
 
   /**
diff --git a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentDeleteFormTest.php b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentDeleteFormTest.php
index cb1e10daf94508ac242ae2ca57fa0e9e793d2fb5..ff829659e3f369a8232c38244036f04a367cc167 100644
--- a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentDeleteFormTest.php
+++ b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentDeleteFormTest.php
@@ -45,19 +45,19 @@ public function testMenuLinkContentDeleteForm() {
     $this->assertText(t('The menu link has been saved.'));
 
     $menu_link = MenuLinkContent::load(1);
-    $this->drupalGet($menu_link->urlInfo('delete-form'));
+    $this->drupalGet($menu_link->toUrl('delete-form'));
     $this->assertRaw(t('Are you sure you want to delete the custom menu link %name?', ['%name' => $menu_link->label()]));
     $this->assertLink(t('Cancel'));
     // Make sure cancel link points to link edit
-    $this->assertLinkByHref($menu_link->url('edit-form'));
+    $this->assertLinkByHref($menu_link->toUrl('edit-form')->toString());
 
     \Drupal::service('module_installer')->install(['menu_ui']);
     \Drupal::service('router.builder')->rebuild();
 
     // Make sure cancel URL points to menu_ui route now.
-    $this->drupalGet($menu_link->urlInfo('delete-form'));
+    $this->drupalGet($menu_link->toUrl('delete-form'));
     $menu = Menu::load($menu_link->getMenuName());
-    $this->assertLinkByHref($menu->url('edit-form'));
+    $this->assertLinkByHref($menu->toUrl('edit-form')->toString());
     $this->drupalPostForm(NULL, [], t('Delete'));
     $this->assertRaw(t('The menu link %title has been deleted.', ['%title' => $menu_link->label()]));
   }
diff --git a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentTranslationUITest.php
index 9fd1ce43fe97f20572c8bd91347dd839a77e7bcc..ec3cdf0535725013cd572adf1861416cfe794577 100644
--- a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentTranslationUITest.php
+++ b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentTranslationUITest.php
@@ -112,7 +112,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('@title [%language translation]', [
diff --git a/core/modules/menu_ui/src/MenuForm.php b/core/modules/menu_ui/src/MenuForm.php
index 9291fda0bf1621c4dfff2814e494b38cfdaa9326..15bf106794f739414b1a5682d4dfd82780742ba0 100644
--- a/core/modules/menu_ui/src/MenuForm.php
+++ b/core/modules/menu_ui/src/MenuForm.php
@@ -164,7 +164,7 @@ public function menuNameExists($value) {
   public function save(array $form, FormStateInterface $form_state) {
     $menu = $this->entity;
     $status = $menu->save();
-    $edit_link = $this->entity->link($this->t('Edit'));
+    $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString();
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus($this->t('Menu %label has been updated.', ['%label' => $menu->label()]));
       $this->logger('menu')->notice('Menu %label has been updated.', ['%label' => $menu->label(), 'link' => $edit_link]);
@@ -174,7 +174,7 @@ public function save(array $form, FormStateInterface $form_state) {
       $this->logger('menu')->notice('Menu %label has been added.', ['%label' => $menu->label(), 'link' => $edit_link]);
     }
 
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('edit-form'));
   }
 
   /**
@@ -269,7 +269,7 @@ protected function buildOverviewForm(array &$form, FormStateInterface $form_stat
 
     $form['links']['#empty'] = $this->t('There are no menu links yet. <a href=":url">Add link</a>.', [
       ':url' => $this->url('entity.menu.add_link_form', ['menu' => $this->entity->id()], [
-        'query' => ['destination' => $this->entity->url('edit-form')],
+        'query' => ['destination' => $this->entity->toUrl('edit-form')->toString()],
       ]),
     ]);
     $links = $this->buildOverviewTreeForm($tree, $delta);
diff --git a/core/modules/menu_ui/src/MenuListBuilder.php b/core/modules/menu_ui/src/MenuListBuilder.php
index b694c8b9372a06deb6ab23c3af79181fb3ac3ab3..c6efe07222775d9d52e283316f12cc23aa1d3ab5 100644
--- a/core/modules/menu_ui/src/MenuListBuilder.php
+++ b/core/modules/menu_ui/src/MenuListBuilder.php
@@ -48,7 +48,7 @@ public function getDefaultOperations(EntityInterface $entity) {
       $operations['add'] = [
         'title' => t('Add link'),
         'weight' => 20,
-        'url' => $entity->urlInfo('add-link-form'),
+        'url' => $entity->toUrl('add-link-form'),
       ];
     }
     if (isset($operations['delete'])) {
diff --git a/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
index 45ed71730a0fa575f6ad181f38d2f58b7567bd89..24a81d98093f7ddbb2b0ce6c44d3ccbf61cf8a88 100644
--- a/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
+++ b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
@@ -189,7 +189,7 @@ public function testMenuNodeFormWidget() {
     $link = MenuLinkContent::load($link_id);
     $link->set('enabled', FALSE);
     $link->save();
-    $this->drupalPostForm($node->urlInfo('edit-form'), $edit, t('Save'));
+    $this->drupalPostForm($node->toUrl('edit-form'), $edit, t('Save'));
     $link = MenuLinkContent::load($link_id);
     $this->assertFalse($link->isEnabled(), 'Saving a node with a disabled menu link keeps the menu link disabled.');
 
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index bd1b6232ad6ed68907ed3cc227db530dd57dc2c7..4d1267e2ddacc31a553bf1aa5a96397f29f05ab0 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -134,7 +134,7 @@ function _node_mass_update_batch_process(array $nodes, array $updates, $langcode
     $node = _node_mass_update_helper($node, $updates, $langcode);
 
     // Store result for post-processing in the finished callback.
-    $context['results'][] = \Drupal::l($node->label(), $node->urlInfo());
+    $context['results'][] = \Drupal::l($node->label(), $node->toUrl());
 
     // Update our progress information.
     $context['sandbox']['progress']++;
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 5c6c88633cea6b8ee39cbcec512d15ab86e83a79..c91d2f0e2e16d1bf1c56f05918761656ff6e7e9a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -221,7 +221,7 @@ function node_title_list(StatementInterface $result, $title = NULL) {
   $num_rows = FALSE;
   $nids = [];
   foreach ($result as $row) {
-    // Do not use $node->label() or $node->urlInfo() here, because we only have
+    // Do not use $node->label() or $node->toUrl() here, because we only have
     // database rows, not actual nodes.
     $nids[] = $row->nid;
     $options = !empty($row->comment_count) ? ['attributes' => ['title' => \Drupal::translation()->formatPlural($row->comment_count, '1 comment', '@count comments')]] : [];
@@ -614,9 +614,7 @@ function template_preprocess_node(&$variables) {
   $variables['author_name'] = \Drupal::service('renderer')->render($variables['elements']['uid']);
   unset($variables['elements']['uid']);
 
-  $variables['url'] = $node->url('canonical', [
-    'language' => $node->language(),
-  ]);
+  $variables['url'] = !$node->isNew() ? $node->toUrl('canonical')->toString() : NULL;
   $variables['label'] = $variables['elements']['title'];
   unset($variables['elements']['title']);
   // The 'page' variable is set to TRUE in two occasions:
diff --git a/core/modules/node/node.tokens.inc b/core/modules/node/node.tokens.inc
index 2fcf0ddfa8f5f4bbce803dcf2f67a6600add24b0..6133700387a829848f88230e75d7aa3610285263 100644
--- a/core/modules/node/node.tokens.inc
+++ b/core/modules/node/node.tokens.inc
@@ -166,11 +166,11 @@ function node_tokens($type, $tokens, array $data, array $options, BubbleableMeta
           break;
 
         case 'url':
-          $replacements[$original] = $node->url('canonical', $url_options);
+          $replacements[$original] = $node->toUrl('canonical', $url_options)->toString();
           break;
 
         case 'edit-url':
-          $replacements[$original] = $node->url('edit-form', $url_options);
+          $replacements[$original] = $node->toUrl('edit-form', $url_options)->toString();
           break;
 
         // Default values for the chained tokens handled below.
diff --git a/core/modules/node/src/Controller/NodeController.php b/core/modules/node/src/Controller/NodeController.php
index 704bf51f88f5c7505c0f5fc47c896b50acc2370b..83e7d1f021ddc7261b4db36a6fd408d9475a6a2c 100644
--- a/core/modules/node/src/Controller/NodeController.php
+++ b/core/modules/node/src/Controller/NodeController.php
@@ -198,7 +198,7 @@ public function revisionOverview(NodeInterface $node) {
           $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
         }
         else {
-          $link = $node->link($date);
+          $link = $node->toLink($date)->toString();
           $current_revision_displayed = TRUE;
         }
 
diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php
index 75ed61d233a8b300945d9ee0984422d5761dba63..fefa24ace74b51cc91cabdd6ccacc7b350fe6a98 100644
--- a/core/modules/node/src/Form/NodePreviewForm.php
+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -83,7 +83,7 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
     $form['backlink'] = [
       '#type' => 'link',
       '#title' => $this->t('Back to content editing'),
-      '#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->urlInfo('edit-form'),
+      '#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->toUrl('edit-form'),
       '#options' => ['attributes' => ['class' => ['node-preview-backlink']]] + $query_options,
     ];
 
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index 3fe71d3b2073f7c8a9ce12e42ac6576476128c77..107f2a01b444b1f70f3ca30f625ae414c5dda525 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -279,9 +279,9 @@ public function save(array $form, FormStateInterface $form_state) {
     $node = $this->entity;
     $insert = $node->isNew();
     $node->save();
-    $node_link = $node->link($this->t('View'));
+    $node_link = $node->toLink($this->t('View'))->toString();
     $context = ['@type' => $node->getType(), '%title' => $node->label(), 'link' => $node_link];
-    $t_args = ['@type' => node_get_type_label($node), '%title' => $node->link($node->label())];
+    $t_args = ['@type' => node_get_type_label($node), '%title' => $node->toLink()->toString()];
 
     if ($insert) {
       $this->logger('content')->notice('@type: added %title.', $context);
diff --git a/core/modules/node/src/NodeListBuilder.php b/core/modules/node/src/NodeListBuilder.php
index eac48fda31aa17cad0b3aa7dea8ef86032580b0b..4cab89106630efe3254aae9da6462db155976fa6 100644
--- a/core/modules/node/src/NodeListBuilder.php
+++ b/core/modules/node/src/NodeListBuilder.php
@@ -96,7 +96,7 @@ public function buildRow(EntityInterface $entity) {
       '#mark_type' => node_mark($entity->id(), $entity->getChangedTime()),
     ];
     $langcode = $entity->language()->getId();
-    $uri = $entity->urlInfo();
+    $uri = $entity->toUrl();
     $options = $uri->getOptions();
     $options += ($langcode != LanguageInterface::LANGCODE_NOT_SPECIFIED && isset($languages[$langcode]) ? ['language' => $languages[$langcode]] : []);
     $uri->setOptions($options);
diff --git a/core/modules/node/src/NodeTypeForm.php b/core/modules/node/src/NodeTypeForm.php
index 23fbe34652cef66270335bf64338b42006e99211..f28056053fa6aa9bced4d763e12591742042fca8 100644
--- a/core/modules/node/src/NodeTypeForm.php
+++ b/core/modules/node/src/NodeTypeForm.php
@@ -230,7 +230,7 @@ public function save(array $form, FormStateInterface $form_state) {
     elseif ($status == SAVED_NEW) {
       node_add_body_field($type);
       $this->messenger()->addStatus($this->t('The content type %name has been added.', $t_args));
-      $context = array_merge($t_args, ['link' => $type->link($this->t('View'), 'collection')]);
+      $context = array_merge($t_args, ['link' => $type->toLink($this->t('View'), 'collection')->toString()]);
       $this->logger('node')->notice('Added content type %name.', $context);
     }
 
@@ -253,7 +253,7 @@ public function save(array $form, FormStateInterface $form_state) {
     }
 
     $this->entityManager->clearCachedFieldDefinitions();
-    $form_state->setRedirectUrl($type->urlInfo('collection'));
+    $form_state->setRedirectUrl($type->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/node/src/NodeViewBuilder.php b/core/modules/node/src/NodeViewBuilder.php
index 43efecb4a4da4911bb1048e032c0161dce21266f..a3a5b7b1604c6ddece7bdaa95c6bf7ddf8366555 100644
--- a/core/modules/node/src/NodeViewBuilder.php
+++ b/core/modules/node/src/NodeViewBuilder.php
@@ -130,7 +130,7 @@ protected static function buildLinks(NodeInterface $entity, $view_mode) {
         'title' => t('Read more<span class="visually-hidden"> about @title</span>', [
           '@title' => $node_title_stripped,
         ]),
-        'url' => $entity->urlInfo(),
+        'url' => $entity->toUrl(),
         'language' => $entity->language(),
         'attributes' => [
           'rel' => 'tag',
diff --git a/core/modules/node/src/Plugin/Search/NodeSearch.php b/core/modules/node/src/Plugin/Search/NodeSearch.php
index dd3f441a1a7103d8379153c3c44d5bd3f4c8b87e..e4d5a0e7fc8e07b5688ade4792cc707ca9a49c7a 100644
--- a/core/modules/node/src/Plugin/Search/NodeSearch.php
+++ b/core/modules/node/src/Plugin/Search/NodeSearch.php
@@ -362,14 +362,13 @@ protected function prepareResults(StatementInterface $found) {
 
       $extra = $this->moduleHandler->invokeAll('node_search_result', [$node]);
 
-      $language = $this->languageManager->getLanguage($item->langcode);
       $username = [
         '#theme' => 'username',
         '#account' => $node->getOwner(),
       ];
 
       $result = [
-        'link' => $node->url('canonical', ['absolute' => TRUE, 'language' => $language]),
+        'link' => $node->toUrl('canonical', ['absolute' => TRUE])->toString(),
         'type' => $type->label(),
         'title' => $node->label(),
         'node' => $node,
diff --git a/core/modules/node/src/Plugin/views/field/RevisionLink.php b/core/modules/node/src/Plugin/views/field/RevisionLink.php
index 29d0176634af744b473ae1b30c3a39e9abca69af..949ad6e07cc49b6a373d987ed37eacf0c4e9ff5f 100644
--- a/core/modules/node/src/Plugin/views/field/RevisionLink.php
+++ b/core/modules/node/src/Plugin/views/field/RevisionLink.php
@@ -24,7 +24,7 @@ protected function getUrlInfo(ResultRow $row) {
     // Current revision uses the node view path.
     return !$node->isDefaultRevision() ?
       Url::fromRoute('entity.node.revision', ['node' => $node->id(), 'node_revision' => $node->getRevisionId()]) :
-      $node->urlInfo();
+      $node->toUrl();
   }
 
   /**
diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php
index 96838796a16b1250167084ebf86209724dd3820f..59a1df012bd66b515d1e2dea59d7c5e3a449397d 100644
--- a/core/modules/node/src/Plugin/views/row/Rss.php
+++ b/core/modules/node/src/Plugin/views/row/Rss.php
@@ -103,7 +103,7 @@ public function render($row) {
       return;
     }
 
-    $node->link = $node->url('canonical', ['absolute' => TRUE]);
+    $node->link = $node->toUrl('canonical', ['absolute' => TRUE])->toString();
     $node->rss_namespaces = [];
     $node->rss_elements = [
       [
diff --git a/core/modules/node/tests/src/Functional/NodeBlockFunctionalTest.php b/core/modules/node/tests/src/Functional/NodeBlockFunctionalTest.php
index fcd3b1f666a48fd00d3d1c2d82442508294133b8..5a8584e286238cacc5895e22d381b144a33ddabc 100644
--- a/core/modules/node/tests/src/Functional/NodeBlockFunctionalTest.php
+++ b/core/modules/node/tests/src/Functional/NodeBlockFunctionalTest.php
@@ -173,7 +173,7 @@ public function testRecentNodeBlock() {
     $this->drupalLogin($this->adminUser);
     $this->drupalGet('admin/structure/block');
     $this->assertText($label, 'Block was displayed on the admin/structure/block page.');
-    $this->assertLinkByHref($block->url());
+    $this->assertLinkByHref($block->toUrl()->toString());
   }
 
 }
diff --git a/core/modules/node/tests/src/Functional/NodeEditFormTest.php b/core/modules/node/tests/src/Functional/NodeEditFormTest.php
index 9a45452ee917da8ca5d7c955c75262619741ac4c..9c9df5b0ca8114a680daa3766cfefdb336b9c44a 100644
--- a/core/modules/node/tests/src/Functional/NodeEditFormTest.php
+++ b/core/modules/node/tests/src/Functional/NodeEditFormTest.php
@@ -70,7 +70,7 @@ public function testNodeEdit() {
 
     // Check that "edit" link points to correct page.
     $this->clickLink(t('Edit'));
-    $this->assertUrl($node->url('edit-form', ['absolute' => TRUE]));
+    $this->assertUrl($node->toUrl('edit-form', ['absolute' => TRUE])->toString());
 
     // Check that the title and body fields are displayed with the correct values.
     // @todo Ideally assertLink would support HTML, but it doesn't.
diff --git a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
index 3646ab3e010779a6080ad4e5f7a397bd406b563b..0648e5b600104180f1c3c164637d5bc38d63e2b8 100644
--- a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
+++ b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
@@ -118,7 +118,7 @@ public function testNodeRevisionDoubleEscapeFix() {
 
     // Assert the current revision message.
     $date = format_date($nodes[1]->revision_timestamp->value, 'short');
-    $this->assertRaw($nodes[1]->link($date) . ' by ' . $editor . '<p class="revision-log">' . $revision_log . '</p>');
+    $this->assertRaw($nodes[1]->toLink($date)->toString() . ' by ' . $editor . '<p class="revision-log">' . $revision_log . '</p>');
   }
 
   /**
diff --git a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
index b32a4b0a031838023a0017c4677db6bc9df7343d..2fbee14c3a7e44cb2544352db0c2e9813dd2ea2d 100644
--- a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
@@ -161,7 +161,7 @@ protected function doTestPublishedStatus() {
       // statuses are (un)published accordingly.
       foreach ($this->langcodes as $langcode) {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalPostForm($url, ['status[value]' => $value], t('Save') . $this->getFormSubmitSuffix($entity, $langcode), $options);
       }
       $storage->resetCache([$this->entityId]);
@@ -204,7 +204,7 @@ protected function doTestAuthoringInfo() {
         'promote[value]' => $values[$langcode]['promote'],
       ];
       $options = ['language' => $languages[$langcode]];
-      $url = $entity->urlInfo('edit-form', $options);
+      $url = $entity->toUrl('edit-form', $options);
       $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
     }
 
@@ -353,7 +353,7 @@ public function testTranslationRendering() {
     $this->doTestTranslations('node/' . $node->id(), $values);
 
     // Test that the node page has the correct alternate hreflang links.
-    $this->doTestAlternateHreflangLinks($node->urlInfo());
+    $this->doTestAlternateHreflangLinks($node->toUrl());
   }
 
   /**
@@ -440,7 +440,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('<em>Edit @type</em> @title [%language translation]', [
diff --git a/core/modules/node/tests/src/Functional/NodeViewLanguageTest.php b/core/modules/node/tests/src/Functional/NodeViewLanguageTest.php
index 473042756012b0efc3b612a8ce493b3b1cb5af7d..0ebedfb8fbc79c32fc1e341b42c6cd0811bc8a00 100644
--- a/core/modules/node/tests/src/Functional/NodeViewLanguageTest.php
+++ b/core/modules/node/tests/src/Functional/NodeViewLanguageTest.php
@@ -33,7 +33,7 @@ public function testViewLanguage() {
     // Create a node in Spanish.
     $node = $this->drupalCreateNode(['langcode' => 'es']);
 
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $this->assertText('Spanish', 'The language field is displayed properly.');
   }
 
diff --git a/core/modules/node/tests/src/Functional/NodeViewTest.php b/core/modules/node/tests/src/Functional/NodeViewTest.php
index 36a26fc370f04cf7ffb5fc0cdc6a61dfba5ab7dc..4e99a6c0ef05163b31c2a0612ee8554bea3a3e4f 100644
--- a/core/modules/node/tests/src/Functional/NodeViewTest.php
+++ b/core/modules/node/tests/src/Functional/NodeViewTest.php
@@ -18,10 +18,10 @@ class NodeViewTest extends NodeTestBase {
   public function testHtmlHeadLinks() {
     $node = $this->drupalCreateNode();
 
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
 
     $result = $this->xpath('//link[@rel = "canonical"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url());
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl()->toString());
 
     // Link relations are checked for access for anonymous users.
     $result = $this->xpath('//link[@rel = "version-history"]');
@@ -31,17 +31,17 @@ public function testHtmlHeadLinks() {
     $this->assertFalse($result, 'Edit form not present for anonymous users without access.');
 
     $this->drupalLogin($this->createUser(['access content']));
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
 
     $result = $this->xpath('//link[@rel = "canonical"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url());
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl()->toString());
 
     // Link relations are present regardless of access for authenticated users.
     $result = $this->xpath('//link[@rel = "version-history"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url('version-history'));
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl('version-history')->toString());
 
     $result = $this->xpath('//link[@rel = "edit-form"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl('edit-form')->toString());
 
     // Give anonymous users access to edit the node. Do this through the UI to
     // ensure caches are handled properly.
@@ -54,15 +54,15 @@ public function testHtmlHeadLinks() {
 
     // Anonymous user's should now see the edit-form link but not the
     // version-history link.
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $result = $this->xpath('//link[@rel = "canonical"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url());
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl()->toString());
 
     $result = $this->xpath('//link[@rel = "version-history"]');
     $this->assertFalse($result, 'Version history not present for anonymous users without access.');
 
     $result = $this->xpath('//link[@rel = "edit-form"]');
-    $this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
+    $this->assertEqual($result[0]->getAttribute('href'), $node->toUrl('edit-form')->toString());
   }
 
   /**
@@ -72,12 +72,12 @@ public function testLinkHeader() {
     $node = $this->drupalCreateNode();
 
     $expected = [
-      '<' . Html::escape($node->url('canonical')) . '>; rel="canonical"',
-      '<' . Html::escape($node->url('canonical'), ['alias' => TRUE]) . '>; rel="shortlink"',
-      '<' . Html::escape($node->url('revision')) . '>; rel="revision"',
+      '<' . Html::escape($node->toUrl('canonical')->toString()) . '>; rel="canonical"',
+      '<' . Html::escape($node->toUrl('canonical')->toString(), ['alias' => TRUE]) . '>; rel="shortlink"',
+      '<' . Html::escape($node->toUrl('revision')->toString()) . '>; rel="revision"',
     ];
 
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
 
     $links = $this->drupalGetHeaders()['Link'];
     $this->assertEqual($links, $expected);
@@ -90,7 +90,7 @@ public function testMultiByteUtf8() {
     $title = '🐝';
     $this->assertTrue(mb_strlen($title, 'utf-8') < strlen($title), 'Title has multi-byte characters.');
     $node = $this->drupalCreateNode(['title' => $title]);
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $result = $this->xpath('//span[contains(@class, "field--name-title")]');
     $this->assertEqual($result[0]->getText(), $title, 'The passed title was returned.');
   }
diff --git a/core/modules/node/tests/src/Functional/Views/PathPluginTest.php b/core/modules/node/tests/src/Functional/Views/PathPluginTest.php
index d8d088d978ac2f6dfcde682866cfca9259915bd0..b1da9fa4b56c2514669cc3352832da868945e715 100644
--- a/core/modules/node/tests/src/Functional/Views/PathPluginTest.php
+++ b/core/modules/node/tests/src/Functional/Views/PathPluginTest.php
@@ -79,7 +79,7 @@ public function testPathPlugin() {
     $output = $view->preview();
     $output = $renderer->renderRoot($output);
     foreach ($this->nodes as $node) {
-      $this->assertTrue(strpos($output, 'This is <strong>not escaped</strong> and this is ' . $node->link('the link')) !== FALSE, 'Make sure path field rewriting is not escaped.');
+      $this->assertTrue(strpos($output, 'This is <strong>not escaped</strong> and this is ' . $node->toLink('the link')->toString()) !== FALSE, 'Make sure path field rewriting is not escaped.');
     }
   }
 
diff --git a/core/modules/node/tests/src/Functional/Views/RevisionLinkTest.php b/core/modules/node/tests/src/Functional/Views/RevisionLinkTest.php
index 6dc20d64eccf3fb390c7935c262eeadf729f7bc8..b5a4829b09fdc163835079fc60b18389a7da02d6 100644
--- a/core/modules/node/tests/src/Functional/Views/RevisionLinkTest.php
+++ b/core/modules/node/tests/src/Functional/Views/RevisionLinkTest.php
@@ -46,7 +46,7 @@ public function testRevisionLinks() {
     $this->assertResponse(200, 'Test view can be accessed in the path expected');
     // The first node revision should link to the node directly as you get an
     // access denied if you link to the revision.
-    $url = $nodes[0]->urlInfo()->toString();
+    $url = $nodes[0]->toUrl()->toString();
     $this->assertLinkByHref($url);
     $this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/view');
     $this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/delete');
@@ -54,7 +54,7 @@ public function testRevisionLinks() {
 
     // For the second node the current revision got set to the last revision, so
     // the first one should also link to the node page itself.
-    $url = $nodes[1]->urlInfo()->toString();
+    $url = $nodes[1]->toUrl()->toString();
     $this->assertLinkByHref($url);
     $this->assertLinkByHref($url . '/revisions/' . $first_revision . '/view');
     $this->assertLinkByHref($url . '/revisions/' . $first_revision . '/delete');
@@ -69,7 +69,7 @@ public function testRevisionLinks() {
       'delete' => $this->drupalCreateUser(['delete all revisions', 'delete any page content']),
     ];
 
-    $url = $nodes[1]->urlInfo()->toString();
+    $url = $nodes[1]->toUrl()->toString();
     // Render the view with users which can only delete/revert revisions.
     foreach ($accounts as $allowed_operation => $account) {
       $this->drupalLogin($account);
diff --git a/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php b/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
index 969287a40b6c347761d1fc5c8eb159df7e615abd..d4c51bc442e9054381b55642916d438411ddb0e2 100644
--- a/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
+++ b/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
@@ -67,8 +67,8 @@ public function testNodeTokenReplacement() {
     $tests['[node:body]'] = $node->body->processed;
     $tests['[node:summary]'] = $node->body->summary_processed;
     $tests['[node:langcode]'] = $node->language()->getId();
-    $tests['[node:url]'] = $node->url('canonical', $url_options);
-    $tests['[node:edit-url]'] = $node->url('edit-form', $url_options);
+    $tests['[node:url]'] = $node->toUrl('canonical', $url_options)->toString();
+    $tests['[node:edit-url]'] = $node->toUrl('edit-form', $url_options)->toString();
     $tests['[node:author]'] = $account->getAccountName();
     $tests['[node:author:uid]'] = $node->getOwnerId();
     $tests['[node:author:name]'] = $account->getAccountName();
diff --git a/core/modules/options/tests/options_test/options_test.module b/core/modules/options/tests/options_test/options_test.module
index dbb474b493731df84400088d1e5f495e91c9e967..b38d647404dc1c1212bed22e46645a8406e45602 100644
--- a/core/modules/options/tests/options_test/options_test.module
+++ b/core/modules/options/tests/options_test/options_test.module
@@ -46,7 +46,7 @@ function options_test_dynamic_values_callback(FieldStorageDefinitionInterface $d
     $cacheable = FALSE;
     $values = [
       $entity->label(),
-      $entity->url(),
+      $entity->toUrl()->toString(),
       $entity->uuid(),
       $entity->bundle(),
     ];
diff --git a/core/modules/options/tests/src/Functional/OptionsDynamicValuesApiTest.php b/core/modules/options/tests/src/Functional/OptionsDynamicValuesApiTest.php
index a96fd7a04b172e618a77b214ac569177cd55adb6..4f047f97f3b0e6d9b75260db33effb52a51bb0e6 100644
--- a/core/modules/options/tests/src/Functional/OptionsDynamicValuesApiTest.php
+++ b/core/modules/options/tests/src/Functional/OptionsDynamicValuesApiTest.php
@@ -23,7 +23,7 @@ public function testOptionsAllowedValues() {
 
     $expected_values = [
       $this->entity->label(),
-      $this->entity->url(),
+      $this->entity->toUrl()->toString(),
       $this->entity->uuid(),
       $this->entity->bundle(),
     ];
diff --git a/core/modules/options/tests/src/Functional/OptionsDynamicValuesTestBase.php b/core/modules/options/tests/src/Functional/OptionsDynamicValuesTestBase.php
index 16c29907c885ede74b0b478805805dcb603dbb56..1b39cf6da7065dc6d1cfbc04859d0eda3f40e0c4 100644
--- a/core/modules/options/tests/src/Functional/OptionsDynamicValuesTestBase.php
+++ b/core/modules/options/tests/src/Functional/OptionsDynamicValuesTestBase.php
@@ -72,7 +72,7 @@ protected function setUp() {
       'label' => $this->entity->label(),
       'uuid' => $this->entity->uuid(),
       'bundle' => $this->entity->bundle(),
-      'uri' => $this->entity->url(),
+      'uri' => $this->entity->toUrl()->toString(),
     ];
   }
 
diff --git a/core/modules/page_cache/tests/src/Functional/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/tests/src/Functional/PageCacheTagsIntegrationTest.php
index 952ad623f6786d9c8fb10fd8fef55a5de026af96..4059aa72afc2cbb1425c8aa7f690fb98565906bb 100644
--- a/core/modules/page_cache/tests/src/Functional/PageCacheTagsIntegrationTest.php
+++ b/core/modules/page_cache/tests/src/Functional/PageCacheTagsIntegrationTest.php
@@ -82,7 +82,7 @@ public function testPageCacheTags() {
     ];
 
     // Full node page 1.
-    $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, [
+    $this->assertPageCacheContextsAndTags($node_1->toUrl(), $cache_contexts, [
       'http_response',
       'rendered',
       'block_view',
@@ -124,7 +124,7 @@ public function testPageCacheTags() {
     $cache_contexts[] = 'languages:' . LanguageInterface::TYPE_CONTENT;
 
     // Full node page 2.
-    $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, [
+    $this->assertPageCacheContextsAndTags($node_2->toUrl(), $cache_contexts, [
       'http_response',
       'rendered',
       'block_view',
diff --git a/core/modules/page_cache/tests/src/Functional/PageCacheTest.php b/core/modules/page_cache/tests/src/Functional/PageCacheTest.php
index 0f8f14e2d01c0edb18d79e4ef1da606906b7e623..839ce4d465ef6761296e3d9d707e3ca8dc605268 100644
--- a/core/modules/page_cache/tests/src/Functional/PageCacheTest.php
+++ b/core/modules/page_cache/tests/src/Functional/PageCacheTest.php
@@ -141,8 +141,8 @@ public function testQueryParameterFormatRequests() {
     \Drupal::service('module_installer')->install(['node', 'rest', 'hal', 'basic_auth']);
     $this->drupalCreateContentType(['type' => 'article']);
     $node = $this->drupalCreateNode(['type' => 'article']);
-    $node_uri = $node->urlInfo();
-    $node_url_with_hal_json_format = $node->urlInfo('canonical')->setRouteParameter('_format', 'hal_json');
+    $node_uri = $node->toUrl();
+    $node_url_with_hal_json_format = $node->toUrl('canonical')->setRouteParameter('_format', 'hal_json');
 
     $this->drupalGet($node_uri);
     $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
index fc263ce60afcdd573b6df721448c5507a619b776..0fb913b9a3954886a1f2be526ba4892b88308567 100644
--- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
+++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
@@ -71,7 +71,7 @@ public function postSave($update) {
     if (!$update) {
       if ($this->alias) {
         $entity = $this->getEntity();
-        if ($path = \Drupal::service('path.alias_storage')->save('/' . $entity->urlInfo()->getInternalPath(), $this->alias, $alias_langcode)) {
+        if ($path = \Drupal::service('path.alias_storage')->save('/' . $entity->toUrl()->getInternalPath(), $this->alias, $alias_langcode)) {
           $this->pid = $path['pid'];
         }
       }
@@ -84,7 +84,7 @@ public function postSave($update) {
       // Only save a non-empty alias.
       elseif ($this->alias) {
         $entity = $this->getEntity();
-        \Drupal::service('path.alias_storage')->save('/' . $entity->urlInfo()->getInternalPath(), $this->alias, $alias_langcode, $this->pid);
+        \Drupal::service('path.alias_storage')->save('/' . $entity->toUrl()->getInternalPath(), $this->alias, $alias_langcode, $this->pid);
       }
     }
   }
diff --git a/core/modules/path/tests/src/Functional/PathLanguageTest.php b/core/modules/path/tests/src/Functional/PathLanguageTest.php
index 8e41edeaeefc839dcc64012986d5886f1941a735..eadd88f34512573148d89ef2e7703ae7337c38f6 100644
--- a/core/modules/path/tests/src/Functional/PathLanguageTest.php
+++ b/core/modules/path/tests/src/Functional/PathLanguageTest.php
@@ -118,7 +118,7 @@ public function testAliasTranslation() {
     // many levels, and we need to clear those caches.
     $this->container->get('language_manager')->reset();
     $languages = $this->container->get('language_manager')->getLanguages();
-    $url = $english_node_french_translation->url('canonical', ['language' => $languages['fr']]);
+    $url = $english_node_french_translation->toUrl('canonical', ['language' => $languages['fr']])->toString();
 
     $this->assertTrue(strpos($url, $edit['path[0][alias]']), 'URL contains the path alias.');
 
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index dbffb20c017b728025c1dfa5a775408b901eae4d..8c70786eac9ed014aea605711ba139655a22be30 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -241,7 +241,7 @@ function rdf_comment_storage_load($comments) {
     // bubbleable metadata, because it can be outside of a render context.
     $comment->rdf_data['entity_uri'] = $entity->toUrl()->toString(TRUE)->getGeneratedUrl();
     if ($comment->hasParentComment()) {
-      $comment->rdf_data['pid_uri'] = $comment->getParentComment()->url();
+      $comment->rdf_data['pid_uri'] = $comment->getParentComment()->toUrl()->toString();
     }
   }
 }
@@ -360,7 +360,7 @@ function rdf_preprocess_node(&$variables) {
 function rdf_preprocess_user(&$variables) {
   /** @var $account \Drupal\user\UserInterface */
   $account = $variables['elements']['#user'];
-  $uri = $account->urlInfo();
+  $uri = $account->toUrl();
   $mapping = rdf_get_mapping('user', 'user');
   $bundle_mapping = $mapping->getPreparedBundleMapping();
 
@@ -368,7 +368,7 @@ function rdf_preprocess_user(&$variables) {
   // will automatically describe the user.
   if (!empty($bundle_mapping['types'])) {
     $variables['attributes']['typeof'] = $bundle_mapping['types'];
-    $variables['attributes']['about'] = $account->url();
+    $variables['attributes']['about'] = $account->toUrl()->toString();
   }
   // If we are on the user account page, add the relationship between the
   // sioc:UserAccount and the foaf:Person who holds the account.
@@ -380,7 +380,7 @@ function rdf_preprocess_user(&$variables) {
       $username_meta = [
         '#tag' => 'meta',
         '#attributes' => [
-          'about' => $account->url(),
+          'about' => $account->toUrl()->toString(),
           'property' => $name_mapping['properties'],
           'content' => $account->getDisplayName(),
           'lang' => '',
@@ -454,7 +454,7 @@ function rdf_preprocess_comment(&$variables) {
     // the URI of the resource described within the HTML element, while the
     // typeof attribute indicates its RDF type (e.g., sioc:Post, foaf:Document,
     // and so on.)
-    $variables['attributes']['about'] = $comment->url();
+    $variables['attributes']['about'] = $comment->toUrl()->toString();
     $variables['attributes']['typeof'] = $bundle_mapping['types'];
   }
 
@@ -549,7 +549,7 @@ function rdf_preprocess_taxonomy_term(&$variables) {
   $term = $variables['term'];
   $mapping = rdf_get_mapping('taxonomy_term', $term->bundle());
   $bundle_mapping = $mapping->getPreparedBundleMapping();
-  $variables['attributes']['about'] = $term->url();
+  $variables['attributes']['about'] = $term->toUrl()->toString();
   $variables['attributes']['typeof'] = empty($bundle_mapping['types']) ? NULL : $bundle_mapping['types'];
 
   // Add RDFa markup for the taxonomy term name as metadata, if present.
diff --git a/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php b/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php
index 1caf6cb48ac9d8ce3376addfc3bd20ade830fa89..3266eb6b7f70bc7cf49055bb46088ce32e439a0c 100644
--- a/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php
@@ -54,7 +54,7 @@ protected function setUp() {
 
     // Prepares commonly used URIs.
     $this->baseUri = \Drupal::url('<front>', [], ['absolute' => TRUE]);
-    $this->nodeUri = $this->node->url('canonical', ['absolute' => TRUE]);
+    $this->nodeUri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Set relation between node and comment.
     $article_mapping = rdf_get_mapping('node', 'article');
@@ -211,11 +211,11 @@ public function testCommentReplyOfRdfaMarkup() {
     $this->drupalLogin($this->webUser);
     $comment_1 = $this->saveComment($this->node->id(), $this->webUser->id());
 
-    $comment_1_uri = $comment_1->url('canonical', ['absolute' => TRUE]);
+    $comment_1_uri = $comment_1->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Posts a reply to the first comment.
     $comment_2 = $this->saveComment($this->node->id(), $this->webUser->id(), NULL, $comment_1->id());
-    $comment_2_uri = $comment_2->url('canonical', ['absolute' => TRUE]);
+    $comment_2_uri = $comment_2->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     $parser = new \EasyRdf_Parser_Rdfa();
     $graph = new \EasyRdf_Graph();
@@ -252,7 +252,7 @@ public function testCommentReplyOfRdfaMarkup() {
    *   An array containing information about an anonymous user.
    */
   public function _testBasicCommentRdfaMarkup($graph, CommentInterface $comment, $account = []) {
-    $comment_uri = $comment->url('canonical', ['absolute' => TRUE]);
+    $comment_uri = $comment->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Comment type.
     $expected_value = [
diff --git a/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php
index b91215db5b3d611ede4a02992495aa73877018fc..9ef3252fe64e23d74ce28aa0722c5517022f4dbd 100644
--- a/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php
@@ -86,8 +86,8 @@ public function testNodeTeaser() {
     // Create a term in each vocabulary.
     $term1 = $this->createTerm($this->vocabulary);
     $term2 = $this->createTerm($this->vocabulary);
-    $taxonomy_term_1_uri = $term1->url('canonical', ['absolute' => TRUE]);
-    $taxonomy_term_2_uri = $term2->url('canonical', ['absolute' => TRUE]);
+    $taxonomy_term_1_uri = $term1->toUrl('canonical', ['absolute' => TRUE])->toString();
+    $taxonomy_term_2_uri = $term2->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Create the node.
     $node = $this->drupalCreateNode(['type' => 'article']);
@@ -107,7 +107,7 @@ public function testNodeTeaser() {
     $parser->parse($graph, $html, 'rdfa', $base_uri);
 
     // Node relations to taxonomy terms.
-    $node_uri = $node->url('canonical', ['absolute' => TRUE]);
+    $node_uri = $node->toUrl('canonical', ['absolute' => TRUE])->toString();
     $expected_value = [
       'type' => 'uri',
       'value' => $taxonomy_term_1_uri,
diff --git a/core/modules/rdf/tests/src/Functional/FileFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/FileFieldAttributesTest.php
index bf567c2615adffd4f38c4757e7c5915ba3dc13b3..39931cd0d100079805349e2cbb551fa37ea1ee75 100644
--- a/core/modules/rdf/tests/src/Functional/FileFieldAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/FileFieldAttributesTest.php
@@ -84,7 +84,7 @@ public function testNodeTeaser() {
     $base_uri = \Drupal::url('<front>', [], ['absolute' => TRUE]);
     $parser->parse($graph, $html, 'rdfa', $base_uri);
 
-    $node_uri = $this->node->url('canonical', ['absolute' => TRUE]);
+    $node_uri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString();
     $file_uri = file_create_url($this->file->getFileUri());
 
     // Node relation to attached file.
diff --git a/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php
index e5707591f70d2a4a3d5fc38f8ce02513b88074fa..bd62f1b009fa5762005ba9302eb7900b0e5e8a28 100644
--- a/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php
@@ -97,7 +97,7 @@ public function testNodeTeaser() {
     $parser->parse($graph, $html, 'rdfa', $base_uri);
 
     // Construct the node and image URIs for testing.
-    $node_uri = $this->node->url('canonical', ['absolute' => TRUE]);
+    $node_uri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString();
     $image_uri = ImageStyle::load('medium')->buildUrl($this->file->getFileUri());
 
     // Test relations from node to image.
diff --git a/core/modules/rdf/tests/src/Functional/NodeAttributesTest.php b/core/modules/rdf/tests/src/Functional/NodeAttributesTest.php
index 2e5675fe6a87460639acb6e540625cd850633f11..ffae74b260d4447872992de60c187a7057f2897b 100644
--- a/core/modules/rdf/tests/src/Functional/NodeAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/NodeAttributesTest.php
@@ -47,7 +47,7 @@ public function testNodeAttributes() {
       'title' => $this->randomMachineName(8) . "'",
     ]);
 
-    $node_uri = $node->url('canonical', ['absolute' => TRUE]);
+    $node_uri = $node->toUrl('canonical', ['absolute' => TRUE])->toString();
     $base_uri = \Drupal::url('<front>', [], ['absolute' => TRUE]);
 
     // Parses front page where the node is displayed in its teaser form.
diff --git a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php
index 84372e05820218749914670523d5e41e00e51309..083e637e9656e0c22e37321af157902eb3fdb35e 100644
--- a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php
+++ b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php
@@ -170,17 +170,17 @@ protected function setUp() {
     $image_file = $this->article->get('field_image')->entity;
     $this->imageUri = ImageStyle::load('large')->buildUrl($image_file->getFileUri());
     // Term.
-    $this->termUri = $this->term->url('canonical', ['absolute' => TRUE]);
+    $this->termUri = $this->term->toUrl('canonical', ['absolute' => TRUE])->toString();
     // Article.
-    $this->articleUri = $this->article->url('canonical', ['absolute' => TRUE]);
+    $this->articleUri = $this->article->toUrl('canonical', ['absolute' => TRUE])->toString();
     // Page.
-    $this->pageUri = $this->page->url('canonical', ['absolute' => TRUE]);
+    $this->pageUri = $this->page->toUrl('canonical', ['absolute' => TRUE])->toString();
     // Author.
-    $this->authorUri = $this->adminUser->url('canonical', ['absolute' => TRUE]);
+    $this->authorUri = $this->adminUser->toUrl('canonical', ['absolute' => TRUE])->toString();
     // Comment.
-    $this->articleCommentUri = $this->articleComment->url('canonical', ['absolute' => TRUE]);
+    $this->articleCommentUri = $this->articleComment->toUrl('canonical', ['absolute' => TRUE])->toString();
     // Commenter.
-    $this->commenterUri = $this->webUser->url('canonical', ['absolute' => TRUE]);
+    $this->commenterUri = $this->webUser->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     $this->drupalLogout();
   }
@@ -244,7 +244,7 @@ protected function doFrontPageRdfaTests() {
    */
   protected function doArticleRdfaTests() {
     // Feed the HTML into the parser.
-    $graph = $this->getRdfGraph($this->article->urlInfo());
+    $graph = $this->getRdfGraph($this->article->toUrl());
 
     // Type.
     $this->assertEqual($graph->type($this->articleUri), 'schema:Article', 'Article type was found (schema:Article).');
@@ -281,7 +281,7 @@ protected function doPageRdfaTests() {
     $node_type->save();
 
     // Feed the HTML into the parser.
-    $graph = $this->getRdfGraph($this->page->urlInfo());
+    $graph = $this->getRdfGraph($this->page->toUrl());
 
     // Type.
     $this->assertEqual($graph->type($this->pageUri), 'schema:WebPage', 'Page type was found (schema:WebPage).');
@@ -297,7 +297,7 @@ protected function doUserRdfaTests() {
     $this->drupalLogin($this->rootUser);
 
     // Feed the HTML into the parser.
-    $graph = $this->getRdfGraph($this->adminUser->urlInfo());
+    $graph = $this->getRdfGraph($this->adminUser->toUrl());
 
     // User type.
     $this->assertEqual($graph->type($this->authorUri), 'schema:Person', "User type was found (schema:Person) on user page.");
@@ -317,7 +317,7 @@ protected function doUserRdfaTests() {
    */
   protected function doTermRdfaTests() {
     // Feed the HTML into the parser.
-    $graph = $this->getRdfGraph($this->term->urlInfo());
+    $graph = $this->getRdfGraph($this->term->toUrl());
 
     // Term type.
     $this->assertEqual($graph->type($this->termUri), 'schema:Thing', "Term type was found (schema:Thing) on term page.");
@@ -345,7 +345,7 @@ protected function doTermRdfaTests() {
    *   The word to use in the test assertion message.
    */
   protected function assertRdfaCommonNodeProperties($graph, NodeInterface $node, $message_prefix) {
-    $uri = $node->url('canonical', ['absolute' => TRUE]);
+    $uri = $node->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Title.
     $expected_value = [
diff --git a/core/modules/rdf/tests/src/Functional/TaxonomyAttributesTest.php b/core/modules/rdf/tests/src/Functional/TaxonomyAttributesTest.php
index 346ffb7eb9dca78586822d796ce4737117df870d..feb114eb51147f4a5e5d23f8c0389dd8cb98e8a0 100644
--- a/core/modules/rdf/tests/src/Functional/TaxonomyAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/TaxonomyAttributesTest.php
@@ -44,7 +44,7 @@ protected function setUp() {
    */
   public function testTaxonomyTermRdfaAttributes() {
     $term = $this->createTerm($this->vocabulary);
-    $term_uri = $term->url('canonical', ['absolute' => TRUE]);
+    $term_uri = $term->toUrl('canonical', ['absolute' => TRUE])->toString();
 
     // Parses the term's page and checks that the RDF output is correct.
     $parser = new \EasyRdf_Parser_Rdfa();
diff --git a/core/modules/rdf/tests/src/Functional/UserAttributesTest.php b/core/modules/rdf/tests/src/Functional/UserAttributesTest.php
index 1b8c0b1897e9558d8cace381a16212d982b5746f..17274b84e815a4bf3f5846edd23a5b079aeb19bf 100644
--- a/core/modules/rdf/tests/src/Functional/UserAttributesTest.php
+++ b/core/modules/rdf/tests/src/Functional/UserAttributesTest.php
@@ -54,7 +54,7 @@ public function testUserAttributesInMarkup() {
 
     /** @var \Drupal\user\UserInterface[] $authors */
     foreach ($authors as $author) {
-      $account_uri = $author->url('canonical', ['absolute' => TRUE]);
+      $account_uri = $author->toUrl('canonical', ['absolute' => TRUE])->toString();
 
       // Parses the user profile page where the default bundle mapping for user
       // should be used.
diff --git a/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php
index 82ea9efdcc025a8855054be1ddeeda18a6e3b50c..7d8e54bf7714705db5c773e9cf3480706da36638 100644
--- a/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php
+++ b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php
@@ -135,7 +135,7 @@ protected function createTestField($field_settings = []) {
    *   The absolute URI.
    */
   protected function getAbsoluteUri($entity) {
-    return $entity->url('canonical', ['absolute' => TRUE]);
+    return $entity->toUrl('canonical', ['absolute' => TRUE])->toString();
   }
 
   /**
diff --git a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
index 0c1da94622e14069bb00c7167053a944869f26bf..362d7623ddc4c935d364e914b25ea0a5c519e068 100644
--- a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
+++ b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
@@ -206,7 +206,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
     if ($this->getSetting('image_link') == 'content') {
       $entity = $items->getEntity();
       if (!$entity->isNew()) {
-        $url = $entity->urlInfo();
+        $url = $entity->toUrl();
       }
     }
     elseif ($this->getSetting('image_link') == 'file') {
diff --git a/core/modules/responsive_image/src/ResponsiveImageStyleForm.php b/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
index 3a619ba7a260b6e1c0515c868512d9b821ac46e5..86328255e7e968a633f78f043a854d49698f73df 100644
--- a/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
+++ b/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
@@ -287,7 +287,7 @@ public function save(array $form, FormStateInterface $form_state) {
       );
     }
     else {
-      $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+      $form_state->setRedirectUrl($this->entity->toUrl('collection'));
     }
   }
 
diff --git a/core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php b/core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php
index 1b4ded009d77beed576ba639c74d325af3a0db3c..e6fdcfa8db74122dcf1dd5c00dd0d6541b09a6ed 100644
--- a/core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php
+++ b/core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php
@@ -36,7 +36,7 @@ public function getDefaultOperations(EntityInterface $entity) {
     $operations['duplicate'] = [
       'title' => t('Duplicate'),
       'weight' => 15,
-      'url' => $entity->urlInfo('duplicate-form'),
+      'url' => $entity->toUrl('duplicate-form'),
     ];
     return $operations;
   }
diff --git a/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php
index ff809823d0cc99d8999d93cfedb0241a5990ce67..ac3a0f007cf6c3ecf2e03987aeb2d3b6ffd8d9ce 100644
--- a/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php
+++ b/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php
@@ -507,7 +507,7 @@ private function assertResponsiveImageFieldFormattersLink($link_type) {
 
       case 'content':
         // Make sure the link to the node is present.
-        $this->assertPattern('/<a(.*?)href="' . preg_quote($node->url(), '/') . '"(.*?)>\s*<picture/');
+        $this->assertPattern('/<a(.*?)href="' . preg_quote($node->toUrl()->toString(), '/') . '"(.*?)>\s*<picture/');
         break;
     }
   }
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index d736b4d99c02aa43a7064bc7556ccaa869d918bd..a56de490abfaa886aa17ce2594efa05592408727 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -189,7 +189,7 @@ public function post(EntityInterface $entity = NULL) {
       // metadata here.
       $headers = [];
       if (in_array('canonical', $entity->uriRelationships(), TRUE)) {
-        $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
+        $url = $entity->toUrl('canonical', ['absolute' => TRUE])->toString(TRUE);
         $headers['Location'] = $url->getGeneratedUrl();
       }
       return new ModifiedResourceResponse($entity, 201, $headers);
diff --git a/core/modules/rest/tests/src/Functional/ResourceTest.php b/core/modules/rest/tests/src/Functional/ResourceTest.php
index d04b990e60fee893b62c823e7fa7c04ac5bb45bd..5a569106910d1e0371323082a7143f741b22cb6d 100644
--- a/core/modules/rest/tests/src/Functional/ResourceTest.php
+++ b/core/modules/rest/tests/src/Functional/ResourceTest.php
@@ -72,7 +72,7 @@ public function testFormats() {
     ])->save();
 
     // Verify that accessing the resource returns 406.
-    $this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
+    $this->drupalGet($this->entity->toUrl()->setRouteParameter('_format', 'hal_json'));
     // \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
     // non-REST route a match, but a lower quality one: no format restrictions
     // means there's always a match and hence when there is no matching REST
@@ -98,7 +98,7 @@ public function testAuthentication() {
     ])->save();
 
     // Verify that accessing the resource returns 401.
-    $this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
+    $this->drupalGet($this->entity->toUrl()->setRouteParameter('_format', 'hal_json'));
     // \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
     // non-REST route a match, but a lower quality one: no format restrictions
     // means there's always a match and hence when there is no matching REST
diff --git a/core/modules/search/src/Controller/SearchController.php b/core/modules/search/src/Controller/SearchController.php
index f6449dfc25e362c904a73d88e8469a88698a7666..5a9f8252c50b97cc1719429292a7edc21c214f16 100644
--- a/core/modules/search/src/Controller/SearchController.php
+++ b/core/modules/search/src/Controller/SearchController.php
@@ -212,7 +212,7 @@ public function performOperation(SearchPageInterface $search_page, $op) {
       $this->messenger()->addStatus($this->t('The %label search page has been disabled.', ['%label' => $search_page->label()]));
     }
 
-    $url = $search_page->urlInfo('collection');
+    $url = $search_page->toUrl('collection');
     return $this->redirect($url->getRouteName(), $url->getRouteParameters(), $url->getOptions());
   }
 
diff --git a/core/modules/search/src/Form/SearchPageFormBase.php b/core/modules/search/src/Form/SearchPageFormBase.php
index 1638f90a86b079fb90631ad952f3823a309b8932..a0df9cbee01597d29b576df7dad8e0becc4e3e92 100644
--- a/core/modules/search/src/Form/SearchPageFormBase.php
+++ b/core/modules/search/src/Form/SearchPageFormBase.php
@@ -163,7 +163,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   public function save(array $form, FormStateInterface $form_state) {
     $this->entity->save();
 
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/search/tests/src/Functional/SearchConfigSettingsFormTest.php b/core/modules/search/tests/src/Functional/SearchConfigSettingsFormTest.php
index 5b7b6164c7cc74890291ef17a9e98ab83fec619a..ac058bd064bebf5c0e63d35fa17aa42409d343eb 100644
--- a/core/modules/search/tests/src/Functional/SearchConfigSettingsFormTest.php
+++ b/core/modules/search/tests/src/Functional/SearchConfigSettingsFormTest.php
@@ -47,7 +47,7 @@ protected function setUp() {
     // Link the node to itself to test that it's only indexed once. The content
     // also needs the word "pizza" so we can use it as the search keyword.
     $body_key = 'body[0][value]';
-    $edit[$body_key] = \Drupal::l($node->label(), $node->urlInfo()) . ' pizza sandwich';
+    $edit[$body_key] = \Drupal::l($node->label(), $node->toUrl()) . ' pizza sandwich';
     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
 
     $this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
diff --git a/core/modules/serialization/tests/src/Kernel/EntityResolverTest.php b/core/modules/serialization/tests/src/Kernel/EntityResolverTest.php
index 38af46e9949712efe273f1b514ab4a6be48e8a89..7b9e14b5a0522ec09e9e3faa4e2ac9ca11daa321 100644
--- a/core/modules/serialization/tests/src/Kernel/EntityResolverTest.php
+++ b/core/modules/serialization/tests/src/Kernel/EntityResolverTest.php
@@ -70,7 +70,7 @@ public function testUuidEntityResolver() {
         ],
         $field_uri => [
           [
-            'href' => $entity->url(),
+            'href' => $entity->toUrl()->toString(),
           ],
         ],
       ],
@@ -78,7 +78,7 @@ public function testUuidEntityResolver() {
         $field_uri => [
           [
             '_links' => [
-              'self' => $entity->url(),
+              'self' => $entity->toUrl()->toString(),
             ],
             'uuid' => [
               [
diff --git a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
index ef5362a0edd80473ec2fb06775314dd727035375..c95a9d96e60f6a7af84a15a45e91ef5eadf9eaf0 100644
--- a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
+++ b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
@@ -141,7 +141,7 @@ public function testNormalize() {
           'target_id' => (int) $this->user->id(),
           'target_type' => $this->user->getEntityTypeId(),
           'target_uuid' => $this->user->uuid(),
-          'url' => $this->user->url(),
+          'url' => $this->user->toUrl()->toString(),
         ],
       ],
       'revision_id' => [
@@ -223,7 +223,7 @@ public function testSerialize() {
       'name' => '<name><value>' . $this->values['name'] . '</value></name>',
       'type' => '<type><value>entity_test_mulrev</value></type>',
       'created' => '<created><value>' . $expected_created['value'] . '</value><format>' . $expected_created['format'] . '</format></created>',
-      'user_id' => '<user_id><target_id>' . $this->user->id() . '</target_id><target_type>' . $this->user->getEntityTypeId() . '</target_type><target_uuid>' . $this->user->uuid() . '</target_uuid><url>' . $this->user->url() . '</url></user_id>',
+      'user_id' => '<user_id><target_id>' . $this->user->id() . '</target_id><target_type>' . $this->user->getEntityTypeId() . '</target_type><target_uuid>' . $this->user->uuid() . '</target_uuid><url>' . $this->user->toUrl()->toString() . '</url></user_id>',
       'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
       'revision_translation_affected' => '<revision_translation_affected><value>1</value></revision_translation_affected>',
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 2046271562c931e7fc2f3aa5d9635c7fe7049b9a..5553071e393b003094156be23889962527b3b557 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -407,7 +407,7 @@ function shortcut_toolbar() {
         'tab' => [
           '#type' => 'link',
           '#title' => t('Shortcuts'),
-          '#url' => $shortcut_set->urlInfo('collection'),
+          '#url' => $shortcut_set->toUrl('collection'),
           '#attributes' => [
             'title' => t('Shortcuts'),
             'class' => ['toolbar-icon', 'toolbar-icon-shortcut'],
diff --git a/core/modules/shortcut/src/Form/SetCustomize.php b/core/modules/shortcut/src/Form/SetCustomize.php
index 027f023617b7297c405c7189d991922dde430b32..f69a9c92b5ca14ea10a5cb4be4d57868f47618b0 100644
--- a/core/modules/shortcut/src/Form/SetCustomize.php
+++ b/core/modules/shortcut/src/Form/SetCustomize.php
@@ -67,11 +67,11 @@ public function form(array $form, FormStateInterface $form_state) {
 
       $links['edit'] = [
         'title' => t('Edit'),
-        'url' => $shortcut->urlInfo(),
+        'url' => $shortcut->toUrl(),
       ];
       $links['delete'] = [
         'title' => t('Delete'),
-        'url' => $shortcut->urlInfo('delete-form'),
+        'url' => $shortcut->toUrl('delete-form'),
       ];
       $form['shortcuts']['links'][$id]['operations'] = [
         '#type' => 'operations',
diff --git a/core/modules/shortcut/src/ShortcutSetForm.php b/core/modules/shortcut/src/ShortcutSetForm.php
index 2a27cb7029997c2afffd0c66bed9538a60be393b..635d9baba1e8e3bfda7c550a6d777dcb3d80676f 100644
--- a/core/modules/shortcut/src/ShortcutSetForm.php
+++ b/core/modules/shortcut/src/ShortcutSetForm.php
@@ -58,7 +58,7 @@ public function save(array $form, FormStateInterface $form_state) {
     else {
       $this->messenger()->addStatus($this->t('Updated set name to %set-name.', ['%set-name' => $entity->label()]));
     }
-    $form_state->setRedirectUrl($this->entity->urlInfo('customize-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('customize-form'));
   }
 
 }
diff --git a/core/modules/shortcut/src/ShortcutSetListBuilder.php b/core/modules/shortcut/src/ShortcutSetListBuilder.php
index 2cc582e944578d57b92161cb4ef8a0c6320f48d1..e873aaea4f33de6655f7ff3af6a975c1675b84db 100644
--- a/core/modules/shortcut/src/ShortcutSetListBuilder.php
+++ b/core/modules/shortcut/src/ShortcutSetListBuilder.php
@@ -32,7 +32,7 @@ public function getDefaultOperations(EntityInterface $entity) {
 
     $operations['list'] = [
       'title' => t('List links'),
-      'url' => $entity->urlInfo('customize-form'),
+      'url' => $entity->toUrl('customize-form'),
     ];
     return $operations;
   }
diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php b/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
index 5861d99838e97ff5c50050efbd185ed76aa8f498..6701ee933eccfde8f4e11f7f64fcc7198e33cecd 100644
--- a/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
+++ b/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
@@ -97,7 +97,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('@title [%language translation]', [
diff --git a/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php b/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php
index 0f169238c6a3b221582c72b18402aa802338e815..574a27fa636cef3ff0d8c1d4657eb14e41a276a6 100644
--- a/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php
+++ b/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php
@@ -57,7 +57,7 @@ public function testPopularContentBlock() {
     $this->assertCacheContexts($contexts);
 
     // Check if the node link is displayed.
-    $this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical')), 'Found link to visited node.');
+    $this->assertRaw(\Drupal::l($node->label(), $node->toUrl('canonical')), 'Found link to visited node.');
   }
 
 }
diff --git a/core/modules/system/src/Form/DateFormatFormBase.php b/core/modules/system/src/Form/DateFormatFormBase.php
index 9e92a70c4420afeb4ec8bfc3b8ffa9a2577026f3..547b27d363b36c0c8cf601283f0ffacc9464d163 100644
--- a/core/modules/system/src/Form/DateFormatFormBase.php
+++ b/core/modules/system/src/Form/DateFormatFormBase.php
@@ -154,7 +154,7 @@ public function save(array $form, FormStateInterface $form_state) {
     else {
       $this->messenger()->addStatus($this->t('Custom date format added.'));
     }
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
 }
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 517e1c670ece810e53f4b5f27af427d0bdbc0be1..92587b3628fa13910b6846b477c309fc622ce7d7 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -331,8 +331,8 @@ protected function createReferenceTestEntities($referenced_entity) {
    */
   public function testReferencedEntity() {
     $entity_type = $this->entity->getEntityTypeId();
-    $referencing_entity_url = $this->referencingEntity->urlInfo('canonical');
-    $non_referencing_entity_url = $this->nonReferencingEntity->urlInfo('canonical');
+    $referencing_entity_url = $this->referencingEntity->toUrl('canonical');
+    $non_referencing_entity_url = $this->nonReferencingEntity->toUrl('canonical');
     $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', [
       'entity_reference_field_name' => $entity_type . '_reference',
       'referenced_entity_type' => $entity_type,
diff --git a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
index 3fd9be010e2a7b8d47f40e2365fef9fbfc9a0e9d..69b35028a7de442da7488854d7818511cd37ffa0 100644
--- a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
@@ -27,7 +27,7 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
    * - "<entity_type>:<entity ID>"
    */
   public function testEntityUri() {
-    $entity_url = $this->entity->urlInfo();
+    $entity_url = $this->entity->toUrl();
     $entity_type = $this->entity->getEntityTypeId();
 
     // Selects the view mode that will be used.
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 13456c762dc36c225f93049b8aa5c6fda014c774..02717b448c2dc856a6714a6e6812f6ff2e72513e 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -836,10 +836,10 @@ function system_user_login(UserInterface $account) {
   if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
     \Drupal::messenger()
       ->addStatus(t('Configure your <a href=":user-edit">account time zone setting</a>.', [
-        ':user-edit' => $account->url('edit-form', [
+        ':user-edit' => $account->toUrl('edit-form', [
           'query' => \Drupal::destination()->getAsArray(),
           'fragment' => 'edit-timezone',
-        ]),
+        ])->toString(),
       ]));
   }
 }
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php b/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
index 447afbfa769cc20a112f2d6ac7e17129703410e2..36d8d0670ff0a608ec3ddc72b513c1237d2efa6a 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
@@ -324,8 +324,8 @@ protected function createReferenceTestEntities($referenced_entity) {
    */
   public function testReferencedEntity() {
     $entity_type = $this->entity->getEntityTypeId();
-    $referencing_entity_url = $this->referencingEntity->urlInfo('canonical');
-    $non_referencing_entity_url = $this->nonReferencingEntity->urlInfo('canonical');
+    $referencing_entity_url = $this->referencingEntity->toUrl('canonical');
+    $non_referencing_entity_url = $this->nonReferencingEntity->toUrl('canonical');
     $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', [
       'entity_reference_field_name' => $entity_type . '_reference',
       'referenced_entity_type' => $entity_type,
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityOperationsTest.php b/core/modules/system/tests/src/Functional/Entity/EntityOperationsTest.php
index b020f42870cd01e745bf719e0c03e748fc286ae0..bcb385bf163931a91f565be1fe52d333fdaf1b42 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityOperationsTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityOperationsTest.php
@@ -35,7 +35,7 @@ public function testEntityOperationAlter() {
     $this->drupalGet('admin/people/roles');
     $roles = user_roles();
     foreach ($roles as $role) {
-      $this->assertLinkByHref($role->url() . '/test_operation');
+      $this->assertLinkByHref($role->toUrl()->toString() . '/test_operation');
       $this->assertLink(format_string('Test Operation: @label', ['@label' => $role->label()]));
     }
   }
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/tests/src/Functional/Entity/EntityWithUriCacheTagsTestBase.php
index ed4914407ea1157a741fb13cea43b24edf0982f1..27f896b9a66faeee7eb09a1b639e7953083b7242 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityWithUriCacheTagsTestBase.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityWithUriCacheTagsTestBase.php
@@ -20,7 +20,7 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
    * - "<entity_type>:<entity ID>"
    */
   public function testEntityUri() {
-    $entity_url = $this->entity->urlInfo();
+    $entity_url = $this->entity->toUrl();
     $entity_type = $this->entity->getEntityTypeId();
 
     // Selects the view mode that will be used.
diff --git a/core/modules/system/tests/src/Functional/Menu/MenuRouterTest.php b/core/modules/system/tests/src/Functional/Menu/MenuRouterTest.php
index 7669e1875af72d97579d32d8f6863e69844f4659..4296df55bde1fdcb87502cf390e4a8e72f287844 100644
--- a/core/modules/system/tests/src/Functional/Menu/MenuRouterTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/MenuRouterTest.php
@@ -233,11 +233,11 @@ public function testAuthUserUserLogin() {
 
     $this->drupalGet('user/login');
     // Check that we got to 'user'.
-    $this->assertUrl($this->loggedInUser->url('canonical', ['absolute' => TRUE]));
+    $this->assertUrl($this->loggedInUser->toUrl('canonical', ['absolute' => TRUE])->toString());
 
     // user/register should redirect to user/UID/edit.
     $this->drupalGet('user/register');
-    $this->assertUrl($this->loggedInUser->url('edit-form', ['absolute' => TRUE]));
+    $this->assertUrl($this->loggedInUser->toUrl('edit-form', ['absolute' => TRUE])->toString());
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/System/ResponseGeneratorTest.php b/core/modules/system/tests/src/Functional/System/ResponseGeneratorTest.php
index 592364636d954f36159d458f12ae45ace40de25c..d0f2ab539a43a5f549041c0e2b4f7bb107fa41ef 100644
--- a/core/modules/system/tests/src/Functional/System/ResponseGeneratorTest.php
+++ b/core/modules/system/tests/src/Functional/System/ResponseGeneratorTest.php
@@ -41,7 +41,7 @@ public function testGeneratorHeaderAdded() {
     $expectedGeneratorHeader = 'Drupal ' . $version . ' (https://www.drupal.org)';
 
     // Check to see if the header is added when viewing a normal content page
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $this->assertResponse(200);
     $this->assertEqual('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
     $this->assertEqual($expectedGeneratorHeader, $this->drupalGetHeader('X-Generator'));
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index 2c8dbda8a55c95c3e11a75ae4b563d7234c6c682..5f7d0ce8714a42dc3de33786b96f4a50e297bc28 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -276,7 +276,7 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
         '#prefix' => !empty($indentation) ? \Drupal::service('renderer')->render($indentation) : '',
         '#type' => 'link',
         '#title' => $term->getName(),
-        '#url' => $term->urlInfo(),
+        '#url' => $term->toUrl(),
       ];
       if ($vocabulary_hierarchy != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) {
         $parent_fields = TRUE;
@@ -496,7 +496,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   public function submitReset(array &$form, FormStateInterface $form_state) {
     /** @var $vocabulary \Drupal\taxonomy\VocabularyInterface */
     $vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
-    $form_state->setRedirectUrl($vocabulary->urlInfo('reset-form'));
+    $form_state->setRedirectUrl($vocabulary->toUrl('reset-form'));
   }
 
 }
diff --git a/core/modules/taxonomy/src/Form/VocabularyResetForm.php b/core/modules/taxonomy/src/Form/VocabularyResetForm.php
index c7ea78f7c02c888327624a98910e78e8757e2e97..763e14b131b6be397eb6ebcbacb0339d13e2790f 100644
--- a/core/modules/taxonomy/src/Form/VocabularyResetForm.php
+++ b/core/modules/taxonomy/src/Form/VocabularyResetForm.php
@@ -58,7 +58,7 @@ public function getQuestion() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->entity->urlInfo('overview-form');
+    return $this->entity->toUrl('overview-form');
   }
 
   /**
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index 2c3e510b6025c4e739f2907205f125d1b68c78a0..438b219a7fc45591e75a3a76e37bd2edecd838f1 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -129,8 +129,8 @@ public function save(array $form, FormStateInterface $form_state) {
 
     $result = $term->save();
 
-    $edit_link = $term->link($this->t('Edit'), 'edit-form');
-    $view_link = $term->link($term->getName());
+    $edit_link = $term->toLink($this->t('Edit'), 'edit-form')->toString();
+    $view_link = $term->toLink()->toString();
     switch ($result) {
       case SAVED_NEW:
         $this->messenger()->addStatus($this->t('Created new term %term.', ['%term' => $view_link]));
diff --git a/core/modules/taxonomy/src/TermTranslationHandler.php b/core/modules/taxonomy/src/TermTranslationHandler.php
index 688c63e38bdd9f65ab5dfc40a5c082038447030e..7108232d5c8b34bf99b19adb2f719398e885c807 100644
--- a/core/modules/taxonomy/src/TermTranslationHandler.php
+++ b/core/modules/taxonomy/src/TermTranslationHandler.php
@@ -32,7 +32,7 @@ public function entityFormSave(array $form, FormStateInterface $form_state) {
       // We need a redirect here, otherwise we would get an access denied page,
       // since the current URL would be preserved and we would try to add a
       // translation for a language that already has a translation.
-      $form_state->setRedirectUrl($entity->urlInfo('edit-form'));
+      $form_state->setRedirectUrl($entity->toUrl('edit-form'));
     }
   }
 
diff --git a/core/modules/taxonomy/src/VocabularyForm.php b/core/modules/taxonomy/src/VocabularyForm.php
index 61b3638ec24e348c126ed3d4c34e028e988a7f2a..d80b72b4debf3d6efb8aad6b98a530baa155d4a6 100644
--- a/core/modules/taxonomy/src/VocabularyForm.php
+++ b/core/modules/taxonomy/src/VocabularyForm.php
@@ -122,18 +122,18 @@ public function save(array $form, FormStateInterface $form_state) {
     $vocabulary->set('name', trim($vocabulary->label()));
 
     $status = $vocabulary->save();
-    $edit_link = $this->entity->link($this->t('Edit'));
+    $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString();
     switch ($status) {
       case SAVED_NEW:
         $this->messenger()->addStatus($this->t('Created new vocabulary %name.', ['%name' => $vocabulary->label()]));
         $this->logger('taxonomy')->notice('Created new vocabulary %name.', ['%name' => $vocabulary->label(), 'link' => $edit_link]);
-        $form_state->setRedirectUrl($vocabulary->urlInfo('overview-form'));
+        $form_state->setRedirectUrl($vocabulary->toUrl('overview-form'));
         break;
 
       case SAVED_UPDATED:
         $this->messenger()->addStatus($this->t('Updated vocabulary %name.', ['%name' => $vocabulary->label()]));
         $this->logger('taxonomy')->notice('Updated vocabulary %name.', ['%name' => $vocabulary->label(), 'link' => $edit_link]);
-        $form_state->setRedirectUrl($vocabulary->urlInfo('collection'));
+        $form_state->setRedirectUrl($vocabulary->toUrl('collection'));
         break;
     }
 
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 123dd92f389e8bd79787bc37dbb3e9fa8316d6ba..4ad306298586b9b2208ab52ab042b0c9dd4207a8 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -125,7 +125,7 @@ function taxonomy_page_attachments_alter(array &$page) {
       $page['#attached']['html_head_link'][] = [
         [
           'rel' => $rel,
-          'href' => $term->url($rel),
+          'href' => $term->toUrl($rel)->toString(),
         ],
         TRUE,
       ];
@@ -136,7 +136,7 @@ function taxonomy_page_attachments_alter(array &$page) {
         $page['#attached']['html_head_link'][] = [
           [
             'rel' => 'shortlink',
-            'href' => $term->url($rel, ['alias' => TRUE]),
+            'href' => $term->toUrl($rel, ['alias' => TRUE])->toString(),
           ],
           TRUE,
         ];
@@ -256,7 +256,7 @@ function template_preprocess_taxonomy_term(&$variables) {
   /** @var \Drupal\taxonomy\TermInterface $term */
   $term = $variables['term'];
 
-  $variables['url'] = $term->url();
+  $variables['url'] = $term->toUrl()->toString();
   // We use name here because that is what appears in the UI.
   $variables['name'] = $variables['elements']['name'];
   unset($variables['elements']['name']);
diff --git a/core/modules/taxonomy/taxonomy.tokens.inc b/core/modules/taxonomy/taxonomy.tokens.inc
index 8511c7acb9a71e9764eeac7016ddaefcc8dd0ad1..b6931b7cbb08097e57c911eab489bc2ad85a07a7 100644
--- a/core/modules/taxonomy/taxonomy.tokens.inc
+++ b/core/modules/taxonomy/taxonomy.tokens.inc
@@ -116,7 +116,7 @@ function taxonomy_tokens($type, $tokens, array $data, array $options, Bubbleable
           break;
 
         case 'url':
-          $replacements[$original] = $term->url('canonical', ['absolute' => TRUE]);
+          $replacements[$original] = $term->toUrl('canonical', ['absolute' => TRUE])->toString();
           break;
 
         case 'node-count':
diff --git a/core/modules/taxonomy/tests/src/Functional/RssTest.php b/core/modules/taxonomy/tests/src/Functional/RssTest.php
index 2f22196469154f9dc3ff69bbaf7be7507ac909de..906109a4a39603a311410b811420b4f2916c0bfe 100644
--- a/core/modules/taxonomy/tests/src/Functional/RssTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/RssTest.php
@@ -95,7 +95,7 @@ public function testTaxonomyRss() {
     $this->drupalGet('rss.xml');
     $test_element = sprintf(
       '<category %s>%s</category>',
-      'domain="' . $term1->url('canonical', ['absolute' => TRUE]) . '"',
+      'domain="' . $term1->toUrl('canonical', ['absolute' => TRUE])->toString() . '"',
       $term1->getName()
     );
     $this->assertRaw($test_element, 'Term is displayed when viewing the rss feed.');
diff --git a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
index 83ab04d5e13ef19b82fd106295d44cdd1600a454..64588c05f7654eb2f3022d52bb88516dba7a4ad2 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
@@ -207,7 +207,7 @@ public function testTaxonomyTermHierarchyBreadcrumbs() {
     $this->drupalGet('taxonomy/term/' . $term1->id());
     // Breadcrumbs are not rendered with a language, prevent the term
     // language from being added to the options.
-    $this->assertRaw(\Drupal::l($term2->getName(), $term2->urlInfo('canonical', ['language' => NULL])), 'Parent term link is displayed when viewing the node.');
+    $this->assertRaw(\Drupal::l($term2->getName(), $term2->toUrl('canonical', ['language' => NULL])), 'Parent term link is displayed when viewing the node.');
   }
 
 }
diff --git a/core/modules/taxonomy/tests/src/Functional/TermTranslationTest.php b/core/modules/taxonomy/tests/src/Functional/TermTranslationTest.php
index bd9085017b64c6da7fef6e745c909a8ee9d5b9d2..411ae5e496046888e4bbc474c8705daacbc75cc6 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermTranslationTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermTranslationTest.php
@@ -57,14 +57,14 @@ public function testTranslatedBreadcrumbs() {
     // Ensure non-translated breadcrumb is correct.
     $breadcrumb = [Url::fromRoute('<front>')->toString() => 'Home'];
     foreach ($this->terms as $term) {
-      $breadcrumb[$term->url()] = $term->label();
+      $breadcrumb[$term->toUrl()->toString()] = $term->label();
     }
     // The last item will not be in the breadcrumb.
     array_pop($breadcrumb);
 
     // Check the breadcrumb on the leaf term page.
     $term = $this->getLeafTerm();
-    $this->assertBreadcrumb($term->urlInfo(), $breadcrumb, $term->label());
+    $this->assertBreadcrumb($term->toUrl(), $breadcrumb, $term->label());
 
     $languages = \Drupal::languageManager()->getLanguages();
 
@@ -72,7 +72,7 @@ public function testTranslatedBreadcrumbs() {
     $breadcrumb = [Url::fromRoute('<front>', [], ['language' => $languages[$this->translateToLangcode]])->toString() => 'Home'];
     foreach ($this->terms as $term) {
       $translated = $term->getTranslation($this->translateToLangcode);
-      $url = $translated->url('canonical', ['language' => $languages[$this->translateToLangcode]]);
+      $url = $translated->toUrl('canonical', ['language' => $languages[$this->translateToLangcode]])->toString();
       $breadcrumb[$url] = $translated->label();
     }
     array_pop($breadcrumb);
@@ -80,7 +80,7 @@ public function testTranslatedBreadcrumbs() {
     // Check for the translated breadcrumb on the translated leaf term page.
     $term = $this->getLeafTerm();
     $translated = $term->getTranslation($this->translateToLangcode);
-    $this->assertBreadcrumb($translated->urlInfo('canonical', ['language' => $languages[$this->translateToLangcode]]), $breadcrumb, $translated->label());
+    $this->assertBreadcrumb($translated->toUrl('canonical', ['language' => $languages[$this->translateToLangcode]]), $breadcrumb, $translated->label());
 
   }
 
diff --git a/core/modules/taxonomy/tests/src/Functional/TermTranslationUITest.php b/core/modules/taxonomy/tests/src/Functional/TermTranslationUITest.php
index 0d24b6990e7bf9af33618ff5f3cedafbba3a6970..b51b20a3f39fa096d44fd474b7d003747f170a12 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermTranslationUITest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermTranslationUITest.php
@@ -149,7 +149,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('@title [%language translation]', [
diff --git a/core/modules/taxonomy/tests/src/Functional/TokenReplaceTest.php b/core/modules/taxonomy/tests/src/Functional/TokenReplaceTest.php
index 958da3bebaaed02a662071844f171751c0466ac7..e62f72895cbc180ee17dfb93bcfee5c92bfd081f 100644
--- a/core/modules/taxonomy/tests/src/Functional/TokenReplaceTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TokenReplaceTest.php
@@ -81,7 +81,7 @@ public function testTaxonomyTokenReplacement() {
     $tests['[term:tid]'] = $term1->id();
     $tests['[term:name]'] = $term1->getName();
     $tests['[term:description]'] = $term1->description->processed;
-    $tests['[term:url]'] = $term1->url('canonical', ['absolute' => TRUE]);
+    $tests['[term:url]'] = $term1->toUrl('canonical', ['absolute' => TRUE])->toString();
     $tests['[term:node-count]'] = 0;
     $tests['[term:parent:name]'] = '[term:parent:name]';
     $tests['[term:vocabulary:name]'] = $this->vocabulary->label();
@@ -112,10 +112,10 @@ public function testTaxonomyTokenReplacement() {
     $tests['[term:tid]'] = $term2->id();
     $tests['[term:name]'] = $term2->getName();
     $tests['[term:description]'] = $term2->description->processed;
-    $tests['[term:url]'] = $term2->url('canonical', ['absolute' => TRUE]);
+    $tests['[term:url]'] = $term2->toUrl('canonical', ['absolute' => TRUE])->toString();
     $tests['[term:node-count]'] = 1;
     $tests['[term:parent:name]'] = $term1->getName();
-    $tests['[term:parent:url]'] = $term1->url('canonical', ['absolute' => TRUE]);
+    $tests['[term:parent:url]'] = $term1->toUrl('canonical', ['absolute' => TRUE])->toString();
     $tests['[term:parent:parent:name]'] = '[term:parent:parent:name]';
     $tests['[term:vocabulary:name]'] = $this->vocabulary->label();
 
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
index ad1d67d6bc32814f48c1223b1cd7a55daa766b57..de313e087f10a0a5f06aa15f900922a4698145dc 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
@@ -28,13 +28,13 @@ public function testViewsHandlerAllTermsField() {
     $this->executeView($view);
     $this->drupalGet('taxonomy_all_terms_test');
 
-    $actual = $this->xpath('//a[@href="' . $this->term1->url() . '"]');
+    $actual = $this->xpath('//a[@href="' . $this->term1->toUrl()->toString() . '"]');
     $this->assertEqual(count($actual), 2, 'Correct number of taxonomy term1 links');
     $this->assertEqual($actual[0]->getText(), $this->term1->label());
     $this->assertEqual($actual[1]->getText(), $this->term1->label());
     $this->assertEscaped($this->term1->label());
 
-    $actual = $this->xpath('//a[@href="' . $this->term2->url() . '"]');
+    $actual = $this->xpath('//a[@href="' . $this->term2->toUrl()->toString() . '"]');
     $this->assertEqual(count($actual), 2, 'Correct number of taxonomy term2 links');
     $this->assertEqual($actual[0]->getText(), $this->term2->label());
     $this->assertEqual($actual[1]->getText(), $this->term2->label());
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldTidTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldTidTest.php
index 0a44d4cb2ea15aa55f479875de890c9ab16bc1c5..6ac6bb9f31dd092d1acd144be539f70fc14912cf 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldTidTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldTidTest.php
@@ -29,7 +29,7 @@ public function testViewsHandlerTidField() {
     $actual = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
       return $view->field['name']->advancedRender($view->result[0]);
     });
-    $expected = \Drupal::l($this->term1->label(), $this->term1->urlInfo());
+    $expected = \Drupal::l($this->term1->label(), $this->term1->toUrl());
 
     $this->assertEqual($expected, $actual);
   }
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
index 8b1b822e3e32140cf7b8d9adf7a60a29b0f1a20e..a269c8fe0df8ffe81174cb5b244b8cc2a38456f9 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -153,9 +153,9 @@ public function testExposedFilter() {
     $this->drupalGet('test-filter-taxonomy-index-tid');
     $xpath = $this->xpath('//div[@class="view-content"]//a');
     $this->assertIdentical(2, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node3->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node3->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
 
     // Expose the filter.
@@ -173,7 +173,7 @@ public function testExposedFilter() {
     $this->drupalGet('test-filter-taxonomy-index-tid');
     $xpath = $this->xpath('//div[@class="view-content"]//a');
     $this->assertIdentical(1, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node1->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node1->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
 
     // Set the operator to 'not empty'.
@@ -186,11 +186,11 @@ public function testExposedFilter() {
     $this->drupalGet('test-filter-taxonomy-index-tid');
     $xpath = $this->xpath('//div[@class="view-content"]//a');
     $this->assertIdentical(3, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node3->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node3->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
-    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node4->url()]);
+    $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node4->toUrl()->toString()]);
     $this->assertIdentical(1, count($xpath));
 
     // Select 'Term ID' as the field to be displayed.
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php
index 3b9ba26d87822ee02d52a5db08082b75aeec2149..5ccc8e16cec108c9bfba9c897203571268c8ce17 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php
@@ -55,7 +55,7 @@ protected function initViewWithRequest($request_url, $view_name = 'taxonomy_defa
    * Tests the relationship.
    */
   public function testNodePath() {
-    $view = $this->initViewWithRequest($this->nodes[0]->url());
+    $view = $this->initViewWithRequest($this->nodes[0]->toUrl()->toString());
 
     $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
     $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
@@ -77,14 +77,14 @@ public function testNodePathWithViewSelection() {
     ]);
     $field->save();
 
-    $view = $this->initViewWithRequest($this->nodes[0]->url());
+    $view = $this->initViewWithRequest($this->nodes[0]->toUrl()->toString());
 
     $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
     $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
   }
 
   public function testTermPath() {
-    $view = $this->initViewWithRequest($this->term1->url());
+    $view = $this->initViewWithRequest($this->term1->toUrl()->toString());
 
     $expected = $this->term1->id();
     $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
diff --git a/core/modules/tracker/tracker.pages.inc b/core/modules/tracker/tracker.pages.inc
index 9093776de2e57f14d954d50ac813551dacd5bc4e..507f14b64efd2269561cf61222a83a3fb459bcd2 100644
--- a/core/modules/tracker/tracker.pages.inc
+++ b/core/modules/tracker/tracker.pages.inc
@@ -89,7 +89,7 @@ function tracker_page($account = NULL) {
         'title' => [
           'data' => [
             '#type' => 'link',
-            '#url' => $node->urlInfo(),
+            '#url' => $node->toUrl(),
             '#title' => $node->getTitle(),
           ],
           'data-history-node-id' => $node->id(),
diff --git a/core/modules/user/src/Form/UserCancelForm.php b/core/modules/user/src/Form/UserCancelForm.php
index 4430d0b4762e3aa9917ea79d02e68cff5ee0ea48..5535cb4588db39257975069e9fe839d935bcae98 100644
--- a/core/modules/user/src/Form/UserCancelForm.php
+++ b/core/modules/user/src/Form/UserCancelForm.php
@@ -47,7 +47,7 @@ public function getQuestion() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->entity->urlInfo();
+    return $this->entity->toUrl();
   }
 
   /**
@@ -142,7 +142,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     if (!$form_state->isValueEmpty('access') && $form_state->isValueEmpty('user_cancel_confirm') && $this->entity->id() != $this->currentUser()->id()) {
       user_cancel($form_state->getValues(), $this->entity->id(), $form_state->getValue('user_cancel_method'));
 
-      $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+      $form_state->setRedirectUrl($this->entity->toUrl('collection'));
     }
     else {
       // Store cancelling method and whether to notify the user in
diff --git a/core/modules/user/src/Plugin/Search/UserSearch.php b/core/modules/user/src/Plugin/Search/UserSearch.php
index cb9029410e3cc91d271b136e59e0a079ad821a29..8bae56510c1f70117cd3b6c437b48bc617486827 100644
--- a/core/modules/user/src/Plugin/Search/UserSearch.php
+++ b/core/modules/user/src/Plugin/Search/UserSearch.php
@@ -146,7 +146,7 @@ public function execute() {
     foreach ($accounts as $account) {
       $result = [
         'title' => $account->getDisplayName(),
-        'link' => $account->url('canonical', ['absolute' => TRUE]),
+        'link' => $account->toUrl('canonical', ['absolute' => TRUE])->toString(),
       ];
       if ($this->currentUser->hasPermission('administer users')) {
         $result['title'] .= ' (' . $account->getEmail() . ')';
diff --git a/core/modules/user/src/ProfileTranslationHandler.php b/core/modules/user/src/ProfileTranslationHandler.php
index 5b469525a4a25d3e1239f929501459fe15ca10b6..429385ac65d8af33aedccab19e1ac98ab87d196d 100644
--- a/core/modules/user/src/ProfileTranslationHandler.php
+++ b/core/modules/user/src/ProfileTranslationHandler.php
@@ -48,7 +48,7 @@ public function entityFormSave(array $form, FormStateInterface $form_state) {
       // We need a redirect here, otherwise we would get an access denied page
       // since the current URL would be preserved and we would try to add a
       // translation for a language that already has a translation.
-      $form_state->setRedirectUrl($entity->urlInfo());
+      $form_state->setRedirectUrl($entity->toUrl());
     }
   }
 
diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php
index a1ea5e944fc46d0d0cc11c6278d35a2b218756a4..9cf710da38664c40ce4dd12e12f33986e619c4aa 100644
--- a/core/modules/user/src/RegisterForm.php
+++ b/core/modules/user/src/RegisterForm.php
@@ -103,14 +103,14 @@ public function save(array $form, FormStateInterface $form_state) {
     $form_state->set('user', $account);
     $form_state->setValue('uid', $account->id());
 
-    $this->logger('user')->notice('New user: %name %email.', ['%name' => $form_state->getValue('name'), '%email' => '<' . $form_state->getValue('mail') . '>', 'type' => $account->link($this->t('Edit'), 'edit-form')]);
+    $this->logger('user')->notice('New user: %name %email.', ['%name' => $form_state->getValue('name'), '%email' => '<' . $form_state->getValue('mail') . '>', 'type' => $account->toLink($this->t('Edit'), 'edit-form')->toString()]);
 
     // Add plain text password into user account to generate mail tokens.
     $account->password = $pass;
 
     // New administrative account without notification.
     if ($admin && !$notify) {
-      $this->messenger()->addStatus($this->t('Created a new user account for <a href=":url">%name</a>. No email has been sent.', [':url' => $account->url(), '%name' => $account->getAccountName()]));
+      $this->messenger()->addStatus($this->t('Created a new user account for <a href=":url">%name</a>. No email has been sent.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
     }
     // No email verification required; log in user immediately.
     elseif (!$admin && !\Drupal::config('user.settings')->get('verify_mail') && $account->isActive()) {
@@ -122,13 +122,13 @@ public function save(array $form, FormStateInterface $form_state) {
     // No administrator approval required.
     elseif ($account->isActive() || $notify) {
       if (!$account->getEmail() && $notify) {
-        $this->messenger()->addStatus($this->t('The new user <a href=":url">%name</a> was created without an email address, so no welcome message was sent.', [':url' => $account->url(), '%name' => $account->getAccountName()]));
+        $this->messenger()->addStatus($this->t('The new user <a href=":url">%name</a> was created without an email address, so no welcome message was sent.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
       }
       else {
         $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
         if (_user_mail_notify($op, $account)) {
           if ($notify) {
-            $this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user <a href=":url">%name</a>.', [':url' => $account->url(), '%name' => $account->getAccountName()]));
+            $this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user <a href=":url">%name</a>.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
           }
           else {
             $this->messenger()->addStatus($this->t('A welcome message with further instructions has been sent to your email address.'));
diff --git a/core/modules/user/src/RoleForm.php b/core/modules/user/src/RoleForm.php
index 023159b8709b101edde8c5e95f9ec8371dd00b55..34aa98e8fc199369d9fc7b2c999f5eb0a7fc57f1 100644
--- a/core/modules/user/src/RoleForm.php
+++ b/core/modules/user/src/RoleForm.php
@@ -55,7 +55,7 @@ public function save(array $form, FormStateInterface $form_state) {
     $entity->set('label', trim($entity->label()));
     $status = $entity->save();
 
-    $edit_link = $this->entity->link($this->t('Edit'));
+    $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString();
     if ($status == SAVED_UPDATED) {
       $this->messenger()->addStatus($this->t('Role %label has been updated.', ['%label' => $entity->label()]));
       $this->logger('user')->notice('Role %label has been updated.', ['%label' => $entity->label(), 'link' => $edit_link]);
diff --git a/core/modules/user/src/RoleListBuilder.php b/core/modules/user/src/RoleListBuilder.php
index 306677a1ef8901c4bc3b72ccb213c4c4e69e7390..40ae5e4891bafc3166cd133dd48a1c015a2144ee 100644
--- a/core/modules/user/src/RoleListBuilder.php
+++ b/core/modules/user/src/RoleListBuilder.php
@@ -85,7 +85,7 @@ public function getDefaultOperations(EntityInterface $entity) {
       $operations['permissions'] = [
         'title' => t('Edit permissions'),
         'weight' => 20,
-        'url' => $entity->urlInfo('edit-permissions-form'),
+        'url' => $entity->toUrl('edit-permissions-form'),
       ];
     }
     return $operations;
diff --git a/core/modules/user/tests/src/Functional/UserAdminTest.php b/core/modules/user/tests/src/Functional/UserAdminTest.php
index 71a32bd76063f237cef5a64392030bd6062637c0..3201059567d908a60dc8a2f1bde9e4e19fe0d3f3 100644
--- a/core/modules/user/tests/src/Functional/UserAdminTest.php
+++ b/core/modules/user/tests/src/Functional/UserAdminTest.php
@@ -56,7 +56,7 @@ public function testUserAdmin() {
     $this->assertText($admin_user->getAccountName(), 'Found Admin user on admin users page');
 
     // Test for existence of edit link in table.
-    $link = $user_a->link(t('Edit'), 'edit-form', ['query' => ['destination' => $user_a->url('collection')]]);
+    $link = $user_a->toLink(t('Edit'), 'edit-form', ['query' => ['destination' => $user_a->toUrl('collection')->toString()]])->toString();
     $this->assertRaw($link, 'Found user A edit link on admin users page');
 
     // Test exposed filter elements.
diff --git a/core/modules/user/tests/src/Functional/UserBlocksTest.php b/core/modules/user/tests/src/Functional/UserBlocksTest.php
index f44d2a6b6465d9710c38a6d85707d87da5e3668b..6c56ff1f89009e08fc0c06566a72557f26865678 100644
--- a/core/modules/user/tests/src/Functional/UserBlocksTest.php
+++ b/core/modules/user/tests/src/Functional/UserBlocksTest.php
@@ -108,7 +108,7 @@ public function testUserLoginBlock() {
     $this->drupalLogout();
     $this->drupalPostForm('http://example.com/', $edit, t('Log in'), ['external' => FALSE]);
     // Check that we remain on the site after login.
-    $this->assertUrl($user->url('canonical', ['absolute' => TRUE]), [], 'Redirected to user profile page after login from the frontpage');
+    $this->assertUrl($user->toUrl('canonical', ['absolute' => TRUE])->toString(), [], 'Redirected to user profile page after login from the frontpage');
 
     // Verify that form validation errors are displayed immediately for forms
     // in blocks and not on subsequent page requests.
diff --git a/core/modules/user/tests/src/Functional/UserTokenReplaceTest.php b/core/modules/user/tests/src/Functional/UserTokenReplaceTest.php
index e272b60732524716751149708d8681e4b4008764..774119866bbded7d375030a930b0520ed9ae4f60 100644
--- a/core/modules/user/tests/src/Functional/UserTokenReplaceTest.php
+++ b/core/modules/user/tests/src/Functional/UserTokenReplaceTest.php
@@ -62,8 +62,8 @@ public function testUserTokenReplacement() {
     $tests['[user:account-name]'] = $account->getAccountName();
     $tests['[user:display-name]'] = $account->getDisplayName();
     $tests['[user:mail]'] = $account->getEmail();
-    $tests['[user:url]'] = $account->url('canonical', $url_options);
-    $tests['[user:edit-url]'] = $account->url('edit-form', $url_options);
+    $tests['[user:url]'] = $account->toUrl('canonical', $url_options)->toString();
+    $tests['[user:edit-url]'] = $account->toUrl('edit-form', $url_options)->toString();
     $tests['[user:last-login]'] = format_date($account->getLastLoginTime(), 'medium', '', NULL, $language_interface->getId());
     $tests['[user:last-login:short]'] = format_date($account->getLastLoginTime(), 'short', '', NULL, $language_interface->getId());
     $tests['[user:created]'] = format_date($account->getCreatedTime(), 'medium', '', NULL, $language_interface->getId());
diff --git a/core/modules/user/tests/src/Functional/UserTranslationUITest.php b/core/modules/user/tests/src/Functional/UserTranslationUITest.php
index 630e58586c5531e2b9922ef6f85042cde0b7a088..12502680364caa664026ad61bb54d0fb9947c10b 100644
--- a/core/modules/user/tests/src/Functional/UserTranslationUITest.php
+++ b/core/modules/user/tests/src/Functional/UserTranslationUITest.php
@@ -63,7 +63,7 @@ protected function doTestTranslationEdit() {
       // We only want to test the title for non-english translations.
       if ($langcode != 'en') {
         $options = ['language' => $languages[$langcode]];
-        $url = $entity->urlInfo('edit-form', $options);
+        $url = $entity->toUrl('edit-form', $options);
         $this->drupalGet($url);
 
         $title = t('@title [%language translation]', [
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index b574b8e3f1f3444c923a1c4927ffa5b5a243e8f2..88812ea4fa006c08d72046635c43da366b90e682 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -150,11 +150,11 @@ function hook_user_login(UserInterface $account) {
   if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
     \Drupal::messenger()
       ->addStatus(t('Configure your <a href=":user-edit">account time zone setting</a>.', [
-        ':user-edit' => $account->url('edit-form', [
+        ':user-edit' => $account->toUrl('edit-form', [
           'query' => \Drupal::destination()
             ->getAsArray(),
           'fragment' => 'edit-timezone',
-        ]),
+        ])->toString(),
       ]));
   }
 }
diff --git a/core/modules/user/user.tokens.inc b/core/modules/user/user.tokens.inc
index 8c02f6e6cbb7899004893a10716cb17c3530230b..df1e1443d85faba30143e3ac25a57117c9860a70 100644
--- a/core/modules/user/user.tokens.inc
+++ b/core/modules/user/user.tokens.inc
@@ -118,11 +118,11 @@ function user_tokens($type, $tokens, array $data, array $options, BubbleableMeta
           break;
 
         case 'url':
-          $replacements[$original] = $account->id() ? $account->url('canonical', $url_options) : t('not yet assigned');
+          $replacements[$original] = $account->id() ? $account->toUrl('canonical', $url_options)->toString() : t('not yet assigned');
           break;
 
         case 'edit-url':
-          $replacements[$original] = $account->id() ? $account->url('edit-form', $url_options) : t('not yet assigned');
+          $replacements[$original] = $account->id() ? $account->toUrl('edit-form', $url_options)->toString() : t('not yet assigned');
           break;
 
         // These tokens are default variations on the chained tokens handled below.
diff --git a/core/modules/views/tests/src/Functional/Plugin/MenuLinkTest.php b/core/modules/views/tests/src/Functional/Plugin/MenuLinkTest.php
index 8afbb57b8c8cfe46eafd5ae176b5532e5b7dbc9a..77591b44dde104553ae1dffd1346dda7114b7e16 100644
--- a/core/modules/views/tests/src/Functional/Plugin/MenuLinkTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/MenuLinkTest.php
@@ -90,7 +90,7 @@ public function testHierarchicalMenuLinkVisibility() {
 
     // Go to the node page and ensure that both the first and second level items
     // are visible.
-    $this->drupalGet($node->urlInfo());
+    $this->drupalGet($node->toUrl());
     $this->assertText('Primary level node');
     $this->assertText('Secondary level view page');
   }
diff --git a/core/modules/views/tests/src/Functional/Wizard/BasicTest.php b/core/modules/views/tests/src/Functional/Wizard/BasicTest.php
index ad690ad77718b39a1a8b978d5cc8fb53f205028f..1e2173481c99270df91f028351efbb55619a9fc3 100644
--- a/core/modules/views/tests/src/Functional/Wizard/BasicTest.php
+++ b/core/modules/views/tests/src/Functional/Wizard/BasicTest.php
@@ -85,9 +85,9 @@ public function testViewsWizardAndListing() {
     $this->assertEquals('2.0', $this->getSession()->getDriver()->getAttribute('//rss', 'version'));
     // The feed should have the same title and nodes as the page.
     $this->assertText($view2['page[title]']);
-    $this->assertRaw($node1->url('canonical', ['absolute' => TRUE]));
+    $this->assertRaw($node1->toUrl('canonical', ['absolute' => TRUE])->toString());
     $this->assertText($node1->label());
-    $this->assertRaw($node2->url('canonical', ['absolute' => TRUE]));
+    $this->assertRaw($node2->toUrl('canonical', ['absolute' => TRUE])->toString());
     $this->assertText($node2->label());
 
     // Go back to the views page and check if this view is there.
diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php
index 2d11874908e310e67afa4ac990a5ae2408e3b1e4..dad172e408412fb72a36e0581def58f4ce5ff829 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php
@@ -135,7 +135,7 @@ protected function doTestEntityLink(AccountInterface $account, $expected_results
       foreach ($expected_results as $template => $expected_result) {
         $expected_link = '';
         if ($expected_result) {
-          $path = $entity->url($info[$template]['relationship'], $info[$template]['options']);
+          $path = $entity->toUrl($info[$template]['relationship'], $info[$template]['options'])->toString();
           $destination = $info[$template]['destination'] ? '?destination=/' : '';
           if ($info[$template]['link']) {
             $expected_link = '<a href="' . $path . $destination . '" hreflang="en">' . $info[$template]['label'] . '</a>';
diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
index a738dcf5e3e690c5bae2f0272b941107f1366c6c..03ad765c46747e286d5c1383c417196540e6111d 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
@@ -329,7 +329,7 @@ public function testFieldAliasRender() {
       $this->assertEqual((string) ($i + 1), $executable->getStyle()->getField($i, 'id'));
       $this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name'));
       $entity = EntityTest::load($i + 1);
-      $this->assertEqual('<a href="' . $entity->url() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', (string) $executable->getStyle()->getField($i, 'name_alias'));
+      $this->assertEqual('<a href="' . $entity->toUrl()->toString() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', (string) $executable->getStyle()->getField($i, 'name_alias'));
     }
   }
 
diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
index 8dfa88201e31af69c1166f49d61f029693643bf9..6f10ce5f1052380343276461a9bf2e12a5867a7e 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
@@ -167,7 +167,7 @@ protected function doTestRenderedOutput(AccountInterface $account, $check_cache
       $counter_output = $view->style_plugin->getField($index, 'counter');
       $this->assertEqual($counter_output, $expected);
 
-      $node_url = $node->url();
+      $node_url = $node->toUrl()->toString();
       $expected = "<a href=\"$node_url\"><span class=\"da-title\">{$node->label()}</span> <span class=\"counter\">$counter_output</span></a>";
       $output = $view->style_plugin->getField($index, 'title');
       $this->assertEqual($output, $expected);
diff --git a/core/modules/views_ui/src/Form/Ajax/Analyze.php b/core/modules/views_ui/src/Form/Ajax/Analyze.php
index 0c630dfe0bbe1618425a55e33afbad66a8e4cf8e..c8295d293480de1f0f9febdbea7a9ba69814c0e9 100644
--- a/core/modules/views_ui/src/Form/Ajax/Analyze.php
+++ b/core/modules/views_ui/src/Form/Ajax/Analyze.php
@@ -56,7 +56,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     /** @var $view \Drupal\views_ui\ViewUI */
     $view = $form_state->get('view');
-    $form_state->setRedirectUrl($view->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($view->toUrl('edit-form'));
   }
 
 }
diff --git a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
index 7e5f83f7f503682e561cb1bb9e67fb30c643abfc..73cff0e4d880d1688a9c1ee8ce28d1a1dc4998ba 100644
--- a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
+++ b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
@@ -193,7 +193,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
 
     // Store in cache.
     $view->cacheSet();
-    $url = $view->urlInfo('edit-form')
+    $url = $view->toUrl('edit-form')
       ->setOption('fragment', 'views-tab-default');
     $form_state->setRedirectUrl($url);
   }
diff --git a/core/modules/views_ui/src/Form/BreakLockForm.php b/core/modules/views_ui/src/Form/BreakLockForm.php
index 91535750c3c6dfe6bff84cdbf12cb7a46fcd1634..6549c0434fbce8b09525a50f2435de5574023ae9 100644
--- a/core/modules/views_ui/src/Form/BreakLockForm.php
+++ b/core/modules/views_ui/src/Form/BreakLockForm.php
@@ -83,7 +83,7 @@ public function getDescription() {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->entity->urlInfo('edit-form');
+    return $this->entity->toUrl('edit-form');
   }
 
   /**
@@ -109,7 +109,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->tempStore->delete($this->entity->id());
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('edit-form'));
     $this->messenger()->addStatus($this->t('The lock has been broken and you may now edit this view.'));
   }
 
diff --git a/core/modules/views_ui/src/ViewAddForm.php b/core/modules/views_ui/src/ViewAddForm.php
index 7ba511e7059a88ac4cb1ab98b2533ec24023c80e..cfdd4e206e0c8ae92673b9cc87402a03e471ccf9 100644
--- a/core/modules/views_ui/src/ViewAddForm.php
+++ b/core/modules/views_ui/src/ViewAddForm.php
@@ -190,7 +190,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     }
     $this->entity->save();
     $this->messenger()->addStatus($this->t('The view %name has been saved.', ['%name' => $form_state->getValue('label')]));
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('edit-form'));
   }
 
   /**
diff --git a/core/modules/views_ui/src/ViewDuplicateForm.php b/core/modules/views_ui/src/ViewDuplicateForm.php
index 9cfe341d95ebb1a5b156989799cdb319bbbe219c..db2ec3f314cde9694b4b4fb0ab2b757a4633bd6f 100644
--- a/core/modules/views_ui/src/ViewDuplicateForm.php
+++ b/core/modules/views_ui/src/ViewDuplicateForm.php
@@ -74,7 +74,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->entity->save();
 
     // Redirect the user to the view admin form.
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->entity->toUrl('edit-form'));
   }
 
 }
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 3ca4885d945c7440a0fab3cc17b498867e376c1b..ac411e03d9d761ae30fb3bd78d2783ebf8ee364b 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -134,7 +134,7 @@ public function form(array $form, FormStateInterface $form_state) {
       $lock_message_substitutions = [
         '@user' => \Drupal::service('renderer')->render($username),
         '@age' => $this->dateFormatter->formatTimeDiffSince($view->lock->updated),
-        ':url' => $view->url('break-lock-form'),
+        ':url' => $view->toUrl('break-lock-form')->toString(),
       ];
       $form['locked'] = [
         '#type' => 'container',
@@ -343,7 +343,7 @@ public function cancel(array $form, FormStateInterface $form_state) {
     // Remove this view from cache so edits will be lost.
     $view = $this->entity;
     $this->tempStore->delete($view->id());
-    $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
   /**
@@ -662,7 +662,7 @@ public function submitDisplayDelete($form, FormStateInterface $form_state) {
 
     // Redirect to the top-level edit page. The first remaining display will
     // become the active display.
-    $form_state->setRedirectUrl($view->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($view->toUrl('edit-form'));
   }
 
   /**
@@ -718,7 +718,7 @@ public function renderDisplayTop(ViewUI $view) {
         ],
         'duplicate' => [
           'title' => $this->t('Duplicate view'),
-          'url' => $view->urlInfo('duplicate-form'),
+          'url' => $view->toUrl('duplicate-form'),
         ],
         'reorder' => [
           'title' => $this->t('Reorder displays'),
@@ -731,7 +731,7 @@ public function renderDisplayTop(ViewUI $view) {
     if ($view->access('delete')) {
       $element['extra_actions']['#links']['delete'] = [
         'title' => $this->t('Delete view'),
-        'url' => $view->urlInfo('delete-form'),
+        'url' => $view->toUrl('delete-form'),
       ];
     }
 
@@ -743,13 +743,13 @@ public function renderDisplayTop(ViewUI $view) {
         $element['extra_actions']['#links']['revert'] = [
           'title' => $this->t('Revert view'),
           'href' => "admin/structure/views/view/{$view->id()}/revert",
-          'query' => ['destination' => $view->url('edit-form')],
+          'query' => ['destination' => $view->toUrl('edit-form')->toString()],
         ];
       }
       else {
         $element['extra_actions']['#links']['delete'] = [
           'title' => $this->t('Delete view'),
-          'url' => $view->urlInfo('delete-form'),
+          'url' => $view->toUrl('delete-form'),
         ];
       }
     }
diff --git a/core/modules/views_ui/src/ViewFormBase.php b/core/modules/views_ui/src/ViewFormBase.php
index 49cdc4955f2397fae98fe9f28132057817c5ebbb..0b045b519e03ae630dfd3b2c76f22b275d256e45 100644
--- a/core/modules/views_ui/src/ViewFormBase.php
+++ b/core/modules/views_ui/src/ViewFormBase.php
@@ -105,7 +105,7 @@ public function getDisplayTabs(ViewUI $view) {
         '#link' => [
           'title' => $this->getDisplayLabel($view, $id),
           'localized_options' => [],
-          'url' => $view->urlInfo('edit-display-form')->setRouteParameter('display_id', $id),
+          'url' => $view->toUrl('edit-display-form')->setRouteParameter('display_id', $id),
         ],
       ];
       if (!empty($display['deleted'])) {
diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php
index c690aee99bf98341a098d1c58497c06b71d44799..a3e384098b487af4b2bd01aa6673ec3bd75a0b97 100644
--- a/core/modules/views_ui/src/ViewListBuilder.php
+++ b/core/modules/views_ui/src/ViewListBuilder.php
@@ -165,14 +165,14 @@ public function getDefaultOperations(EntityInterface $entity) {
       $operations['duplicate'] = [
         'title' => $this->t('Duplicate'),
         'weight' => 15,
-        'url' => $entity->urlInfo('duplicate-form'),
+        'url' => $entity->toUrl('duplicate-form'),
       ];
     }
 
     // Add AJAX functionality to enable/disable operations.
     foreach (['enable', 'disable'] as $op) {
       if (isset($operations[$op])) {
-        $operations[$op]['url'] = $entity->urlInfo($op);
+        $operations[$op]['url'] = $entity->toUrl($op);
         // Enable and disable operations should use AJAX.
         $operations[$op]['attributes']['class'][] = 'use-ajax';
       }
diff --git a/core/modules/views_ui/src/ViewPreviewForm.php b/core/modules/views_ui/src/ViewPreviewForm.php
index 787527baf8a9938b6591d8e259458352b736ccd8..1e5a2a66320a1bf89e2fc1319d4243b9748cb958 100644
--- a/core/modules/views_ui/src/ViewPreviewForm.php
+++ b/core/modules/views_ui/src/ViewPreviewForm.php
@@ -62,7 +62,7 @@ public function form(array $form, FormStateInterface $form_state) {
         'preview' => $view->renderPreview($this->displayID, $args),
       ];
     }
-    $uri = $view->urlInfo('preview-form');
+    $uri = $view->toUrl('preview-form');
     $uri->setRouteParameter('display_id', $this->displayID);
     $form['#action'] = $uri->toString();
 
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index de323bd277ae9f7d9a4f205ef160d37080013d19..cce4a8c8ed8fe2c00ac987a928cff08e9330b94b 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -264,7 +264,7 @@ public function standardCancel($form, FormStateInterface $form_state) {
       $this->cacheSet();
     }
 
-    $form_state->setRedirectUrl($this->urlInfo('edit-form'));
+    $form_state->setRedirectUrl($this->toUrl('edit-form'));
   }
 
   /**
@@ -993,7 +993,7 @@ public function save() {
    * {@inheritdoc}
    */
   public function urlInfo($rel = 'edit-form', array $options = []) {
-    return $this->storage->urlInfo($rel, $options);
+    return $this->storage->toUrl($rel, $options);
   }
 
   /**
diff --git a/core/modules/views_ui/tests/src/Functional/AreaEntityUITest.php b/core/modules/views_ui/tests/src/Functional/AreaEntityUITest.php
index d74575fd46324653b41b142a379822c81fa9f5d5..4e39164c308f7e72c34cb69d29ce6be5dcb895d4 100644
--- a/core/modules/views_ui/tests/src/Functional/AreaEntityUITest.php
+++ b/core/modules/views_ui/tests/src/Functional/AreaEntityUITest.php
@@ -31,7 +31,7 @@ public function testUI() {
     $id = $default['id'];
     $view = View::load($id);
 
-    $this->drupalGet($view->urlInfo('edit-form'));
+    $this->drupalGet($view->toUrl('edit-form'));
 
     // Add a global NULL argument to the view for testing argument placeholders.
     $this->drupalPostForm("admin/structure/views/nojs/add-handler/$id/page_1/argument", ['name[views.null]' => TRUE], 'Add and configure contextual filters');
diff --git a/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php b/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
index 79f36d914630cc19cecf61bee48da40b813b4a2c..e4b13188c514b9c5a6443324a29cbc6a031cb36b 100644
--- a/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
+++ b/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
@@ -136,7 +136,7 @@ public function testWorkspaceFormRevisions() {
     $this->assertEquals('1', $live_workspace->getRevisionId());
 
     // Re-save the live workspace via the UI to create revision 3.
-    $this->drupalPostForm($live_workspace->url('edit-form'), [], 'Save');
+    $this->drupalPostForm($live_workspace->toUrl('edit-form')->toString(), [], 'Save');
     $live_workspace = $storage->loadUnchanged('live');
     $this->assertEquals('3', $live_workspace->getRevisionId());
   }
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php
index d68a1f5ac8cef7c4b84f6e36cb15109c4f7e9b72..42a75b06e65858c0598623c5a93ffe46ff560259 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php
@@ -79,13 +79,13 @@ public function testHtmlRoutes() {
     ]);
     $entity->save();
 
-    $this->setRawContent($this->httpKernelHandle($entity->url()));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl()->toString()));
     $this->assertTitle('Test title | ');
 
-    $this->setRawContent($this->httpKernelHandle($entity->url('edit-form')));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl('edit-form')->toString()));
     $this->assertTitle('Edit Test title | ');
 
-    $this->setRawContent($this->httpKernelHandle($entity->url('delete-form')));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl('delete-form')->toString()));
     $this->assertTitle('Are you sure you want to delete the test entity - data table Test title? | ');
   }
 
@@ -122,13 +122,13 @@ public function testAdminHtmlRoutes() {
     ]);
     $entity->save();
 
-    $this->setRawContent($this->httpKernelHandle($entity->url()));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl()->toString()));
     $this->assertTitle('Test title | ');
 
-    $this->setRawContent($this->httpKernelHandle($entity->url('edit-form')));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl('edit-form')->toString()));
     $this->assertTitle('Edit Test title | ');
 
-    $this->setRawContent($this->httpKernelHandle($entity->url('delete-form')));
+    $this->setRawContent($this->httpKernelHandle($entity->toUrl('delete-form')->toString()));
     $this->assertTitle('Are you sure you want to delete the test entity - admin routes Test title? | ');
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/KeyValueStore/KeyValueContentEntityStorageTest.php b/core/tests/Drupal/KernelTests/Core/KeyValueStore/KeyValueContentEntityStorageTest.php
index 98850055a5fb92195d9c0d64ba96cb67fe92f743..b9feca82faf9479802d63ccb407f0838908c6809 100644
--- a/core/tests/Drupal/KernelTests/Core/KeyValueStore/KeyValueContentEntityStorageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/KeyValueStore/KeyValueContentEntityStorageTest.php
@@ -58,7 +58,7 @@ public function testCRUD() {
     $this->assertIdentical($empty->getEntityTypeId(), 'entity_test_label');
     // The URI can only be checked after saving.
     try {
-      $empty->urlInfo();
+      $empty->toUrl();
       $this->fail('EntityMalformedException was thrown.');
     }
     catch (EntityMalformedException $e) {
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php
index c606cb0d1b38a7a764d5763948d02c1bc5c09aa5..d42f1d28bbc2f71c7ac9eb3d4058d18f1b9559dd 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php
@@ -118,7 +118,7 @@ public function testLink($entity_label, $link_text, $expected_text, $link_rel =
       ->with($this->equalTo($expected_link))
       ->willReturn($expected);
 
-    $this->assertSame($expected, $entity->link($link_text, $link_rel, $link_options));
+    $this->assertSame($expected, $entity->toLink($link_text, $link_rel, $link_options)->toString());
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
index 335bf20a8da22d6109993e03f1b705d03b621597..700056349b507c8ef5b181db80e0639c02fba483 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
 use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\GeneratedUrl;
+use Drupal\Core\Link;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
@@ -386,6 +387,9 @@ public function providerTestToUrlUriCallback() {
    * @covers ::urlInfo
    *
    * @dataProvider providerTestUrlInfo
+   *
+   * @group legacy
+   * @expectedDeprecation EntityInterface::urlInfo() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. See https://www.drupal.org/node/2614344
    */
   public function testUrlInfo($rel, $options) {
     $entity = $this->getEntity(Entity::class, [], ['toUrl']);
@@ -396,6 +400,30 @@ public function testUrlInfo($rel, $options) {
     $entity->urlInfo($rel, $options);
   }
 
+  /**
+   * Tests the link() method.
+   *
+   * @covers ::urlInfo
+   *
+   * @group legacy
+   * @expectedDeprecation EntityInterface::link() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toLink() instead. Note, the default relationship for configuration entities changes from 'edit-form' to 'canonical'. See https://www.drupal.org/node/2614344
+   */
+  public function testLink() {
+
+    $link = $this->createMock(Link::class);
+    $link->expects($this->once())
+      ->method('toString')
+      ->willReturn('<a href="/foo">The link</a>');
+
+    $entity = $this->getEntity(Entity::class, [], ['toLink']);
+    $entity->expects($this->once())
+      ->method('toLink')
+      ->with(NULL, 'canonical')
+      ->willReturn($link);
+
+    $this->assertEquals('<a href="/foo">The link</a>', $entity->link());
+  }
+
   /**
    * Provides data for testUrlInfo().
    *
@@ -423,6 +451,9 @@ public function providerTestUrlInfo() {
    * @covers ::linkTemplates
    *
    * @dataProvider providerTestUrl
+   *
+   * @group legacy
+   * @expectedDeprecation EntityInterface::url() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. Note, a \Drupal\Core\Url object is returned. See https://www.drupal.org/node/2614344
    */
   public function testUrlEmpty($rel) {
     $entity = $this->getEntity(Entity::class, []);
@@ -461,6 +492,9 @@ public function providerTestUrlEmpty() {
    * @covers ::linkTemplates
    *
    * @dataProvider providerTestUrl
+   *
+   * @group legacy
+   * @expectedDeprecation EntityInterface::url() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. Note, a \Drupal\Core\Url object is returned. See https://www.drupal.org/node/2614344
    */
   public function testUrl($rel, $options, $default_options, $expected_options) {
     $entity = $this->getEntity(Entity::class, ['id' => $this->entityId], ['toUrl']);