about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-10 18:28:24 +0000
committerbors <bors@rust-lang.org>2021-05-10 18:28:24 +0000
commit6fd7a6dc0f5c4c8fd51d57c0f4f795d52481f904 (patch)
treea22101c2f5f999c09d665a18a2325e2df6d57430
parent266f452118e27e6a3d9d6b6f9d5483890a18345f (diff)
parent6ec1de7d4f950c7c0b33bae9858157282416591d (diff)
downloadrust-6fd7a6dc0f5c4c8fd51d57c0f4f795d52481f904.tar.gz
rust-6fd7a6dc0f5c4c8fd51d57c0f4f795d52481f904.zip
Auto merge of #85156 - GuillaumeGomez:rollup-8u4h34g, r=GuillaumeGomez
Rollup of 4 pull requests

Successful merges:

 - #84465 (rustdoc: Implement `is_primitive` in terms of `primitive_type()`)
 - #85118 (Use an SVG image for clipboard instead of unicode character)
 - #85148 (Fix source code line number display and make it clickable again)
 - #85152 (Adjust target search algorithm for rustlib path)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs3
-rw-r--r--compiler/rustc_session/src/filesearch.rs68
-rw-r--r--compiler/rustc_target/src/lib.rs51
-rw-r--r--compiler/rustc_target/src/spec/mod.rs25
-rw-r--r--src/librustdoc/clean/types.rs9
-rw-r--r--src/librustdoc/html/highlight.rs15
-rw-r--r--src/librustdoc/html/layout.rs8
-rw-r--r--src/librustdoc/html/markdown.rs1
-rw-r--r--src/librustdoc/html/render/context.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs15
-rw-r--r--src/librustdoc/html/render/write_shared.rs1
-rw-r--r--src/librustdoc/html/sources.rs9
-rw-r--r--src/librustdoc/html/static/clipboard.svg1
-rw-r--r--src/librustdoc/html/static/main.js20
-rw-r--r--src/librustdoc/html/static/rustdoc.css23
-rw-r--r--src/librustdoc/html/static/themes/ayu.css4
-rw-r--r--src/librustdoc/html/static/themes/dark.css2
-rw-r--r--src/librustdoc/html/static/themes/light.css2
-rw-r--r--src/librustdoc/html/static_files.rs3
-rw-r--r--src/test/rustdoc-gui/source-code-page.goml13
21 files changed, 173 insertions, 104 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 59f66c55572..30a56badeb5 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1576,7 +1576,7 @@ fn add_rpath_args(
         let target_triple = sess.opts.target_triple.triple();
         let mut get_install_prefix_lib_path = || {
             let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
-            let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple);
+            let tlib = rustc_target::target_rustlib_path(&sess.sysroot, target_triple).join("lib");
             let mut path = PathBuf::from(install_prefix);
             path.push(&tlib);
 
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 59488fc80a5..fd29053433e 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -423,8 +423,7 @@ pub fn get_codegen_sysroot(
         .iter()
         .chain(sysroot_candidates.iter())
         .map(|sysroot| {
-            let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
-            sysroot.join(libdir).with_file_name("codegen-backends")
+            filesearch::make_target_lib_path(&sysroot, &target).with_file_name("codegen-backends")
         })
         .find(|f| {
             info!("codegen backend candidate: {}", f.display());
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 2df326628e7..6fe6a555f1a 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -1,6 +1,5 @@
 pub use self::FileMatch::*;
 
-use std::borrow::Cow;
 use std::env;
 use std::fs;
 use std::path::{Path, PathBuf};
@@ -91,26 +90,21 @@ impl<'a> FileSearch<'a> {
 
     // Returns a list of directories where target-specific tool binaries are located.
     pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
-        let mut p = PathBuf::from(self.sysroot);
-        p.push(find_libdir(self.sysroot).as_ref());
-        p.push(RUST_LIB_DIR);
-        p.push(&self.triple);
-        p.push("bin");
+        let rustlib_path = rustc_target::target_rustlib_path(self.sysroot, &self.triple);
+        let p = std::array::IntoIter::new([
+            Path::new(&self.sysroot),
+            Path::new(&rustlib_path),
+            Path::new("bin"),
+        ])
+        .collect::<PathBuf>();
         if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] }
     }
 }
 
-pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let mut p = PathBuf::from(find_libdir(sysroot).as_ref());
-    assert!(p.is_relative());
-    p.push(RUST_LIB_DIR);
-    p.push(target_triple);
-    p.push("lib");
-    p
-}
-
 pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    sysroot.join(&relative_target_lib_path(sysroot, target_triple))
+    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
+    std::array::IntoIter::new([sysroot, Path::new(&rustlib_path), Path::new("lib")])
+        .collect::<PathBuf>()
 }
 
 // This function checks if sysroot is found using env::args().next(), and if it
@@ -157,11 +151,13 @@ pub fn get_or_default_sysroot() -> PathBuf {
                     return None;
                 }
 
+                // Pop off `bin/rustc`, obtaining the suspected sysroot.
                 p.pop();
                 p.pop();
-                let mut libdir = PathBuf::from(&p);
-                libdir.push(find_libdir(&p).as_ref());
-                if libdir.exists() { Some(p) } else { None }
+                // Look for the target rustlib directory in the suspected sysroot.
+                let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
+                rustlib_path.pop(); // pop off the dummy target.
+                if rustlib_path.exists() { Some(p) } else { None }
             }
             None => None,
         }
@@ -171,37 +167,3 @@ pub fn get_or_default_sysroot() -> PathBuf {
     // use env::current_exe() to imply sysroot.
     from_env_args_next().unwrap_or_else(from_current_exe)
 }
-
-// The name of the directory rustc expects libraries to be located.
-fn find_libdir(sysroot: &Path) -> Cow<'static, str> {
-    // FIXME: This is a quick hack to make the rustc binary able to locate
-    // Rust libraries in Linux environments where libraries might be installed
-    // to lib64/lib32. This would be more foolproof by basing the sysroot off
-    // of the directory where `librustc_driver` is located, rather than
-    // where the rustc binary is.
-    // If --libdir is set during configuration to the value other than
-    // "lib" (i.e., non-default), this value is used (see issue #16552).
-
-    #[cfg(target_pointer_width = "64")]
-    const PRIMARY_LIB_DIR: &str = "lib64";
-
-    #[cfg(target_pointer_width = "32")]
-    const PRIMARY_LIB_DIR: &str = "lib32";
-
-    const SECONDARY_LIB_DIR: &str = "lib";
-
-    match option_env!("CFG_LIBDIR_RELATIVE") {
-        None | Some("lib") => {
-            if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
-                PRIMARY_LIB_DIR.into()
-            } else {
-                SECONDARY_LIB_DIR.into()
-            }
-        }
-        Some(libdir) => libdir.into(),
-    }
-}
-
-// The name of rustc's own place to organize libraries.
-// Used to be "rustc", now the default is "rustlib"
-const RUST_LIB_DIR: &str = "rustlib";
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index 67025388747..48ace9b65b6 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -15,6 +15,8 @@
 #![feature(associated_type_bounds)]
 #![feature(exhaustive_patterns)]
 
+use std::path::{Path, PathBuf};
+
 #[macro_use]
 extern crate rustc_macros;
 
@@ -29,3 +31,52 @@ pub mod spec;
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
 pub trait HashStableContext {}
+
+/// The name of rustc's own place to organize libraries.
+///
+/// Used to be `rustc`, now the default is `rustlib`.
+const RUST_LIB_DIR: &str = "rustlib";
+
+/// Returns a `rustlib` path for this particular target, relative to the provided sysroot.
+///
+/// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` =>
+/// `"lib*/rustlib/x86_64-unknown-linux-gnu"`.
+pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+    let libdir = find_libdir(sysroot);
+    std::array::IntoIter::new([
+        Path::new(libdir.as_ref()),
+        Path::new(RUST_LIB_DIR),
+        Path::new(target_triple),
+    ])
+    .collect::<PathBuf>()
+}
+
+/// The name of the directory rustc expects libraries to be located.
+fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
+    // FIXME: This is a quick hack to make the rustc binary able to locate
+    // Rust libraries in Linux environments where libraries might be installed
+    // to lib64/lib32. This would be more foolproof by basing the sysroot off
+    // of the directory where `librustc_driver` is located, rather than
+    // where the rustc binary is.
+    // If --libdir is set during configuration to the value other than
+    // "lib" (i.e., non-default), this value is used (see issue #16552).
+
+    #[cfg(target_pointer_width = "64")]
+    const PRIMARY_LIB_DIR: &str = "lib64";
+
+    #[cfg(target_pointer_width = "32")]
+    const PRIMARY_LIB_DIR: &str = "lib32";
+
+    const SECONDARY_LIB_DIR: &str = "lib";
+
+    match option_env!("CFG_LIBDIR_RELATIVE") {
+        None | Some("lib") => {
+            if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
+                PRIMARY_LIB_DIR.into()
+            } else {
+                SECONDARY_LIB_DIR.into()
+            }
+        }
+        Some(libdir) => libdir.into(),
+    }
+}
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 27ca6a23a96..4bffd6e8ddd 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1897,15 +1897,15 @@ impl Target {
         Ok(base)
     }
 
-    /// Search RUST_TARGET_PATH for a JSON file specifying the given target
-    /// triple. If none is found, look for a file called `target.json` inside
-    /// the sysroot under the target-triple's `rustlib` directory.
-    /// Note that it could also just be a bare filename already, so also
-    /// check for that. If one of the hardcoded targets we know about, just
-    /// return it directly.
+    /// Search for a JSON file specifying the given target triple.
     ///
-    /// The error string could come from any of the APIs called, including
-    /// filesystem access and JSON decoding.
+    /// If none is found in `$RUST_TARGET_PATH`, look for a file called `target.json` inside the
+    /// sysroot under the target-triple's `rustlib` directory.  Note that it could also just be a
+    /// bare filename already, so also check for that. If one of the hardcoded targets we know
+    /// about, just return it directly.
+    ///
+    /// The error string could come from any of the APIs called, including filesystem access and
+    /// JSON decoding.
     pub fn search(target_triple: &TargetTriple, sysroot: &PathBuf) -> Result<Target, String> {
         use rustc_serialize::json;
         use std::env;
@@ -1942,8 +1942,13 @@ impl Target {
 
                 // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
                 // as a fallback.
-                let p =
-                    sysroot.join("lib").join("rustlib").join(&target_triple).join("target.json");
+                let rustlib_path = crate::target_rustlib_path(&sysroot, &target_triple);
+                let p = std::array::IntoIter::new([
+                    Path::new(sysroot),
+                    Path::new(&rustlib_path),
+                    Path::new("target.json"),
+                ])
+                .collect::<PathBuf>();
                 if p.is_file() {
                     return load_file(&p);
                 }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 33aa42b137a..bca7a8cfcee 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1606,7 +1606,6 @@ impl Type {
                 }
             }
             RawPointer(..) => Some(PrimitiveType::RawPointer),
-            BorrowedRef { type_: box Generic(..), .. } => Some(PrimitiveType::Reference),
             BareFunction(..) => Some(PrimitiveType::Fn),
             Never => Some(PrimitiveType::Never),
             _ => None,
@@ -1665,13 +1664,7 @@ impl Type {
     }
 
     crate fn is_primitive(&self) -> bool {
-        match self {
-            Self::Primitive(_) => true,
-            Self::BorrowedRef { ref type_, .. } | Self::RawPointer(_, ref type_) => {
-                type_.is_primitive()
-            }
-            _ => false,
-        }
+        self.primitive_type().is_some()
     }
 
     crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index f631f627fc2..51392ca1191 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -24,6 +24,7 @@ crate fn render_with_highlighting(
     playground_button: Option<&str>,
     tooltip: Option<(Option<Edition>, &str)>,
     edition: Edition,
+    extra_content: Option<Buffer>,
 ) {
     debug!("highlighting: ================\n{}\n==============", src);
     if let Some((edition_info, class)) = tooltip {
@@ -39,13 +40,21 @@ crate fn render_with_highlighting(
         );
     }
 
-    write_header(out, class);
+    write_header(out, class, extra_content);
     write_code(out, &src, edition);
     write_footer(out, playground_button);
 }
 
-fn write_header(out: &mut Buffer, class: Option<&str>) {
-    writeln!(out, "<div class=\"example-wrap\"><pre class=\"rust {}\">", class.unwrap_or_default());
+fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buffer>) {
+    write!(out, "<div class=\"example-wrap\">");
+    if let Some(extra) = extra_content {
+        out.push_buffer(extra);
+    }
+    if let Some(class) = class {
+        writeln!(out, "<pre class=\"rust {}\">", class);
+    } else {
+        writeln!(out, "<pre class=\"rust\">");
+    }
 }
 
 fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index dc29add9333..99e96fdcf1e 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -34,6 +34,12 @@ crate struct Page<'a> {
     crate static_extra_scripts: &'a [&'a str],
 }
 
+impl<'a> Page<'a> {
+    crate fn get_static_root_path(&self) -> &str {
+        self.static_root_path.unwrap_or(self.root_path)
+    }
+}
+
 crate fn render<T: Print, S: Print>(
     layout: &Layout,
     page: &Page<'_>,
@@ -41,7 +47,7 @@ crate fn render<T: Print, S: Print>(
     t: T,
     style_files: &[StylePath],
 ) -> String {
-    let static_root_path = page.static_root_path.unwrap_or(page.root_path);
+    let static_root_path = page.get_static_root_path();
     format!(
         "<!DOCTYPE html>\
 <html lang=\"en\">\
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 509f1730557..c2b40ab34e2 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -315,6 +315,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             playground_button.as_deref(),
             tooltip,
             edition,
+            None,
         );
         Some(Event::Html(s.into_inner().into()))
     }
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index e0c1fd06e7b..4e17dc8d3a7 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -215,7 +215,7 @@ impl<'tcx> Context<'tcx> {
                 &self.shared.layout,
                 &page,
                 |buf: &mut _| print_sidebar(self, it, buf),
-                |buf: &mut _| print_item(self, it, buf),
+                |buf: &mut _| print_item(self, it, buf, &page),
                 &self.shared.style_files,
             )
         } else {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 4b7664f28a1..7ccc313cc59 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -22,9 +22,10 @@ use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace};
 use crate::html::highlight;
+use crate::html::layout::Page;
 use crate::html::markdown::MarkdownSummaryLine;
 
-pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
+pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) {
     debug_assert!(!item.is_stripped());
     // Write the breadcrumb trail header for the top
     buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
@@ -74,7 +75,16 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
         }
     }
     write!(buf, "<a class=\"{}\" href=\"\">{}</a>", item.type_(), item.name.as_ref().unwrap());
-    write!(buf, "<button id=\"copy-path\" onclick=\"copy_path(this)\">⎘</button>");
+    write!(
+        buf,
+        "<button id=\"copy-path\" onclick=\"copy_path(this)\">\
+            <img src=\"{static_root_path}clipboard{suffix}.svg\" \
+                width=\"19\" height=\"18\" \
+                alt=\"Copy item import\">\
+         </button>",
+        static_root_path = page.get_static_root_path(),
+        suffix = page.resource_suffix,
+    );
 
     buf.write_str("</span>"); // in-band
     buf.write_str("<span class=\"out-of-band\">");
@@ -1016,6 +1026,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac
             None,
             None,
             it.span(cx.tcx()).inner().edition(),
+            None,
         );
     });
     document(w, cx, it, None)
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index c493801d990..d0518cb6862 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -207,6 +207,7 @@ pub(super) fn write_shared(
     }
     write_toolchain("brush.svg", static_files::BRUSH_SVG)?;
     write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
+    write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
     write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
 
     let mut themes: Vec<&String> = themes.iter().collect();
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 14e2d65d94e..57c33f94918 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -169,16 +169,17 @@ where
 /// adding line numbers to the left-hand side.
 fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
     let lines = s.lines().count();
+    let mut line_numbers = Buffer::empty_from(buf);
     let mut cols = 0;
     let mut tmp = lines;
     while tmp > 0 {
         cols += 1;
         tmp /= 10;
     }
-    buf.write_str("<pre class=\"line-numbers\">");
+    line_numbers.write_str("<pre class=\"line-numbers\">");
     for i in 1..=lines {
-        writeln!(buf, "<span id=\"{0}\">{0:1$}</span>", i, cols);
+        writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols);
     }
-    buf.write_str("</pre>");
-    highlight::render_with_highlighting(s, buf, None, None, None, edition);
+    line_numbers.write_str("</pre>");
+    highlight::render_with_highlighting(s, buf, None, None, None, edition, Some(line_numbers));
 }
diff --git a/src/librustdoc/html/static/clipboard.svg b/src/librustdoc/html/static/clipboard.svg
new file mode 100644
index 00000000000..8adbd996304
--- /dev/null
+++ b/src/librustdoc/html/static/clipboard.svg
@@ -0,0 +1 @@
+<svg width="24" height="25" viewBox="0 0 24 25" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg>
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index a03d20c053d..dc65e14ab37 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1252,15 +1252,31 @@ function hideThemeButtonState() {
         document.execCommand('copy');
         document.body.removeChild(el);
 
-        but.textContent = '✓';
+        // There is always one children, but multiple childNodes.
+        but.children[0].style.display = 'none';
+
+        var tmp;
+        if (but.childNodes.length < 2) {
+            tmp = document.createTextNode('✓');
+            but.appendChild(tmp);
+        } else {
+            onEachLazy(but.childNodes, function(e) {
+                if (e.nodeType === Node.TEXT_NODE) {
+                    tmp = e;
+                    return true;
+                }
+            });
+            tmp.textContent = '✓';
+        }
 
         if (reset_button_timeout !== null) {
             window.clearTimeout(reset_button_timeout);
         }
 
         function reset_button() {
-            but.textContent = '⎘';
+            tmp.textContent = '';
             reset_button_timeout = null;
+            but.children[0].style.display = "";
         }
 
         reset_button_timeout = window.setTimeout(reset_button, 1000);
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 42a85fcce03..aaa2525644f 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -206,7 +206,6 @@ li {
 	max-width: none;
 	overflow: visible;
 	margin-left: 0px;
-	min-width: 70em;
 }
 
 nav.sub {
@@ -357,7 +356,7 @@ nav.sub {
 	padding-left: 0;
 }
 
-.rustdoc:not(.source) .example-wrap {
+.rustdoc .example-wrap {
 	display: inline-flex;
 	margin-bottom: 10px;
 }
@@ -370,8 +369,6 @@ nav.sub {
 .example-wrap > pre.line-number {
 	overflow: initial;
 	border: 1px solid;
-	border-top-left-radius: 5px;
-	border-bottom-left-radius: 5px;
 	padding: 13px 8px;
 	text-align: right;
 }
@@ -381,7 +378,7 @@ nav.sub {
 	overflow-x: auto;
 }
 
-.rustdoc:not(.source) .example-wrap > pre {
+.rustdoc .example-wrap > pre {
 	margin: 0;
 }
 
@@ -395,15 +392,14 @@ nav.sub {
 	table-layout: fixed;
 }
 
-.content pre.line-numbers {
-	float: left;
-	border: none;
+.content > .example-wrap pre.line-numbers {
 	position: relative;
-
 	-webkit-user-select: none;
 	-moz-user-select: none;
 	-ms-user-select: none;
 	user-select: none;
+	border-top-left-radius: 5px;
+	border-bottom-left-radius: 5px;
 }
 .line-numbers span {
 	cursor: pointer;
@@ -1321,11 +1317,12 @@ h4 > .notable-traits {
 }
 
 #copy-path {
-	height: 30px;
-	font-size: 18px;
 	margin-left: 10px;
-	padding: 0 6px;
-	width: 28px;
+	padding: 0;
+	padding-left: 2px;
+}
+#copy-path> img {
+	margin-bottom: 2px;
 }
 
 #theme-choices {
diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css
index aace0b3c037..aafb7f6300e 100644
--- a/src/librustdoc/html/static/themes/ayu.css
+++ b/src/librustdoc/html/static/themes/ayu.css
@@ -53,7 +53,7 @@ span code {
 .docblock code, .docblock-short code {
 	background-color: #191f26;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
 	color: #e6e1cf;
 	background-color: #191f26;
 }
@@ -509,7 +509,7 @@ kbd {
 	color: #fff;
 }
 
-#theme-picker > img, #settings-menu > img {
+#theme-picker > img, #settings-menu > img, #copy-path > img {
 	filter: invert(100);
 }
 
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index c23e95ce107..715605d7b37 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -26,7 +26,7 @@ h4:not(.method):not(.type):not(.tymethod) {
 .docblock code, .docblock-short code {
 	background-color: #2A2A2A;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
 	background-color: #2A2A2A;
 }
 
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 93309721210..60ed8898793 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -28,7 +28,7 @@ h4:not(.method):not(.type):not(.tymethod) {
 .docblock code, .docblock-short code {
 	background-color: #F5F5F5;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
 	background-color: #F5F5F5;
 }
 
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 2b73bd5d52e..1abb1f7294a 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -41,6 +41,9 @@ crate static BRUSH_SVG: &[u8] = include_bytes!("static/brush.svg");
 /// The file contents of `wheel.svg`, the icon used for the settings button.
 crate static WHEEL_SVG: &[u8] = include_bytes!("static/wheel.svg");
 
+/// The file contents of `clipboard.svg`, the icon used for the "copy path" button.
+crate static CLIPBOARD_SVG: &[u8] = include_bytes!("static/clipboard.svg");
+
 /// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox.
 crate static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/down-arrow.svg");
 
diff --git a/src/test/rustdoc-gui/source-code-page.goml b/src/test/rustdoc-gui/source-code-page.goml
new file mode 100644
index 00000000000..f11c41e8bd5
--- /dev/null
+++ b/src/test/rustdoc-gui/source-code-page.goml
@@ -0,0 +1,13 @@
+goto: file://|DOC_PATH|/../src/test_docs/lib.rs.html
+// Check that we can click on the line number.
+click: (40, 224) // This is the position of the span for line 4.
+// Unfortunately, "#4" isn't a valid query selector, so we have to go around that limitation
+// by instead getting the nth span.
+assert: (".line-numbers > span:nth-child(4)", "class", "line-highlighted")
+// We now check that the good spans are highlighted
+goto: file://|DOC_PATH|/../src/test_docs/lib.rs.html#4-6
+assert-false: (".line-numbers > span:nth-child(3)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(4)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(5)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(6)", "class", "line-highlighted")
+assert-false: (".line-numbers > span:nth-child(7)", "class", "line-highlighted")