From 14986081355db0a2ae67df6a43dd9e6e360d718c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 20 Jul 2019 16:34:41 -0400 Subject: Improve Rustdoc's handling of procedural macros Fixes #58700 Fixes #58696 Fixes #49553 Fixes #52210 This commit removes the special rustdoc handling for proc macros, as we can now retrieve their span and attributes just like any other item. A new command-line option is added to rustdoc: `--crate-type`. This takes the same options as rustc's `--crate-type` option. However, all values other than `proc-macro` are treated the same. This allows Rustdoc to enable 'proc macro mode' when handling a proc macro crate. In compiletest, a new 'rustdoc-flags' option is added. This allows us to pass in the '--proc-macro-crate' flag in the absence of Cargo. I've opened [an additional PR to Cargo](https://github.com/rust-lang/cargo/pull/7159) to support passing in this flag. These two PRS can be merged in any order - the Cargo changes will not take effect until the 'cargo' submodule is updated in this repository. --- src/librustdoc/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/librustdoc/lib.rs') diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e30b35937db..bb83ff64b2f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -46,7 +46,7 @@ use std::panic; use std::process; use rustc::session::{early_warn, early_error}; -use rustc::session::config::{ErrorOutputType, RustcOptGroup}; +use rustc::session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option}; #[macro_use] mod externalfiles; @@ -132,6 +132,7 @@ fn opts() -> Vec { stable("crate-name", |o| { o.optopt("", "crate-name", "specify the name of this crate", "NAME") }), + make_crate_type_option(), stable("L", |o| { o.optmulti("L", "library-path", "directory to add to crate search path", "DIR") -- cgit 1.4.1-3-g733a5 From a77247acb1bbecff8acedefce3c6cc06d7787311 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 11 Aug 2019 16:11:53 -0400 Subject: Move source HTML generation to own module --- src/librustdoc/html/render.rs | 179 ++------------------------------------- src/librustdoc/html/sources.rs | 187 +++++++++++++++++++++++++++++++++++++++++ src/librustdoc/lib.rs | 1 + 3 files changed, 194 insertions(+), 173 deletions(-) create mode 100644 src/librustdoc/html/sources.rs (limited to 'src/librustdoc/lib.rs') diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index b64e74468e6..c8921a92d33 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -72,6 +72,7 @@ use crate::html::format::fmt_impl_for_trait_page; use crate::html::item_type::ItemType; use crate::html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, ErrorCodes, IdMap}; use crate::html::{highlight, layout, static_files}; +use crate::html::sources; use minifier; @@ -173,7 +174,7 @@ struct Context { playground: Option, } -struct SharedContext { +crate struct SharedContext { /// The path to the crate root source minus the file name. /// Used for simplifying paths to the highlighted source code files. pub src_root: PathBuf, @@ -218,7 +219,7 @@ struct SharedContext { } impl SharedContext { - fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { + crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { let mut dirs = self.created_dirs.borrow_mut(); if !dirs.contains(dst) { try_err!(self.fs.create_dir_all(dst), dst); @@ -389,18 +390,6 @@ pub struct RenderInfo { pub owned_box_did: Option, } -/// Helper struct to render all source code to HTML pages -struct SourceCollector<'a> { - scx: &'a mut SharedContext, - - /// Root destination to place all HTML output into - dst: PathBuf, -} - -/// Wrapper struct to render the source code of a file. This will do things like -/// adding line numbers to the left-hand side. -struct Source<'a>(&'a str); - // Helper structs for rendering items/sidebars and carrying along contextual // information @@ -612,7 +601,7 @@ pub fn run(mut krate: clean::Crate, } let dst = output; scx.ensure_dir(&dst)?; - krate = render_sources(&dst, &mut scx, krate)?; + krate = sources::render(&dst, &mut scx, krate)?; let mut cx = Context { current: Vec::new(), dst, @@ -1293,18 +1282,6 @@ themePicker.onblur = handleThemeButtonsBlur; Ok(()) } -fn render_sources(dst: &Path, scx: &mut SharedContext, - krate: clean::Crate) -> Result { - info!("emitting source files"); - let dst = dst.join("src").join(&krate.name); - scx.ensure_dir(&dst)?; - let mut folder = SourceCollector { - dst, - scx, - }; - Ok(folder.fold_crate(krate)) -} - fn write_minify(fs:&DocFS, dst: PathBuf, contents: &str, enable_minification: bool ) -> Result<(), Error> { if enable_minification { @@ -1384,33 +1361,6 @@ fn write_minify_replacer( } } -/// Takes a path to a source file and cleans the path to it. This canonicalizes -/// things like ".." to components which preserve the "top down" hierarchy of a -/// static HTML tree. Each component in the cleaned path will be passed as an -/// argument to `f`. The very last component of the path (ie the file name) will -/// be passed to `f` if `keep_filename` is true, and ignored otherwise. -fn clean_srcpath(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) -where - F: FnMut(&OsStr), -{ - // make it relative, if possible - let p = p.strip_prefix(src_root).unwrap_or(p); - - let mut iter = p.components().peekable(); - - while let Some(c) = iter.next() { - if !keep_filename && iter.peek().is_none() { - break; - } - - match c { - Component::ParentDir => f("up".as_ref()), - Component::Normal(c) => f(c), - _ => continue, - } - } -} - /// Attempts to find where an external crate is located, given that we're /// rendering in to the specified source destination. fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path) @@ -1444,102 +1394,6 @@ fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Pat }).next().unwrap_or(Unknown) // Well, at least we tried. } -impl<'a> DocFolder for SourceCollector<'a> { - fn fold_item(&mut self, item: clean::Item) -> Option { - // If we're including source files, and we haven't seen this file yet, - // then we need to render it out to the filesystem. - if self.scx.include_sources - // skip all invalid or macro spans - && item.source.filename.is_real() - // skip non-local items - && item.def_id.is_local() { - - // If it turns out that we couldn't read this file, then we probably - // can't read any of the files (generating html output from json or - // something like that), so just don't include sources for the - // entire crate. The other option is maintaining this mapping on a - // per-file basis, but that's probably not worth it... - self.scx - .include_sources = match self.emit_source(&item.source.filename) { - Ok(()) => true, - Err(e) => { - println!("warning: source code was requested to be rendered, \ - but processing `{}` had an error: {}", - item.source.filename, e); - println!(" skipping rendering of source code"); - false - } - }; - } - self.fold_item_recur(item) - } -} - -impl<'a> SourceCollector<'a> { - /// Renders the given filename into its corresponding HTML source file. - fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> { - let p = match *filename { - FileName::Real(ref file) => file, - _ => return Ok(()), - }; - if self.scx.local_sources.contains_key(&**p) { - // We've already emitted this source - return Ok(()); - } - - let contents = try_err!(fs::read_to_string(&p), &p); - - // Remove the utf-8 BOM if any - let contents = if contents.starts_with("\u{feff}") { - &contents[3..] - } else { - &contents[..] - }; - - // Create the intermediate directories - let mut cur = self.dst.clone(); - let mut root_path = String::from("../../"); - let mut href = String::new(); - clean_srcpath(&self.scx.src_root, &p, false, |component| { - cur.push(component); - root_path.push_str("../"); - href.push_str(&component.to_string_lossy()); - href.push('/'); - }); - self.scx.ensure_dir(&cur)?; - let mut fname = p.file_name() - .expect("source has no filename") - .to_os_string(); - fname.push(".html"); - cur.push(&fname); - href.push_str(&fname.to_string_lossy()); - - let mut v = Vec::new(); - let title = format!("{} -- source", cur.file_name().expect("failed to get file name") - .to_string_lossy()); - let desc = format!("Source to the Rust file `{}`.", filename); - let page = layout::Page { - title: &title, - css_class: "source", - root_path: &root_path, - static_root_path: self.scx.static_root_path.as_deref(), - description: &desc, - keywords: BASIC_KEYWORDS, - resource_suffix: &self.scx.resource_suffix, - extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)], - static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)], - }; - try_err!(layout::render(&mut v, &self.scx.layout, - &page, &(""), &Source(contents), - self.scx.css_file_extension.is_some(), - &self.scx.themes, - self.scx.generate_search_filter), &cur); - self.scx.fs.write(&cur, &v)?; - self.scx.local_sources.insert(p.clone(), href); - Ok(()) - } -} - impl DocFolder for Cache { fn fold_item(&mut self, item: clean::Item) -> Option { if item.def_id.is_local() { @@ -2399,7 +2253,7 @@ impl<'a> Item<'a> { (_, _, Unknown) => return None, }; - clean_srcpath(&src_root, file, false, |component| { + sources::clean_path(&src_root, file, false, |component| { path.push_str(&component.to_string_lossy()); path.push('/'); }); @@ -5048,27 +4902,6 @@ fn sidebar_foreign_type(fmt: &mut fmt::Formatter<'_>, it: &clean::Item) -> fmt:: Ok(()) } -impl<'a> fmt::Display for Source<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let Source(s) = *self; - let lines = s.lines().count(); - let mut cols = 0; - let mut tmp = lines; - while tmp > 0 { - cols += 1; - tmp /= 10; - } - write!(fmt, "
")?;
-        for i in 1..=lines {
-            write!(fmt, "{0:1$}\n", i, cols)?;
-        }
-        write!(fmt, "
")?; - write!(fmt, "{}", - highlight::render_with_highlighting(s, None, None, None))?; - Ok(()) - } -} - fn item_macro(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item, t: &clean::Macro) -> fmt::Result { wrap_into_docblock(w, |w| { @@ -5125,7 +4958,7 @@ fn item_keyword(w: &mut fmt::Formatter<'_>, cx: &Context, document(w, cx, it) } -const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang"; +crate const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang"; fn make_item_keywords(it: &clean::Item) -> String { format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs new file mode 100644 index 00000000000..c1f1f59d914 --- /dev/null +++ b/src/librustdoc/html/sources.rs @@ -0,0 +1,187 @@ +use crate::clean; +use crate::docfs::PathError; +use crate::fold::DocFolder; +use crate::html::layout; +use crate::html::render::{Error, SharedContext, BASIC_KEYWORDS}; +use crate::html::highlight; +use std::ffi::OsStr; +use std::fs; +use std::path::{Component, Path, PathBuf}; +use std::fmt; +use syntax::source_map::FileName; + +crate fn render(dst: &Path, scx: &mut SharedContext, + krate: clean::Crate) -> Result { + info!("emitting source files"); + let dst = dst.join("src").join(&krate.name); + scx.ensure_dir(&dst)?; + let mut folder = SourceCollector { + dst, + scx, + }; + Ok(folder.fold_crate(krate)) +} + +/// Helper struct to render all source code to HTML pages +struct SourceCollector<'a> { + scx: &'a mut SharedContext, + + /// Root destination to place all HTML output into + dst: PathBuf, +} + +impl<'a> DocFolder for SourceCollector<'a> { + fn fold_item(&mut self, item: clean::Item) -> Option { + // If we're including source files, and we haven't seen this file yet, + // then we need to render it out to the filesystem. + if self.scx.include_sources + // skip all invalid or macro spans + && item.source.filename.is_real() + // skip non-local items + && item.def_id.is_local() { + + // If it turns out that we couldn't read this file, then we probably + // can't read any of the files (generating html output from json or + // something like that), so just don't include sources for the + // entire crate. The other option is maintaining this mapping on a + // per-file basis, but that's probably not worth it... + self.scx + .include_sources = match self.emit_source(&item.source.filename) { + Ok(()) => true, + Err(e) => { + println!("warning: source code was requested to be rendered, \ + but processing `{}` had an error: {}", + item.source.filename, e); + println!(" skipping rendering of source code"); + false + } + }; + } + self.fold_item_recur(item) + } +} + +impl<'a> SourceCollector<'a> { + /// Renders the given filename into its corresponding HTML source file. + fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> { + let p = match *filename { + FileName::Real(ref file) => file, + _ => return Ok(()), + }; + if self.scx.local_sources.contains_key(&**p) { + // We've already emitted this source + return Ok(()); + } + + let contents = match fs::read_to_string(&p) { + Ok(contents) => contents, + Err(e) => { + return Err(Error::new(e, &p)); + } + }; + + // Remove the utf-8 BOM if any + let contents = if contents.starts_with("\u{feff}") { + &contents[3..] + } else { + &contents[..] + }; + + // Create the intermediate directories + let mut cur = self.dst.clone(); + let mut root_path = String::from("../../"); + let mut href = String::new(); + clean_path(&self.scx.src_root, &p, false, |component| { + cur.push(component); + root_path.push_str("../"); + href.push_str(&component.to_string_lossy()); + href.push('/'); + }); + self.scx.ensure_dir(&cur)?; + let mut fname = p.file_name() + .expect("source has no filename") + .to_os_string(); + fname.push(".html"); + cur.push(&fname); + href.push_str(&fname.to_string_lossy()); + + let mut v = Vec::new(); + let title = format!("{} -- source", cur.file_name().expect("failed to get file name") + .to_string_lossy()); + let desc = format!("Source to the Rust file `{}`.", filename); + let page = layout::Page { + title: &title, + css_class: "source", + root_path: &root_path, + static_root_path: self.scx.static_root_path.as_deref(), + description: &desc, + keywords: BASIC_KEYWORDS, + resource_suffix: &self.scx.resource_suffix, + extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)], + static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)], + }; + let result = layout::render(&mut v, &self.scx.layout, + &page, &(""), &Source(contents), + self.scx.css_file_extension.is_some(), + &self.scx.themes, + self.scx.generate_search_filter); + if let Err(e) = result { + return Err(Error::new(e, &cur)); + } + self.scx.fs.write(&cur, &v)?; + self.scx.local_sources.insert(p.clone(), href); + Ok(()) + } +} + +/// Takes a path to a source file and cleans the path to it. This canonicalizes +/// things like ".." to components which preserve the "top down" hierarchy of a +/// static HTML tree. Each component in the cleaned path will be passed as an +/// argument to `f`. The very last component of the path (ie the file name) will +/// be passed to `f` if `keep_filename` is true, and ignored otherwise. +pub fn clean_path(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) +where + F: FnMut(&OsStr), +{ + // make it relative, if possible + let p = p.strip_prefix(src_root).unwrap_or(p); + + let mut iter = p.components().peekable(); + + while let Some(c) = iter.next() { + if !keep_filename && iter.peek().is_none() { + break; + } + + match c { + Component::ParentDir => f("up".as_ref()), + Component::Normal(c) => f(c), + _ => continue, + } + } +} + +/// Wrapper struct to render the source code of a file. This will do things like +/// adding line numbers to the left-hand side. +struct Source<'a>(&'a str); + +impl<'a> fmt::Display for Source<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let Source(s) = *self; + let lines = s.lines().count(); + let mut cols = 0; + let mut tmp = lines; + while tmp > 0 { + cols += 1; + tmp /= 10; + } + write!(fmt, "
")?;
+        for i in 1..=lines {
+            write!(fmt, "{0:1$}\n", i, cols)?;
+        }
+        write!(fmt, "
")?; + write!(fmt, "{}", + highlight::render_with_highlighting(s, None, None, None))?; + Ok(()) + } +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e30b35937db..968578f957c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -67,6 +67,7 @@ pub mod html { crate mod render; crate mod static_files; crate mod toc; + crate mod sources; } mod markdown; mod passes; -- cgit 1.4.1-3-g733a5