From 7aa4a205a83bfbdba1131ee8a6463d5c029bb089 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Thu, 26 Jan 2023 13:32:33 -0700
Subject: [PATCH 1/7] rustdoc: merge doctest tooltip with notable traits
 tooltip

Fixes https://discord.com/channels/442252698964721669/443150878111694848/1066420140167680000

Fixes #91100
---
 src/librustdoc/html/highlight.rs              |  16 ++-
 src/librustdoc/html/render/mod.rs             |   2 +-
 src/librustdoc/html/static/css/rustdoc.css    |  54 ++------
 src/librustdoc/html/static/css/themes/ayu.css |   2 -
 .../html/static/css/themes/dark.css           |   2 -
 .../html/static/css/themes/light.css          |   2 -
 src/librustdoc/html/static/js/main.js         | 112 ++++++++-------
 tests/rustdoc-gui/codeblock-tooltip.goml      |  90 +++++-------
 tests/rustdoc-gui/notable-trait.goml          | 128 +++++++++---------
 tests/rustdoc/codeblock-title.rs              |   2 +-
 tests/rustdoc/doc-notable_trait.rs            |   6 +-
 tests/rustdoc/spotlight-from-dependency.rs    |   2 +-
 12 files changed, 184 insertions(+), 234 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 8a9e6caf611..11dd69487fb 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -96,13 +96,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to
     );
 
     if tooltip != Tooltip::None {
+        let edition_code;
         write!(
             out,
-            "<div class='tooltip'{}>ⓘ</div>",
-            if let Tooltip::Edition(edition_info) = tooltip {
-                format!(" data-edition=\"{}\"", edition_info)
-            } else {
-                String::new()
+            "<a href=\"#\" class=\"tooltip\" title=\"{}\">ⓘ</a>",
+            match tooltip {
+                Tooltip::Ignore => "This example is not tested",
+                Tooltip::CompileFail => "This example deliberately fails to compile",
+                Tooltip::ShouldPanic => "This example panics",
+                Tooltip::Edition(edition) => {
+                    edition_code = format!("This example runs with edition {edition}");
+                    &edition_code
+                }
+                Tooltip::None => unreachable!(),
             },
         );
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index d644293d3ef..5655246532c 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1313,7 +1313,7 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O
     if has_notable_trait {
         cx.types_with_notable_traits.insert(ty.clone());
         Some(format!(
-            " <a href=\"#\" class=\"notable-traits\" data-ty=\"{ty}\">ⓘ</a>",
+            " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
             ty = Escape(&format!("{:#}", ty.print(cx))),
         ))
     } else {
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index bf83ff2044e..437b8748c1d 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -697,8 +697,8 @@ h2.small-section-header > .anchor {
 .main-heading a:hover,
 .example-wrap > pre.rust a:hover,
 .all-items a:hover,
-.docblock a:not(.test-arrow):not(.scrape-help):hover,
-.docblock-short a:not(.test-arrow):not(.scrape-help):hover,
+.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
+.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
 .item-info a {
 	text-decoration: underline;
 }
@@ -1093,44 +1093,8 @@ pre.rust .doccomment {
 	display: block;
 	left: -25px;
 	top: 5px;
-}
-
-.example-wrap .tooltip:hover::after {
-	padding: 5px 3px 3px 3px;
-	border-radius: 6px;
-	margin-left: 5px;
-	font-size: 1rem;
-	border: 1px solid var(--border-color);
-	position: absolute;
-	width: max-content;
-	top: -2px;
-	z-index: 1;
-	background-color: var(--tooltip-background-color);
-	color: var(--tooltip-color);
-}
-
-.example-wrap .tooltip:hover::before {
-	content: " ";
-	position: absolute;
-	top: 50%;
-	left: 16px;
-	margin-top: -5px;
-	z-index: 1;
-	border: 5px solid transparent;
-	border-right-color: var(--tooltip-background-color);
-}
-
-.example-wrap.ignore .tooltip:hover::after {
-	content: "This example is not tested";
-}
-.example-wrap.compile_fail .tooltip:hover::after {
-	content: "This example deliberately fails to compile";
-}
-.example-wrap.should_panic .tooltip:hover::after {
-	content: "This example panics";
-}
-.example-wrap.edition .tooltip:hover::after {
-	content: "This code runs with edition " attr(data-edition);
+	margin: 0;
+	line-height: 1;
 }
 
 .example-wrap.compile_fail .tooltip,
@@ -1196,7 +1160,7 @@ a.test-arrow:hover {
 	border-right: 3px solid var(--target-border-color);
 }
 
-.notable-traits {
+.code-header a.tooltip {
 	color: inherit;
 	margin-right: 15px;
 	position: relative;
@@ -1205,7 +1169,7 @@ a.test-arrow:hover {
 /* placeholder thunk so that the mouse can easily travel from "(i)" to popover
 	the resulting "hover tunnel" is a stepped triangle, approximating
 	https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */
-.notable-traits:hover::after {
+a.tooltip:hover::after {
 	position: absolute;
 	top: calc(100% - 10px);
 	left: -15px;
@@ -1214,11 +1178,11 @@ a.test-arrow:hover {
 	content: "\00a0";
 }
 
-.notable .content {
+.popover.tooltip .content {
 	margin: 0.25em 0.5em;
 }
 
-.notable .content pre, .notable .content code {
+.popover.tooltip .content pre, .popover.tooltip .content code {
 	background: transparent;
 	margin: 0;
 	padding: 0;
@@ -1226,7 +1190,7 @@ a.test-arrow:hover {
 	white-space: pre-wrap;
 }
 
-.notable .content > h3:first-child {
+.popover.tooltip .content > h3:first-child {
 	margin: 0 0 5px 0;
 }
 
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index ed779bf6166..43fe676e68b 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -70,8 +70,6 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 	--test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
 	--target-background-color: rgba(255, 236, 164, 0.06);
 	--target-border-color: rgba(255, 180, 76, 0.85);
-	--tooltip-background-color: #314559;
-	--tooltip-color: #c5c5c5;
 	--kbd-color: #c5c5c5;
 	--kbd-background: #314559;
 	--kbd-box-shadow-color: #5c6773;
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 3766f0daa42..6777675f66f 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -65,8 +65,6 @@
 	--test-arrow-hover-background-color: #4e8bca;
 	--target-background-color: #494a3d;
 	--target-border-color: #bb7410;
-	--tooltip-background-color: #000;
-	--tooltip-color: #fff;
 	--kbd-color: #000;
 	--kbd-background: #fafbfc;
 	--kbd-box-shadow-color: #c6cbd1;
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index 8a7f6abcf8d..a7ee84b43f5 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -65,8 +65,6 @@
 	--test-arrow-hover-background-color: #4e8bca;
 	--target-background-color: #fdffd3;
 	--target-border-color: #ad7c37;
-	--tooltip-background-color: #000;
-	--tooltip-color: #fff;
 	--kbd-color: #000;
 	--kbd-background: #fafbfc;
 	--kbd-box-shadow-color: #c6cbd1;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 604ab147f6a..8d57ed98a87 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -379,7 +379,7 @@ function loadCss(cssUrl) {
         }
         ev.preventDefault();
         searchState.defocus();
-        window.hideAllModals(true); // true = reset focus for notable traits
+        window.hideAllModals(true); // true = reset focus for tooltips
     }
 
     function handleShortcut(ev) {
@@ -789,17 +789,17 @@ function loadCss(cssUrl) {
             // we need to switch away from mobile mode and make the main content area scrollable.
             hideSidebar();
         }
-        if (window.CURRENT_NOTABLE_ELEMENT) {
-            // As a workaround to the behavior of `contains: layout` used in doc togglers, the
-            // notable traits popup is positioned using javascript.
+        if (window.CURRENT_TOOLTIP_ELEMENT) {
+            // As a workaround to the behavior of `contains: layout` used in doc togglers,
+            // tooltip popovers are positioned using javascript.
             //
             // This means when the window is resized, we need to redo the layout.
-            const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE;
-            const force_visible = base.NOTABLE_FORCE_VISIBLE;
-            hideNotable(false);
+            const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;
+            const force_visible = base.TOOLTIP_FORCE_VISIBLE;
+            hideTooltip(false);
             if (force_visible) {
-                showNotable(base);
-                base.NOTABLE_FORCE_VISIBLE = true;
+                showTooltip(base);
+                base.TOOLTIP_FORCE_VISIBLE = true;
             }
         }
     });
@@ -827,27 +827,35 @@ function loadCss(cssUrl) {
         });
     });
 
-    function showNotable(e) {
-        if (!window.NOTABLE_TRAITS) {
+    function showTooltip(e) {
+        const notable_ty = e.getAttribute("data-notable-ty");
+        if (!window.NOTABLE_TRAITS && notable_ty) {
             const data = document.getElementById("notable-traits-data");
             if (data) {
                 window.NOTABLE_TRAITS = JSON.parse(data.innerText);
             } else {
-                throw new Error("showNotable() called on page without any notable traits!");
+                throw new Error("showTooltip() called with notable without any notable traits!");
             }
         }
-        if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) {
+        if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) {
             // Make this function idempotent.
             return;
         }
         window.hideAllModals(false);
-        const ty = e.getAttribute("data-ty");
         const wrapper = document.createElement("div");
-        wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>";
-        wrapper.className = "notable popover";
+        if (notable_ty) {
+            wrapper.innerHTML = "<div class=\"content\">" +
+                window.NOTABLE_TRAITS[notable_ty] + "</div>";
+        } else if (e.getAttribute("title") !== undefined) {
+            const titleContent = document.createElement("div");
+            titleContent.className = "content";
+            titleContent.appendChild(document.createTextNode(e.getAttribute("title")));
+            wrapper.appendChild(titleContent);
+        }
+        wrapper.className = "tooltip popover";
         const focusCatcher = document.createElement("div");
         focusCatcher.setAttribute("tabindex", "0");
-        focusCatcher.onfocus = hideNotable;
+        focusCatcher.onfocus = hideTooltip;
         wrapper.appendChild(focusCatcher);
         const pos = e.getBoundingClientRect();
         // 5px overlap so that the mouse can easily travel from place to place
@@ -869,62 +877,62 @@ function loadCss(cssUrl) {
             );
         }
         wrapper.style.visibility = "";
-        window.CURRENT_NOTABLE_ELEMENT = wrapper;
-        window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e;
+        window.CURRENT_TOOLTIP_ELEMENT = wrapper;
+        window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e;
         wrapper.onpointerleave = function(ev) {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
-                hideNotable(true);
+            if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
+                hideTooltip(true);
             }
         };
     }
 
-    function notableBlurHandler(event) {
-        if (window.CURRENT_NOTABLE_ELEMENT &&
-            !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT) &&
-            !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT) &&
-            !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) &&
-            !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE)
+    function tooltipBlurHandler(event) {
+        if (window.CURRENT_TOOLTIP_ELEMENT &&
+            !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT) &&
+            !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT) &&
+            !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) &&
+            !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE)
         ) {
             // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari.
-            // When I click the button on an already-opened notable trait popover, Safari
+            // When I click the button on an already-opened tooltip popover, Safari
             // hides the popover and then immediately shows it again, while everyone else hides it
             // and it stays hidden.
             //
             // To work around this, make sure the click finishes being dispatched before
-            // hiding the popover. Since `hideNotable()` is idempotent, this makes Safari behave
+            // hiding the popover. Since `hideTooltip()` is idempotent, this makes Safari behave
             // consistently with the other two.
-            setTimeout(() => hideNotable(false), 0);
+            setTimeout(() => hideTooltip(false), 0);
         }
     }
 
-    function hideNotable(focus) {
-        if (window.CURRENT_NOTABLE_ELEMENT) {
-            if (window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE) {
+    function hideTooltip(focus) {
+        if (window.CURRENT_TOOLTIP_ELEMENT) {
+            if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) {
                 if (focus) {
-                    window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.focus();
+                    window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus();
                 }
-                window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false;
+                window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false;
             }
             const body = document.getElementsByTagName("body")[0];
-            body.removeChild(window.CURRENT_NOTABLE_ELEMENT);
-            window.CURRENT_NOTABLE_ELEMENT = null;
+            body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);
+            window.CURRENT_TOOLTIP_ELEMENT = null;
         }
     }
 
-    onEachLazy(document.getElementsByClassName("notable-traits"), e => {
+    onEachLazy(document.getElementsByClassName("tooltip"), e => {
         e.onclick = function() {
-            this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true;
-            if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) {
-                hideNotable(true);
+            this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true;
+            if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) {
+                hideTooltip(true);
             } else {
-                showNotable(this);
-                window.CURRENT_NOTABLE_ELEMENT.setAttribute("tabindex", "0");
-                window.CURRENT_NOTABLE_ELEMENT.focus();
-                window.CURRENT_NOTABLE_ELEMENT.onblur = notableBlurHandler;
+                showTooltip(this);
+                window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0");
+                window.CURRENT_TOOLTIP_ELEMENT.focus();
+                window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler;
             }
             return false;
         };
@@ -933,16 +941,16 @@ function loadCss(cssUrl) {
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            showNotable(this);
+            showTooltip(this);
         };
         e.onpointerleave = function(ev) {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            if (!this.NOTABLE_FORCE_VISIBLE &&
-                !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
-                hideNotable(true);
+            if (!this.TOOLTIP_FORCE_VISIBLE &&
+                !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) {
+                hideTooltip(true);
             }
         };
     });
@@ -1044,14 +1052,14 @@ function loadCss(cssUrl) {
     }
 
     /**
-     * Hide popover menus, notable trait tooltips, and the sidebar (if applicable).
+     * Hide popover menus, clickable tooltips, and the sidebar (if applicable).
      *
-     * Pass "true" to reset focus for notable traits.
+     * Pass "true" to reset focus for tooltip popovers.
      */
     window.hideAllModals = function(switchFocus) {
         hideSidebar();
         window.hidePopoverMenus();
-        hideNotable(switchFocus);
+        hideTooltip(switchFocus);
     };
 
     /**
diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml
index a3ef4e77b54..36b67073a03 100644
--- a/tests/rustdoc-gui/codeblock-tooltip.goml
+++ b/tests/rustdoc-gui/codeblock-tooltip.goml
@@ -30,24 +30,16 @@ define-function: (
             ".docblock .example-wrap.compile_fail",
             {"border-left": "2px solid rgb(255, 0, 0)"},
         )
-        assert-css: (
-            ".docblock .example-wrap.compile_fail .tooltip::after",
-            {
-                "content": '"This example deliberately fails to compile"',
-                "padding": "5px 3px 3px",
-                "background-color": |background|,
-                "color": |color|,
-                "border": "1px solid " + |border|,
-            },
-        )
-        assert-css: (
-            ".docblock .example-wrap.compile_fail .tooltip::before",
-            {
-                "border-width": "5px",
-                "border-style": "solid",
-                "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
-            },
+        click: ".docblock .example-wrap.compile_fail .tooltip"
+        assert-text: (
+            ".popover.tooltip",
+            "This example deliberately fails to compile"
         )
+        assert-css: (".popover.tooltip", {
+            "color": |color|,
+            "background-color": |background|,
+            "border-color": |border|,
+        })
 
         // should_panic block
         assert-css: (
@@ -69,24 +61,16 @@ define-function: (
             ".docblock .example-wrap.should_panic",
             {"border-left": "2px solid rgb(255, 0, 0)"},
         )
-        assert-css: (
-            ".docblock .example-wrap.should_panic .tooltip::after",
-            {
-                "content": '"This example panics"',
-                "padding": "5px 3px 3px",
-                "background-color": |background|,
-                "color": |color|,
-                "border": "1px solid " + |border|,
-            },
-        )
-        assert-css: (
-            ".docblock .example-wrap.should_panic .tooltip::before",
-            {
-                "border-width": "5px",
-                "border-style": "solid",
-                "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
-            },
+        click: ".docblock .example-wrap.should_panic .tooltip"
+        assert-text: (
+            ".popover.tooltip",
+            "This example panics"
         )
+        assert-css: (".popover.tooltip", {
+            "color": |color|,
+            "background-color": |background|,
+            "border-color": |border|,
+        })
 
         // ignore block
         assert-css: (
@@ -108,42 +92,36 @@ define-function: (
             ".docblock .example-wrap.ignore",
             {"border-left": "2px solid rgb(255, 142, 0)"},
         )
-        assert-css: (
-            ".docblock .example-wrap.ignore .tooltip::after",
-            {
-                "content": '"This example is not tested"',
-                "padding": "5px 3px 3px",
-                "background-color": |background|,
-                "color": |color|,
-                "border": "1px solid " + |border|,
-            },
-        )
-        assert-css: (
-            ".docblock .example-wrap.ignore .tooltip::before",
-            {
-                "border-width": "5px",
-                "border-style": "solid",
-                "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
-            },
+        click: ".docblock .example-wrap.ignore .tooltip"
+        assert-text: (
+            ".popover.tooltip",
+            "This example is not tested"
         )
+        assert-css: (".popover.tooltip", {
+            "color": |color|,
+            "background-color": |background|,
+            "border-color": |border|,
+        })
+        click: ".docblock .example-wrap.ignore .tooltip"
+        assert-false: ".popover.tooltip"
     },
 )
 
 call-function: ("check-colors", {
     "theme": "ayu",
-    "background": "rgb(49, 69, 89)",
+    "background": "rgb(15, 20, 25)",
     "color": "rgb(197, 197, 197)",
     "border": "rgb(92, 103, 115)",
 })
 call-function: ("check-colors", {
     "theme": "dark",
-    "background": "rgb(0, 0, 0)",
-    "color": "rgb(255, 255, 255)",
+    "background": "rgb(53, 53, 53)",
+    "color": "rgb(221, 221, 221)",
     "border": "rgb(224, 224, 224)",
 })
 call-function: ("check-colors", {
     "theme": "light",
-    "background": "rgb(0, 0, 0)",
-    "color": "rgb(255, 255, 255)",
+    "background": "rgb(255, 255, 255)",
+    "color": "rgb(0, 0, 0)",
     "border": "rgb(224, 224, 224)",
 })
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
index b4fa7d0dbf0..20728915199 100644
--- a/tests/rustdoc-gui/notable-trait.goml
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -6,13 +6,13 @@ size: (1100, 600)
 // Checking they have the same y position.
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("y"),
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("x"),
 )
 // The `i` should be *after* the type.
@@ -21,33 +21,33 @@ assert-position: (
     {"x": 677},
 )
 assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     {"x": 955},
 )
 // The tooltip should be below the `i`
 // Also, clicking the tooltip should bring its text into the DOM
-assert-count: ("//*[@class='notable popover']", 0)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+assert-count: ("//*[@class='tooltip popover']", 0)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 compare-elements-position-near: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@class='notable popover']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+    "//*[@class='tooltip popover']",
     {"y": 30}
 )
 compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@class='notable popover']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+    "//*[@class='tooltip popover']",
     ("x")
 )
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
 move-cursor-to: "//h1"
-assert-count: ("//*[@class='notable popover']", 0)
+assert-count: ("//*[@class='tooltip popover']", 0)
 
 // Now only the `i` should be on the next line.
 size: (1055, 600)
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("y", "x"),
 )
 
@@ -56,13 +56,13 @@ size: (980, 600)
 // Checking they have the same y position.
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("y"),
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("x"),
 )
 // The `i` should be *after* the type.
@@ -71,7 +71,7 @@ assert-position: (
     {"x": 245},
 )
 assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     {"x": 523},
 )
 
@@ -80,13 +80,13 @@ size: (650, 600)
 // Checking they have the same y position.
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("y"),
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     ("x"),
 )
 // The `i` should be *after* the type.
@@ -95,29 +95,29 @@ assert-position: (
     {"x": 15},
 )
 assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     {"x": 293},
 )
 // The tooltip should STILL be below `i`
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 compare-elements-position-near: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@class='notable popover']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+    "//*[@class='tooltip popover']",
     {"y": 30}
 )
 compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@class='notable popover']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+    "//*[@class='tooltip popover']",
     ("x")
 )
 assert-position: (
-    "//*[@class='notable popover']",
+    "//*[@class='tooltip popover']",
     {"x": 0}
 )
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
 move-cursor-to: "//h1"
-assert-count: ("//*[@class='notable popover']", 0)
+assert-count: ("//*[@class='tooltip popover']", 0)
 
 // Now check the colors.
 define-function: (
@@ -133,26 +133,26 @@ define-function: (
         // We reload the page so the local storage settings are being used.
         reload:
 
-        move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-        assert-count: (".notable.popover", 1)
+        move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+        assert-count: (".tooltip.popover", 1)
 
         assert-css: (
-             ".notable.popover h3",
+             ".tooltip.popover h3",
              {"color": |header_color|},
              ALL,
         )
         assert-css: (
-             ".notable.popover pre",
+             ".tooltip.popover pre",
              {"color": |content_color|},
              ALL,
         )
         assert-css: (
-             ".notable.popover pre a.struct",
+             ".tooltip.popover pre a.struct",
              {"color": |type_color|},
              ALL,
         )
         assert-css: (
-             ".notable.popover pre a.trait",
+             ".tooltip.popover pre a.trait",
              {"color": |trait_color|},
              ALL,
         )
@@ -195,24 +195,24 @@ call-function: (
 reload:
 
 // Check that pressing escape works
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-move-cursor-to: "//*[@class='notable popover']"
-assert-count: ("//*[@class='notable popover']", 1)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+move-cursor-to: "//*[@class='tooltip popover']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 press-key: "Escape"
-assert-count: ("//*[@class='notable popover']", 0)
-assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
+assert-count: ("//*[@class='tooltip popover']", 0)
+assert: "#method\.create_an_iterator_from_read .tooltip:focus"
 
 // Check that clicking outside works.
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 click: ".search-input"
-assert-count: ("//*[@class='notable popover']", 0)
-assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
+assert-count: ("//*[@class='tooltip popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
 
 // Check that pressing tab over and over works.
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-move-cursor-to: "//*[@class='notable popover']"
-assert-count: ("//*[@class='notable popover']", 1)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+move-cursor-to: "//*[@class='tooltip popover']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 press-key: "Tab"
 press-key: "Tab"
 press-key: "Tab"
@@ -220,8 +220,8 @@ press-key: "Tab"
 press-key: "Tab"
 press-key: "Tab"
 press-key: "Tab"
-assert-count: ("//*[@class='notable popover']", 0)
-assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
+assert-count: ("//*[@class='tooltip popover']", 0)
+assert: "#method\.create_an_iterator_from_read .tooltip:focus"
 
 // Now we check that the focus isn't given back to the wrong item when opening
 // another popover.
@@ -231,8 +231,8 @@ click: "#method\.create_an_iterator_from_read .fn"
 assert-window-property-false: {"scrollY": |scroll|}
 // Store the new position.
 store-window-property: (scroll, "scrollY")
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-wait-for: "//*[@class='notable popover']"
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+wait-for: "//*[@class='tooltip popover']"
 click: "#settings-menu a"
 click: ".search-input"
 // We ensure we didn't come back to the previous focused item.
@@ -245,8 +245,8 @@ click: "#method\.create_an_iterator_from_read .fn"
 assert-window-property-false: {"scrollY": |scroll|}
 // Store the new position.
 store-window-property: (scroll, "scrollY")
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-wait-for: "//*[@class='notable popover']"
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+wait-for: "//*[@class='tooltip popover']"
 click: "#settings-menu a"
 press-key: "Escape"
 // We ensure we didn't come back to the previous focused item.
@@ -254,23 +254,23 @@ assert-window-property-false: {"scrollY": |scroll|}
 
 // Opening the mobile sidebar should close the popover.
 size: (650, 600)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 click: ".sidebar-menu-toggle"
 assert: "//*[@class='sidebar shown']"
-assert-count: ("//*[@class='notable popover']", 0)
-assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
-// Clicking a notable popover should close the sidebar.
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+assert-count: ("//*[@class='tooltip popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
+// Clicking a notable trait tooltip popover should close the sidebar.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 assert-false: "//*[@class='sidebar shown']"
 
 // Also check the focus handling for the help button.
 size: (1100, 600)
 reload:
-assert-count: ("//*[@class='notable popover']", 0)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
-assert-count: ("//*[@class='notable popover']", 1)
+assert-count: ("//*[@class='tooltip popover']", 0)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+assert-count: ("//*[@class='tooltip popover']", 1)
 click: "#help-button a"
-assert-count: ("//*[@class='notable popover']", 0)
-assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
+assert-count: ("//*[@class='tooltip popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
diff --git a/tests/rustdoc/codeblock-title.rs b/tests/rustdoc/codeblock-title.rs
index b9b0b0d1abf..761afb8bd08 100644
--- a/tests/rustdoc/codeblock-title.rs
+++ b/tests/rustdoc/codeblock-title.rs
@@ -3,7 +3,7 @@
 // @has foo/fn.bar.html '//*[@class="example-wrap compile_fail"]/*[@class="tooltip"]' "ⓘ"
 // @has foo/fn.bar.html '//*[@class="example-wrap ignore"]/*[@class="tooltip"]' "ⓘ"
 // @has foo/fn.bar.html '//*[@class="example-wrap should_panic"]/*[@class="tooltip"]' "ⓘ"
-// @has foo/fn.bar.html '//*[@data-edition="2018"]' "ⓘ"
+// @has foo/fn.bar.html '//*[@title="This example runs with edition 2018"]' "ⓘ"
 
 /// foo
 ///
diff --git a/tests/rustdoc/doc-notable_trait.rs b/tests/rustdoc/doc-notable_trait.rs
index 279faf55401..d8941769fa6 100644
--- a/tests/rustdoc/doc-notable_trait.rs
+++ b/tests/rustdoc/doc-notable_trait.rs
@@ -9,7 +9,7 @@ impl<T: SomeTrait> SomeTrait for Wrapper<T> {}
 #[doc(notable_trait)]
 pub trait SomeTrait {
     // @has doc_notable_trait/trait.SomeTrait.html
-    // @has - '//a[@class="notable-traits"]/@data-ty' 'Wrapper<Self>'
+    // @has - '//a[@class="tooltip"]/@data-notable-ty' 'Wrapper<Self>'
     // @snapshot wrap-me - '//script[@id="notable-traits-data"]'
     fn wrap_me(self) -> Wrapper<Self> where Self: Sized {
         Wrapper {
@@ -23,7 +23,7 @@ impl SomeTrait for SomeStruct {}
 
 impl SomeStruct {
     // @has doc_notable_trait/struct.SomeStruct.html
-    // @has - '//a[@class="notable-traits"]/@data-ty' 'SomeStruct'
+    // @has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct'
     // @snapshot some-struct-new - '//script[@id="notable-traits-data"]'
     pub fn new() -> SomeStruct {
         SomeStruct
@@ -31,7 +31,7 @@ impl SomeStruct {
 }
 
 // @has doc_notable_trait/fn.bare_fn.html
-// @has - '//a[@class="notable-traits"]/@data-ty' 'SomeStruct'
+// @has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct'
 // @snapshot bare-fn - '//script[@id="notable-traits-data"]'
 pub fn bare_fn() -> SomeStruct {
     SomeStruct
diff --git a/tests/rustdoc/spotlight-from-dependency.rs b/tests/rustdoc/spotlight-from-dependency.rs
index 090ad187d9c..426759c7bf8 100644
--- a/tests/rustdoc/spotlight-from-dependency.rs
+++ b/tests/rustdoc/spotlight-from-dependency.rs
@@ -3,7 +3,7 @@
 use std::iter::Iterator;
 
 // @has foo/struct.Odd.html
-// @has - '//*[@id="method.new"]//a[@class="notable-traits"]/@data-ty' 'Odd'
+// @has - '//*[@id="method.new"]//a[@class="tooltip"]/@data-notable-ty' 'Odd'
 // @snapshot odd - '//script[@id="notable-traits-data"]'
 pub struct Odd {
     current: usize,

From a576514e13d5608ab48847958c2b97ab5ab01d85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Thu, 9 Feb 2023 10:16:00 +0000
Subject: [PATCH 2/7] Introduce `-Zterminal-urls` to use OSC8 for error codes

Terminals supporting the OSC8 Hyperlink Extension can support inline
anchors where the text is user defineable but clicking on it opens a
browser to a specified URLs, just like `<a href="URL">` does in HTML.

https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
---
 compiler/rustc_driver_impl/src/lib.rs          |  3 ++-
 compiler/rustc_errors/src/emitter.rs           | 17 +++++++++++++++--
 compiler/rustc_errors/src/json.rs              |  9 +++++++++
 compiler/rustc_errors/src/json/tests.rs        |  3 ++-
 compiler/rustc_errors/src/lib.rs               |  8 ++++++++
 compiler/rustc_expand/src/tests.rs             |  3 ++-
 compiler/rustc_session/src/options.rs          | 16 +++++++++++++++-
 compiler/rustc_session/src/session.rs          | 18 ++++++++++++++++++
 src/librustdoc/core.rs                         |  3 +++
 src/librustdoc/doctest.rs                      |  5 ++++-
 src/tools/clippy/clippy_lints/src/doc.rs       |  3 ++-
 src/tools/clippy/src/driver.rs                 |  1 +
 src/tools/rustfmt/src/parse/session.rs         |  3 ++-
 tests/rustdoc-ui/z-help.stdout                 |  1 +
 tests/ui/diagnostic-flags/terminal_urls.rs     |  4 ++++
 tests/ui/diagnostic-flags/terminal_urls.stderr | 11 +++++++++++
 16 files changed, 99 insertions(+), 9 deletions(-)
 create mode 100644 tests/ui/diagnostic-flags/terminal_urls.rs
 create mode 100644 tests/ui/diagnostic-flags/terminal_urls.stderr

diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index a392d70f100..290652f4515 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -23,7 +23,7 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
-use rustc_errors::{ErrorGuaranteed, PResult};
+use rustc_errors::{ErrorGuaranteed, PResult, TerminalUrl};
 use rustc_feature::find_gated_cfg;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
@@ -1192,6 +1192,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
         None,
         false,
         false,
+        TerminalUrl::No,
     ));
     let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
 
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 9768526a2f4..ac8f40987e8 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -18,7 +18,7 @@ use crate::translation::{to_fluent_args, Translate};
 use crate::{
     diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
     FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
-    SubstitutionHighlight, SuggestionStyle,
+    SubstitutionHighlight, SuggestionStyle, TerminalUrl,
 };
 use rustc_lint_defs::pluralize;
 
@@ -66,6 +66,7 @@ impl HumanReadableErrorType {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> EmitterWriter {
         let (short, color_config) = self.unzip();
         let color = color_config.suggests_using_colors();
@@ -80,6 +81,7 @@ impl HumanReadableErrorType {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         )
     }
 }
@@ -652,6 +654,7 @@ pub struct EmitterWriter {
 
     macro_backtrace: bool,
     track_diagnostics: bool,
+    terminal_url: TerminalUrl,
 }
 
 #[derive(Debug)]
@@ -672,6 +675,7 @@ impl EmitterWriter {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> EmitterWriter {
         let dst = Destination::from_stderr(color_config);
         EmitterWriter {
@@ -685,6 +689,7 @@ impl EmitterWriter {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         }
     }
 
@@ -699,6 +704,7 @@ impl EmitterWriter {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> EmitterWriter {
         EmitterWriter {
             dst: Raw(dst, colored),
@@ -711,6 +717,7 @@ impl EmitterWriter {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         }
     }
 
@@ -1378,7 +1385,13 @@ impl EmitterWriter {
             // only render error codes, not lint codes
             if let Some(DiagnosticId::Error(ref code)) = *code {
                 buffer.append(0, "[", Style::Level(*level));
-                buffer.append(0, code, Style::Level(*level));
+                let code = if let TerminalUrl::Yes = self.terminal_url {
+                    let path = "https://doc.rust-lang.org/error_codes";
+                    format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07")
+                } else {
+                    code.clone()
+                };
+                buffer.append(0, &code, Style::Level(*level));
                 buffer.append(0, "]", Style::Level(*level));
                 label_width += 2 + code.len();
             }
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index dc38b8725ad..e475fc725c3 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -17,6 +17,7 @@ use crate::translation::{to_fluent_args, Translate};
 use crate::DiagnosticId;
 use crate::{
     CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
+    TerminalUrl,
 };
 use rustc_lint_defs::Applicability;
 
@@ -47,6 +48,7 @@ pub struct JsonEmitter {
     diagnostic_width: Option<usize>,
     macro_backtrace: bool,
     track_diagnostics: bool,
+    terminal_url: TerminalUrl,
 }
 
 impl JsonEmitter {
@@ -60,6 +62,7 @@ impl JsonEmitter {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> JsonEmitter {
         JsonEmitter {
             dst: Box::new(io::BufWriter::new(io::stderr())),
@@ -73,6 +76,7 @@ impl JsonEmitter {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         }
     }
 
@@ -84,6 +88,7 @@ impl JsonEmitter {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> JsonEmitter {
         let file_path_mapping = FilePathMapping::empty();
         JsonEmitter::stderr(
@@ -96,6 +101,7 @@ impl JsonEmitter {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         )
     }
 
@@ -110,6 +116,7 @@ impl JsonEmitter {
         diagnostic_width: Option<usize>,
         macro_backtrace: bool,
         track_diagnostics: bool,
+        terminal_url: TerminalUrl,
     ) -> JsonEmitter {
         JsonEmitter {
             dst,
@@ -123,6 +130,7 @@ impl JsonEmitter {
             diagnostic_width,
             macro_backtrace,
             track_diagnostics,
+            terminal_url,
         }
     }
 
@@ -360,6 +368,7 @@ impl Diagnostic {
                 je.diagnostic_width,
                 je.macro_backtrace,
                 je.track_diagnostics,
+                je.terminal_url,
             )
             .ui_testing(je.ui_testing)
             .emit_diagnostic(diag);
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index f131468971b..f161532d3b7 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -4,7 +4,7 @@ use crate::json::JsonEmitter;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
 
 use crate::emitter::{ColorConfig, HumanReadableErrorType};
-use crate::Handler;
+use crate::{Handler, TerminalUrl};
 use rustc_span::{BytePos, Span};
 
 use std::str;
@@ -60,6 +60,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
             None,
             false,
             false,
+            TerminalUrl::No,
         );
 
         let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index ec04e865d53..83b733d4c06 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -573,6 +573,7 @@ impl Handler {
             None,
             flags.macro_backtrace,
             flags.track_diagnostics,
+            TerminalUrl::No,
         ));
         Self::with_emitter_and_flags(emitter, flags)
     }
@@ -1838,6 +1839,13 @@ pub fn add_elided_lifetime_in_path_suggestion(
     );
 }
 
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum TerminalUrl {
+    No,
+    Yes,
+    Auto,
+}
+
 /// Useful type to use with `Result<>` indicate that an error has already
 /// been reported to the user, so no need to continue checking.
 #[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)]
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index 8f3bea29ffd..f80141403bf 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -8,7 +8,7 @@ use rustc_span::{BytePos, Span};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Handler, MultiSpan, PResult};
+use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl};
 
 use std::io;
 use std::io::prelude::*;
@@ -152,6 +152,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
             None,
             false,
             false,
+            TerminalUrl::No,
         );
         let handler = Handler::with_emitter(true, None, Box::new(emitter));
         #[allow(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 0db4d85ff4b..7bef89a3704 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -4,7 +4,7 @@ use crate::early_error;
 use crate::lint;
 use crate::search_paths::SearchPath;
 use crate::utils::NativeLib;
-use rustc_errors::LanguageIdentifier;
+use rustc_errors::{LanguageIdentifier, TerminalUrl};
 use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
 use rustc_target::spec::{
     RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
@@ -399,6 +399,8 @@ mod desc {
     pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
     pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)";
     pub const parse_target_feature: &str = parse_string;
+    pub const parse_terminal_url: &str =
+        "either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`";
     pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
     pub const parse_split_debuginfo: &str =
         "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)";
@@ -979,6 +981,16 @@ mod parse {
         true
     }
 
+    pub(crate) fn parse_terminal_url(slot: &mut TerminalUrl, v: Option<&str>) -> bool {
+        *slot = match v {
+            Some("on" | "" | "yes" | "y") | None => TerminalUrl::Yes,
+            Some("off" | "no" | "n") => TerminalUrl::No,
+            Some("auto") => TerminalUrl::Auto,
+            _ => return false,
+        };
+        true
+    }
+
     pub(crate) fn parse_symbol_mangling_version(
         slot: &mut Option<SymbolManglingVersion>,
         v: Option<&str>,
@@ -1602,6 +1614,8 @@ options! {
         "show extended diagnostic help (default: no)"),
     temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "the directory the intermediate files are written to"),
+    terminal_urls: TerminalUrl = (TerminalUrl::No, parse_terminal_url, [UNTRACKED],
+        "use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output"),
     #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")]
     thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable ThinLTO when possible"),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 8a0176f6391..54b68b8341f 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -24,6 +24,7 @@ use rustc_errors::registry::Registry;
 use rustc_errors::{
     error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
     ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
+    TerminalUrl,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -1273,6 +1274,19 @@ fn default_emitter(
 ) -> Box<dyn Emitter + sync::Send> {
     let macro_backtrace = sopts.unstable_opts.macro_backtrace;
     let track_diagnostics = sopts.unstable_opts.track_diagnostics;
+    let terminal_url = match sopts.unstable_opts.terminal_urls {
+        TerminalUrl::Auto => {
+            match (std::env::var("COLORTERM").as_deref(), std::env::var("TERM").as_deref()) {
+                (Ok("truecolor"), Ok("xterm-256color"))
+                    if sopts.unstable_features.is_nightly_build() =>
+                {
+                    TerminalUrl::Yes
+                }
+                _ => TerminalUrl::No,
+            }
+        }
+        t => t,
+    };
     match sopts.error_format {
         config::ErrorOutputType::HumanReadable(kind) => {
             let (short, color_config) = kind.unzip();
@@ -1297,6 +1311,7 @@ fn default_emitter(
                     sopts.diagnostic_width,
                     macro_backtrace,
                     track_diagnostics,
+                    terminal_url,
                 );
                 Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             }
@@ -1312,6 +1327,7 @@ fn default_emitter(
                 sopts.diagnostic_width,
                 macro_backtrace,
                 track_diagnostics,
+                terminal_url,
             )
             .ui_testing(sopts.unstable_opts.ui_testing),
         ),
@@ -1624,6 +1640,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
                 None,
                 false,
                 false,
+                TerminalUrl::No,
             ))
         }
         config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic(
@@ -1634,6 +1651,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
             None,
             false,
             false,
+            TerminalUrl::No,
         )),
     };
     rustc_errors::Handler::with_emitter(true, None, emitter)
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 0ce43f7db8e..05b2a5334f1 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -4,6 +4,7 @@ use rustc_data_structures::sync::{self, Lrc};
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::emitter::{Emitter, EmitterWriter};
 use rustc_errors::json::JsonEmitter;
+use rustc_errors::TerminalUrl;
 use rustc_feature::UnstableFeatures;
 use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@@ -164,6 +165,7 @@ pub(crate) fn new_handler(
                     diagnostic_width,
                     false,
                     unstable_opts.track_diagnostics,
+                    TerminalUrl::No,
                 )
                 .ui_testing(unstable_opts.ui_testing),
             )
@@ -183,6 +185,7 @@ pub(crate) fn new_handler(
                     diagnostic_width,
                     false,
                     unstable_opts.track_diagnostics,
+                    TerminalUrl::No,
                 )
                 .ui_testing(unstable_opts.ui_testing),
             )
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 37a1005cba1..57c41b57311 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,7 +1,7 @@
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
+use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
 use rustc_interface::interface;
@@ -557,6 +557,7 @@ pub(crate) fn make_test(
                 Some(80),
                 false,
                 false,
+                TerminalUrl::No,
             )
             .supports_color();
 
@@ -571,6 +572,7 @@ pub(crate) fn make_test(
                 None,
                 false,
                 false,
+                TerminalUrl::No,
             );
 
             // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
@@ -756,6 +758,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
                 None,
                 false,
                 false,
+                TerminalUrl::No,
             );
 
             let handler = Handler::with_emitter(false, None, Box::new(emitter));
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index 127201b72e2..0b31e20fc87 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler, SuggestionStyle};
+use rustc_errors::{Applicability, Handler, SuggestionStyle, TerminalUrl};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{AnonConst, Expr};
@@ -717,6 +717,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                     None,
                     false,
                     false,
+                    TerminalUrl::No,
                 );
                 let handler = Handler::with_emitter(false, None, Box::new(emitter));
                 let sess = ParseSess::with_span_handler(handler, sm);
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index d521e8d8839..e45835efe74 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -220,6 +220,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
         None,
         false,
         false,
+        rustc_errors::TerminalUrl::No,
     ));
     let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
 
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 6bfec79cd70..9014026b0aa 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use rustc_data_structures::sync::{Lrc, Send};
 use rustc_errors::emitter::{Emitter, EmitterWriter};
 use rustc_errors::translation::Translate;
-use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
+use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl};
 use rustc_session::parse::ParseSess as RawParseSess;
 use rustc_span::{
     source_map::{FilePathMapping, SourceMap},
@@ -135,6 +135,7 @@ fn default_handler(
             None,
             false,
             false,
+            TerminalUrl::No,
         ))
     };
     Handler::with_emitter(
diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout
index 4f07fca82d1..3bb975f2882 100644
--- a/tests/rustdoc-ui/z-help.stdout
+++ b/tests/rustdoc-ui/z-help.stdout
@@ -168,6 +168,7 @@
     -Z                 symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0')
     -Z                                   teach=val -- show extended diagnostic help (default: no)
     -Z                               temps-dir=val -- the directory the intermediate files are written to
+    -Z                           terminal-urls=val -- use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output
     -Z                                 thinlto=val -- enable ThinLTO when possible
     -Z                           thir-unsafeck=val -- use the THIR unsafety checker (default: no)
     -Z                                 threads=val -- use a thread pool with N threads
diff --git a/tests/ui/diagnostic-flags/terminal_urls.rs b/tests/ui/diagnostic-flags/terminal_urls.rs
new file mode 100644
index 00000000000..1f04e2aade1
--- /dev/null
+++ b/tests/ui/diagnostic-flags/terminal_urls.rs
@@ -0,0 +1,4 @@
+// compile-flags: -Zterminal-urls=yes
+fn main() {
+    let () = 4; //~ ERROR
+}
diff --git a/tests/ui/diagnostic-flags/terminal_urls.stderr b/tests/ui/diagnostic-flags/terminal_urls.stderr
new file mode 100644
index 00000000000..7f7e69c5d5d
--- /dev/null
+++ b/tests/ui/diagnostic-flags/terminal_urls.stderr
@@ -0,0 +1,11 @@
+error[]8;;https://doc.rust-lang.org/error_codes/E0308.htmlE0308]8;;]: mismatched types
+  --> $DIR/terminal_urls.rs:3:9
+   |
+LL |     let () = 4;
+   |         ^^   - this expression has type `{integer}`
+   |         |
+   |         expected integer, found `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.

From 14033108cd34f08d92b28ff0ae2eafb2d81bf8b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Sat, 11 Feb 2023 10:31:09 +0100
Subject: [PATCH 3/7] Print disk usage in PGO CI script

---
 src/ci/stage-build.py | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py
index 662c9e36694..4e6bcba5e20 100644
--- a/src/ci/stage-build.py
+++ b/src/ci/stage-build.py
@@ -211,7 +211,8 @@ Duration = float
 TimerSection = Union[Duration, "Timer"]
 
 
-def iterate_sections(section: TimerSection, name: str, level: int = 0) -> Iterator[Tuple[int, str, Duration]]:
+def iterate_sections(section: TimerSection, name: str, level: int = 0) -> Iterator[
+    Tuple[int, str, Duration]]:
     """
     Hierarchically iterate the sections of a timer, in a depth-first order.
     """
@@ -239,7 +240,7 @@ class Timer:
         start = get_timestamp()
         exc = None
 
-        child_timer = Timer(parent_names=self.parent_names + (name, ))
+        child_timer = Timer(parent_names=self.parent_names + (name,))
         full_name = " > ".join(child_timer.parent_names)
         try:
             LOGGER.info(f"Section `{full_name}` starts")
@@ -648,6 +649,16 @@ def print_binary_sizes(pipeline: Pipeline):
         LOGGER.info(f"Rustc binary size\n{output.getvalue()}")
 
 
+def print_free_disk_space(pipeline: Pipeline):
+    usage = shutil.disk_usage(pipeline.opt_artifacts())
+    total = usage.total
+    used = usage.used
+    free = usage.free
+
+    logging.info(
+        f"Free disk space: {format_bytes(free)} out of total {format_bytes(total)} ({(used / total) * 100:.2f}% used)")
+
+
 def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: List[str]):
     # Clear and prepare tmp directory
     shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True)
@@ -666,6 +677,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L
 
         with stage1.section("Gather profiles"):
             gather_llvm_profiles(pipeline)
+        print_free_disk_space(pipeline)
 
     clear_llvm_files(pipeline)
     final_build_args += [
@@ -683,6 +695,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L
 
         with stage2.section("Gather profiles"):
             gather_rustc_profiles(pipeline)
+        print_free_disk_space(pipeline)
 
     clear_llvm_files(pipeline)
     final_build_args += [
@@ -702,6 +715,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L
             with stage3.section("Gather profiles"):
                 gather_llvm_bolt_profiles(pipeline)
 
+        print_free_disk_space(pipeline)
         clear_llvm_files(pipeline)
         final_build_args += [
             "--llvm-bolt-profile-use",
@@ -733,5 +747,6 @@ if __name__ == "__main__":
         raise e
     finally:
         timer.print_stats()
+        print_free_disk_space(pipeline)
 
     print_binary_sizes(pipeline)

From e80afa6501a700cb7bd356ea1c26190f6e21e79a Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 11 Feb 2023 17:37:52 +0000
Subject: [PATCH 4/7] Intern span when length is MAX_LEN with parent.

---
 compiler/rustc_span/src/span_encoding.rs | 7 ++++++-
 tests/ui/span/issue-107353.rs            | 8 ++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/span/issue-107353.rs

diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index d48c4f7e5a8..c600298c51a 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -110,11 +110,16 @@ impl Span {
                 // Inline format with parent.
                 let len_or_tag = len_or_tag | PARENT_MASK;
                 let parent2 = parent.local_def_index.as_u32();
-                if ctxt2 == SyntaxContext::root().as_u32() && parent2 <= MAX_CTXT {
+                if ctxt2 == SyntaxContext::root().as_u32()
+                    && parent2 <= MAX_CTXT
+                    && len_or_tag < LEN_TAG
+                {
+                    debug_assert_ne!(len_or_tag, LEN_TAG);
                     return Span { base_or_index: base, len_or_tag, ctxt_or_tag: parent2 as u16 };
                 }
             } else {
                 // Inline format with ctxt.
+                debug_assert_ne!(len_or_tag, LEN_TAG);
                 return Span {
                     base_or_index: base,
                     len_or_tag: len as u16,
diff --git a/tests/ui/span/issue-107353.rs b/tests/ui/span/issue-107353.rs
new file mode 100644
index 00000000000..09c66f42d78
--- /dev/null
+++ b/tests/ui/span/issue-107353.rs
@@ -0,0 +1,8 @@
+// Verify that span interning correctly handles having a span of exactly MAX_LEN length.
+// compile-flags: --crate-type=lib
+// check-pass
+
+#![allow(dead_code)]
+fn a<'a, T>() -> &'a T {
+todo!()////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+}

From 84bb373b2a6a33ea136ba7c09642c2e321f24c62 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 11 Feb 2023 18:07:06 +0000
Subject: [PATCH 5/7] Pacify tidy.

---
 tests/ui/span/issue-107353.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/ui/span/issue-107353.rs b/tests/ui/span/issue-107353.rs
index 09c66f42d78..943f7f0eb19 100644
--- a/tests/ui/span/issue-107353.rs
+++ b/tests/ui/span/issue-107353.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-linelength
 // Verify that span interning correctly handles having a span of exactly MAX_LEN length.
 // compile-flags: --crate-type=lib
 // check-pass

From 37a72a2e3e548c6bdecdf425b1616a7d6edcec0c Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Sat, 11 Feb 2023 22:51:21 +0400
Subject: [PATCH 6/7] rustc_ast: Merge impls and reorder methods for attributes
 and meta items

---
 compiler/rustc_ast/src/attr/mod.rs | 796 ++++++++++++++---------------
 1 file changed, 396 insertions(+), 400 deletions(-)

diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index cd60506dd80..e75d2f77dbb 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -40,310 +40,6 @@ impl MarkedAttrs {
     }
 }
 
-impl NestedMetaItem {
-    /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
-    pub fn meta_item(&self) -> Option<&MetaItem> {
-        match self {
-            NestedMetaItem::MetaItem(item) => Some(item),
-            _ => None,
-        }
-    }
-
-    /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
-    pub fn lit(&self) -> Option<&MetaItemLit> {
-        match self {
-            NestedMetaItem::Lit(lit) => Some(lit),
-            _ => None,
-        }
-    }
-
-    /// Returns `true` if this list item is a MetaItem with a name of `name`.
-    pub fn has_name(&self, name: Symbol) -> bool {
-        self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
-    }
-
-    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
-        self.meta_item().and_then(|meta_item| meta_item.ident())
-    }
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
-
-    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
-    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
-    pub fn value_str(&self) -> Option<Symbol> {
-        self.meta_item().and_then(|meta_item| meta_item.value_str())
-    }
-
-    /// Returns a name and single literal value tuple of the `MetaItem`.
-    pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
-        self.meta_item().and_then(|meta_item| {
-            meta_item.meta_item_list().and_then(|meta_item_list| {
-                if meta_item_list.len() == 1
-                    && let Some(ident) = meta_item.ident()
-                    && let Some(lit) = meta_item_list[0].lit()
-                {
-                    return Some((ident.name, lit));
-                }
-                None
-            })
-        })
-    }
-
-    /// Gets a list of inner meta items from a list `MetaItem` type.
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
-        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
-    }
-
-    /// Returns `true` if the variant is `MetaItem`.
-    pub fn is_meta_item(&self) -> bool {
-        self.meta_item().is_some()
-    }
-
-    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
-    pub fn is_word(&self) -> bool {
-        self.meta_item().map_or(false, |meta_item| meta_item.is_word())
-    }
-
-    /// See [`MetaItem::name_value_literal_span`].
-    pub fn name_value_literal_span(&self) -> Option<Span> {
-        self.meta_item()?.name_value_literal_span()
-    }
-}
-
-impl Attribute {
-    #[inline]
-    pub fn has_name(&self, name: Symbol) -> bool {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.path == name,
-            AttrKind::DocComment(..) => false,
-        }
-    }
-
-    /// For a single-segment attribute, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
-        match &self.kind {
-            AttrKind::Normal(normal) => {
-                if let [ident] = &*normal.item.path.segments {
-                    Some(ident.ident)
-                } else {
-                    None
-                }
-            }
-            AttrKind::DocComment(..) => None,
-        }
-    }
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
-
-    pub fn value_str(&self) -> Option<Symbol> {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.value_str(),
-            AttrKind::DocComment(..) => None,
-        }
-    }
-
-    pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.meta_item_list(),
-            AttrKind::DocComment(..) => None,
-        }
-    }
-
-    pub fn is_word(&self) -> bool {
-        if let AttrKind::Normal(normal) = &self.kind {
-            matches!(normal.item.args, AttrArgs::Empty)
-        } else {
-            false
-        }
-    }
-}
-
-impl MetaItem {
-    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
-        if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
-    }
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
-
-    /// ```text
-    /// Example:
-    ///     #[attribute(name = "value")]
-    ///                 ^^^^^^^^^^^^^^
-    /// ```
-    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
-        match &self.kind {
-            MetaItemKind::NameValue(v) => Some(v),
-            _ => None,
-        }
-    }
-
-    pub fn value_str(&self) -> Option<Symbol> {
-        self.kind.value_str()
-    }
-
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
-        match &self.kind {
-            MetaItemKind::List(l) => Some(&**l),
-            _ => None,
-        }
-    }
-
-    pub fn is_word(&self) -> bool {
-        matches!(self.kind, MetaItemKind::Word)
-    }
-
-    pub fn has_name(&self, name: Symbol) -> bool {
-        self.path == name
-    }
-
-    /// This is used in case you want the value span instead of the whole attribute. Example:
-    ///
-    /// ```text
-    /// #[doc(alias = "foo")]
-    /// ```
-    ///
-    /// In here, it'll return a span for `"foo"`.
-    pub fn name_value_literal_span(&self) -> Option<Span> {
-        Some(self.name_value_literal()?.span)
-    }
-}
-
-impl AttrArgsEq {
-    fn value_str(&self) -> Option<Symbol> {
-        match self {
-            AttrArgsEq::Ast(expr) => match expr.kind {
-                ExprKind::Lit(token_lit) => {
-                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
-                }
-                _ => None,
-            },
-            AttrArgsEq::Hir(lit) => lit.kind.str(),
-        }
-    }
-}
-
-impl AttrItem {
-    pub fn span(&self) -> Span {
-        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
-    }
-
-    pub fn meta(&self, span: Span) -> Option<MetaItem> {
-        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
-    }
-
-    pub fn meta_kind(&self) -> Option<MetaItemKind> {
-        MetaItemKind::from_attr_args(&self.args)
-    }
-
-    fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
-        match &self.args {
-            AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
-                MetaItemKind::list_from_tokens(args.tokens.clone())
-            }
-            AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
-        }
-    }
-
-    fn value_str(&self) -> Option<Symbol> {
-        match &self.args {
-            AttrArgs::Eq(_, args) => args.value_str(),
-            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
-        }
-    }
-}
-
-impl Attribute {
-    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
-    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
-    /// a doc comment) will return `false`.
-    pub fn is_doc_comment(&self) -> bool {
-        match self.kind {
-            AttrKind::Normal(..) => false,
-            AttrKind::DocComment(..) => true,
-        }
-    }
-
-    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
-    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
-    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
-    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
-    /// * `#[doc(...)]` returns `None`.
-    pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
-        match &self.kind {
-            AttrKind::DocComment(kind, data) => Some((*data, *kind)),
-            AttrKind::Normal(normal) if normal.item.path == sym::doc => {
-                normal.item.value_str().map(|s| (s, CommentKind::Line))
-            }
-            _ => None,
-        }
-    }
-
-    /// Returns the documentation if this is a doc comment or a sugared doc comment.
-    /// * `///doc` returns `Some("doc")`.
-    /// * `#[doc = "doc"]` returns `Some("doc")`.
-    /// * `#[doc(...)]` returns `None`.
-    pub fn doc_str(&self) -> Option<Symbol> {
-        match &self.kind {
-            AttrKind::DocComment(.., data) => Some(*data),
-            AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
-            _ => None,
-        }
-    }
-
-    pub fn may_have_doc_links(&self) -> bool {
-        self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
-    }
-
-    pub fn get_normal_item(&self) -> &AttrItem {
-        match &self.kind {
-            AttrKind::Normal(normal) => &normal.item,
-            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
-        }
-    }
-
-    pub fn unwrap_normal_item(self) -> AttrItem {
-        match self.kind {
-            AttrKind::Normal(normal) => normal.into_inner().item,
-            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
-        }
-    }
-
-    /// Extracts the MetaItem from inside this Attribute.
-    pub fn meta(&self) -> Option<MetaItem> {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.meta(self.span),
-            AttrKind::DocComment(..) => None,
-        }
-    }
-
-    pub fn meta_kind(&self) -> Option<MetaItemKind> {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.meta_kind(),
-            AttrKind::DocComment(..) => None,
-        }
-    }
-
-    pub fn tokens(&self) -> TokenStream {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal
-                .tokens
-                .as_ref()
-                .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
-                .to_attr_token_stream()
-                .to_tokenstream(),
-            &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
-                Token::new(token::DocComment(comment_kind, self.style, data), self.span),
-                Spacing::Alone,
-            )]),
-        }
-    }
-}
-
 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
 
 #[cfg(debug_assertions)]
@@ -381,93 +77,236 @@ impl AttrIdGenerator {
     }
 }
 
-pub fn mk_attr(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    path: Path,
-    args: AttrArgs,
-    span: Span,
-) -> Attribute {
-    mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
-}
+impl Attribute {
+    pub fn get_normal_item(&self) -> &AttrItem {
+        match &self.kind {
+            AttrKind::Normal(normal) => &normal.item,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
+    }
 
-pub fn mk_attr_from_item(
-    g: &AttrIdGenerator,
-    item: AttrItem,
-    tokens: Option<LazyAttrTokenStream>,
-    style: AttrStyle,
-    span: Span,
-) -> Attribute {
-    Attribute {
-        kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
-        id: g.mk_attr_id(),
-        style,
-        span,
+    pub fn unwrap_normal_item(self) -> AttrItem {
+        match self.kind {
+            AttrKind::Normal(normal) => normal.into_inner().item,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
+    }
+
+    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
+    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
+    /// a doc comment) will return `false`.
+    pub fn is_doc_comment(&self) -> bool {
+        match self.kind {
+            AttrKind::Normal(..) => false,
+            AttrKind::DocComment(..) => true,
+        }
+    }
+
+    /// For a single-segment attribute, returns its name; otherwise, returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        match &self.kind {
+            AttrKind::Normal(normal) => {
+                if let [ident] = &*normal.item.path.segments {
+                    Some(ident.ident)
+                } else {
+                    None
+                }
+            }
+            AttrKind::DocComment(..) => None,
+        }
+    }
+    pub fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    #[inline]
+    pub fn has_name(&self, name: Symbol) -> bool {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal.item.path == name,
+            AttrKind::DocComment(..) => false,
+        }
+    }
+
+    pub fn is_word(&self) -> bool {
+        if let AttrKind::Normal(normal) = &self.kind {
+            matches!(normal.item.args, AttrArgs::Empty)
+        } else {
+            false
+        }
+    }
+
+    pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal.item.meta_item_list(),
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    pub fn value_str(&self) -> Option<Symbol> {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal.item.value_str(),
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
+    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
+    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
+    /// * `#[doc(...)]` returns `None`.
+    pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+        match &self.kind {
+            AttrKind::DocComment(kind, data) => Some((*data, *kind)),
+            AttrKind::Normal(normal) if normal.item.path == sym::doc => {
+                normal.item.value_str().map(|s| (s, CommentKind::Line))
+            }
+            _ => None,
+        }
+    }
+
+    /// Returns the documentation if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some("doc")`.
+    /// * `#[doc = "doc"]` returns `Some("doc")`.
+    /// * `#[doc(...)]` returns `None`.
+    pub fn doc_str(&self) -> Option<Symbol> {
+        match &self.kind {
+            AttrKind::DocComment(.., data) => Some(*data),
+            AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
+            _ => None,
+        }
+    }
+
+    pub fn may_have_doc_links(&self) -> bool {
+        self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
+    }
+
+    /// Extracts the MetaItem from inside this Attribute.
+    pub fn meta(&self) -> Option<MetaItem> {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal.item.meta(self.span),
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    pub fn meta_kind(&self) -> Option<MetaItemKind> {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal.item.meta_kind(),
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    pub fn tokens(&self) -> TokenStream {
+        match &self.kind {
+            AttrKind::Normal(normal) => normal
+                .tokens
+                .as_ref()
+                .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
+                .to_attr_token_stream()
+                .to_tokenstream(),
+            &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
+                Token::new(token::DocComment(comment_kind, self.style, data), self.span),
+                Spacing::Alone,
+            )]),
+        }
     }
 }
 
-pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
-    let path = Path::from_ident(Ident::new(name, span));
-    let args = AttrArgs::Empty;
-    mk_attr(g, style, path, args, span)
+impl AttrItem {
+    pub fn span(&self) -> Span {
+        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
+    }
+
+    fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+        match &self.args {
+            AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
+                MetaItemKind::list_from_tokens(args.tokens.clone())
+            }
+            AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
+        }
+    }
+
+    fn value_str(&self) -> Option<Symbol> {
+        match &self.args {
+            AttrArgs::Eq(_, args) => args.value_str(),
+            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
+        }
+    }
+
+    pub fn meta(&self, span: Span) -> Option<MetaItem> {
+        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
+    }
+
+    pub fn meta_kind(&self) -> Option<MetaItemKind> {
+        MetaItemKind::from_attr_args(&self.args)
+    }
 }
 
-pub fn mk_attr_name_value_str(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    name: Symbol,
-    val: Symbol,
-    span: Span,
-) -> Attribute {
-    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
-    let expr = P(Expr {
-        id: DUMMY_NODE_ID,
-        kind: ExprKind::Lit(lit),
-        span,
-        attrs: AttrVec::new(),
-        tokens: None,
-    });
-    let path = Path::from_ident(Ident::new(name, span));
-    let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
-    mk_attr(g, style, path, args, span)
-}
-
-pub fn mk_attr_nested_word(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    outer: Symbol,
-    inner: Symbol,
-    span: Span,
-) -> Attribute {
-    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
-        Token::from_ast_ident(Ident::new(inner, span)),
-        Spacing::Alone,
-    )]);
-    let outer_ident = Ident::new(outer, span);
-    let path = Path::from_ident(outer_ident);
-    let attr_args = AttrArgs::Delimited(DelimArgs {
-        dspan: DelimSpan::from_single(span),
-        delim: MacDelimiter::Parenthesis,
-        tokens: inner_tokens,
-    });
-    mk_attr(g, style, path, attr_args, span)
-}
-
-pub fn mk_doc_comment(
-    g: &AttrIdGenerator,
-    comment_kind: CommentKind,
-    style: AttrStyle,
-    data: Symbol,
-    span: Span,
-) -> Attribute {
-    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
-}
-
-pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
-    items.iter().any(|item| item.has_name(name))
+impl AttrArgsEq {
+    fn value_str(&self) -> Option<Symbol> {
+        match self {
+            AttrArgsEq::Ast(expr) => match expr.kind {
+                ExprKind::Lit(token_lit) => {
+                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
+                }
+                _ => None,
+            },
+            AttrArgsEq::Hir(lit) => lit.kind.str(),
+        }
+    }
 }
 
 impl MetaItem {
+    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    pub fn has_name(&self, name: Symbol) -> bool {
+        self.path == name
+    }
+
+    pub fn is_word(&self) -> bool {
+        matches!(self.kind, MetaItemKind::Word)
+    }
+
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+        match &self.kind {
+            MetaItemKind::List(l) => Some(&**l),
+            _ => None,
+        }
+    }
+
+    /// ```text
+    /// Example:
+    ///     #[attribute(name = "value")]
+    ///                 ^^^^^^^^^^^^^^
+    /// ```
+    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
+        match &self.kind {
+            MetaItemKind::NameValue(v) => Some(v),
+            _ => None,
+        }
+    }
+
+    /// This is used in case you want the value span instead of the whole attribute. Example:
+    ///
+    /// ```text
+    /// #[doc(alias = "foo")]
+    /// ```
+    ///
+    /// In here, it'll return a span for `"foo"`.
+    pub fn name_value_literal_span(&self) -> Option<Span> {
+        Some(self.name_value_literal()?.span)
+    }
+
+    pub fn value_str(&self) -> Option<Symbol> {
+        self.kind.value_str()
+    }
+
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
     where
         I: Iterator<Item = TokenTree>,
@@ -564,6 +403,24 @@ impl MetaItemKind {
         }
     }
 
+    fn from_tokens(
+        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+    ) -> Option<MetaItemKind> {
+        match tokens.peek() {
+            Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
+                let inner_tokens = inner_tokens.clone();
+                tokens.next();
+                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
+            }
+            Some(TokenTree::Delimited(..)) => None,
+            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
+                tokens.next();
+                MetaItemKind::name_value_from_tokens(tokens)
+            }
+            _ => Some(MetaItemKind::Word),
+        }
+    }
+
     fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
         match args {
             AttrArgs::Empty => Some(MetaItemKind::Word),
@@ -585,24 +442,6 @@ impl MetaItemKind {
             AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
         }
     }
-
-    fn from_tokens(
-        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
-    ) -> Option<MetaItemKind> {
-        match tokens.peek() {
-            Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
-                let inner_tokens = inner_tokens.clone();
-                tokens.next();
-                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
-            }
-            Some(TokenTree::Delimited(..)) => None,
-            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
-                tokens.next();
-                MetaItemKind::name_value_from_tokens(tokens)
-            }
-            _ => Some(MetaItemKind::Word),
-        }
-    }
 }
 
 impl NestedMetaItem {
@@ -613,6 +452,77 @@ impl NestedMetaItem {
         }
     }
 
+    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        self.meta_item().and_then(|meta_item| meta_item.ident())
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    /// Returns `true` if this list item is a MetaItem with a name of `name`.
+    pub fn has_name(&self, name: Symbol) -> bool {
+        self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
+    }
+
+    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
+    pub fn is_word(&self) -> bool {
+        self.meta_item().map_or(false, |meta_item| meta_item.is_word())
+    }
+
+    /// Gets a list of inner meta items from a list `MetaItem` type.
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
+    }
+
+    /// Returns a name and single literal value tuple of the `MetaItem`.
+    pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
+        self.meta_item().and_then(|meta_item| {
+            meta_item.meta_item_list().and_then(|meta_item_list| {
+                if meta_item_list.len() == 1
+                    && let Some(ident) = meta_item.ident()
+                    && let Some(lit) = meta_item_list[0].lit()
+                {
+                    return Some((ident.name, lit));
+                }
+                None
+            })
+        })
+    }
+
+    /// See [`MetaItem::name_value_literal_span`].
+    pub fn name_value_literal_span(&self) -> Option<Span> {
+        self.meta_item()?.name_value_literal_span()
+    }
+
+    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
+    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
+    pub fn value_str(&self) -> Option<Symbol> {
+        self.meta_item().and_then(|meta_item| meta_item.value_str())
+    }
+
+    /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
+    pub fn lit(&self) -> Option<&MetaItemLit> {
+        match self {
+            NestedMetaItem::Lit(lit) => Some(lit),
+            _ => None,
+        }
+    }
+
+    /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
+    pub fn meta_item(&self) -> Option<&MetaItem> {
+        match self {
+            NestedMetaItem::MetaItem(item) => Some(item),
+            _ => None,
+        }
+    }
+
+    /// Returns `true` if the variant is `MetaItem`.
+    pub fn is_meta_item(&self) -> bool {
+        self.meta_item().is_some()
+    }
+
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
     where
         I: Iterator<Item = TokenTree>,
@@ -634,3 +544,89 @@ impl NestedMetaItem {
         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
     }
 }
+
+pub fn mk_doc_comment(
+    g: &AttrIdGenerator,
+    comment_kind: CommentKind,
+    style: AttrStyle,
+    data: Symbol,
+    span: Span,
+) -> Attribute {
+    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
+}
+
+pub fn mk_attr(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    path: Path,
+    args: AttrArgs,
+    span: Span,
+) -> Attribute {
+    mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
+}
+
+pub fn mk_attr_from_item(
+    g: &AttrIdGenerator,
+    item: AttrItem,
+    tokens: Option<LazyAttrTokenStream>,
+    style: AttrStyle,
+    span: Span,
+) -> Attribute {
+    Attribute {
+        kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
+        id: g.mk_attr_id(),
+        style,
+        span,
+    }
+}
+
+pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Empty;
+    mk_attr(g, style, path, args, span)
+}
+
+pub fn mk_attr_nested_word(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    outer: Symbol,
+    inner: Symbol,
+    span: Span,
+) -> Attribute {
+    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
+        Token::from_ast_ident(Ident::new(inner, span)),
+        Spacing::Alone,
+    )]);
+    let outer_ident = Ident::new(outer, span);
+    let path = Path::from_ident(outer_ident);
+    let attr_args = AttrArgs::Delimited(DelimArgs {
+        dspan: DelimSpan::from_single(span),
+        delim: MacDelimiter::Parenthesis,
+        tokens: inner_tokens,
+    });
+    mk_attr(g, style, path, attr_args, span)
+}
+
+pub fn mk_attr_name_value_str(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    name: Symbol,
+    val: Symbol,
+    span: Span,
+) -> Attribute {
+    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
+    let expr = P(Expr {
+        id: DUMMY_NODE_ID,
+        kind: ExprKind::Lit(lit),
+        span,
+        attrs: AttrVec::new(),
+        tokens: None,
+    });
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
+    mk_attr(g, style, path, args, span)
+}
+
+pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
+    items.iter().any(|item| item.has_name(name))
+}

From 9e84b00d447b5cb878a6b4e4001a94d4ea3531ca Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Mon, 13 Feb 2023 10:45:43 +0100
Subject: [PATCH 7/7] layout: deal with placeholders, ICE on bound types

a placeholder type is the same as a param as they
represent "this could be any type". A bound type
represents a type inside of a `for<T>` or `exists<T>`.
When entering a forall or exists `T` should be
instantiated as a existential (inference var) or universal
(placeholder). You should never observe a bound variable
without its binder.
---
 compiler/rustc_ty_utils/src/layout.rs | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 2aeb255c164..13728919878 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -470,14 +470,11 @@ fn layout_of_uncached<'tcx>(
             return Err(LayoutError::Unknown(ty));
         }
 
-        ty::Placeholder(..)
-        | ty::GeneratorWitness(..)
-        | ty::GeneratorWitnessMIR(..)
-        | ty::Infer(_) => {
+        ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
             bug!("Layout::compute: unexpected type `{}`", ty)
         }
 
-        ty::Bound(..) | ty::Param(_) | ty::Error(_) => {
+        ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
             return Err(LayoutError::Unknown(ty));
         }
     })