From d3d6f76887921d2e2ac5444790872bd030c1b669 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Fri, 27 Jul 2018 10:22:16 -0500 Subject: [PATCH] introduce "early passes" an convert a few over --- src/librustdoc/core.rs | 74 +++++++++++++++++++-- src/librustdoc/lib.rs | 66 ++---------------- src/librustdoc/passes/mod.rs | 67 ++++++++++++++++++- src/librustdoc/passes/strip_hidden.rs | 7 +- src/librustdoc/passes/strip_priv_imports.rs | 5 +- src/librustdoc/passes/strip_private.rs | 5 +- 6 files changed, 150 insertions(+), 74 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 88faea9514f..f6310417dac 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -45,8 +45,9 @@ use std::path::PathBuf; use visit_ast::RustdocVisitor; use clean; -use clean::{get_path_for_type, Clean, MAX_DEF_ID}; +use clean::{get_path_for_type, Clean, MAX_DEF_ID, AttributesExt}; use html::render::RenderInfo; +use passes; pub use rustc::session::config::{Input, Options, CodegenOptions}; pub use rustc::session::search_paths::SearchPaths; @@ -322,7 +323,9 @@ pub fn run_core(search_paths: SearchPaths, error_format: ErrorOutputType, cmd_lints: Vec<(String, lint::Level)>, lint_cap: Option, - describe_lints: bool) -> (clean::Crate, RenderInfo) + describe_lints: bool, + mut manual_passes: Vec, + mut default_passes: passes::DefaultPassOption) -> (clean::Crate, RenderInfo, Vec) { // Parse, resolve, and typecheck the given crate. @@ -527,13 +530,76 @@ pub fn run_core(search_paths: SearchPaths, }; debug!("crate: {:?}", tcx.hir.krate()); - let krate = { + let mut krate = { let mut v = RustdocVisitor::new(&ctxt); v.visit(tcx.hir.krate()); v.clean(&ctxt) }; - (krate, ctxt.renderinfo.into_inner()) + fn report_deprecated_attr(name: &str, diag: &errors::Handler) { + let mut msg = diag.struct_warn(&format!("the `#![doc({})]` attribute is \ + considered deprecated", name)); + msg.warn("please see https://github.com/rust-lang/rust/issues/44136"); + + if name == "no_default_passes" { + msg.help("you may want to use `#![doc(document_private_items)]`"); + } + + msg.emit(); + } + + // Process all of the crate attributes, extracting plugin metadata along + // with the passes which we are supposed to run. + for attr in krate.module.as_ref().unwrap().attrs.lists("doc") { + let diag = ctxt.sess().diagnostic(); + + let name = attr.name().map(|s| s.as_str()); + let name = name.as_ref().map(|s| &s[..]); + if attr.is_word() { + if name == Some("no_default_passes") { + report_deprecated_attr("no_default_passes", diag); + if default_passes == passes::DefaultPassOption::Default { + default_passes = passes::DefaultPassOption::None; + } + } + } else if let Some(value) = attr.value_str() { + let sink = match name { + Some("passes") => { + report_deprecated_attr("passes = \"...\"", diag); + &mut manual_passes + }, + Some("plugins") => { + report_deprecated_attr("plugins = \"...\"", diag); + eprintln!("WARNING: #![doc(plugins = \"...\")] no longer functions; \ + see CVE-2018-1000622"); + continue + }, + _ => continue, + }; + for p in value.as_str().split_whitespace() { + sink.push(p.to_string()); + } + } + + if attr.is_word() && name == Some("document_private_items") { + if default_passes == passes::DefaultPassOption::Default { + default_passes = passes::DefaultPassOption::Private; + } + } + } + + let mut passes: Vec = + passes::defaults(default_passes).iter().map(|p| p.to_string()).collect(); + passes.extend(manual_passes); + + for pass in &passes { + // the "unknown pass" error will be reported when late passes are run + if let Some(pass) = passes::find_pass(pass).and_then(|p| p.early_fn()) { + krate = pass(krate, &ctxt); + } + } + + (krate, ctxt.renderinfo.into_inner(), passes) }), &sess) }) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f59d3fb68bf..bd7f7386fd1 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -97,8 +97,6 @@ mod visit_lib; mod test; mod theme; -use clean::AttributesExt; - struct Output { krate: clean::Crate, renderinfo: html::render::RenderInfo, @@ -631,7 +629,7 @@ fn rust_input(cratefile: PathBuf, where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { - let mut default_passes = if matches.opt_present("no-defaults") { + let default_passes = if matches.opt_present("no-defaults") { passes::DefaultPassOption::None } else if matches.opt_present("document-private-items") { passes::DefaultPassOption::Private @@ -639,8 +637,8 @@ where R: 'static + Send, passes::DefaultPassOption::Default }; - let mut manual_passes = matches.opt_strs("passes"); - let mut plugins = matches.opt_strs("plugins"); + let manual_passes = matches.opt_strs("passes"); + let plugins = matches.opt_strs("plugins"); // First, parse the crate and extract all relevant information. let mut paths = SearchPaths::new(); @@ -674,11 +672,11 @@ where R: 'static + Send, let result = rustc_driver::monitor(move || syntax::with_globals(move || { use rustc::session::config::Input; - let (mut krate, renderinfo) = + let (mut krate, renderinfo, passes) = core::run_core(paths, cfgs, externs, Input::File(cratefile), triple, maybe_sysroot, display_warnings, crate_name.clone(), force_unstable_if_unmarked, edition, cg, error_format, - lint_opts, lint_cap, describe_lints); + lint_opts, lint_cap, describe_lints, manual_passes, default_passes); info!("finished with rustc"); @@ -688,58 +686,6 @@ where R: 'static + Send, krate.version = crate_version; - let diag = core::new_handler(error_format, None); - - fn report_deprecated_attr(name: &str, diag: &errors::Handler) { - let mut msg = diag.struct_warn(&format!("the `#![doc({})]` attribute is \ - considered deprecated", name)); - msg.warn("please see https://github.com/rust-lang/rust/issues/44136"); - - if name == "no_default_passes" { - msg.help("you may want to use `#![doc(document_private_items)]`"); - } - - msg.emit(); - } - - // Process all of the crate attributes, extracting plugin metadata along - // with the passes which we are supposed to run. - for attr in krate.module.as_ref().unwrap().attrs.lists("doc") { - let name = attr.name().map(|s| s.as_str()); - let name = name.as_ref().map(|s| &s[..]); - if attr.is_word() { - if name == Some("no_default_passes") { - report_deprecated_attr("no_default_passes", &diag); - if default_passes == passes::DefaultPassOption::Default { - default_passes = passes::DefaultPassOption::None; - } - } - } else if let Some(value) = attr.value_str() { - let sink = match name { - Some("passes") => { - report_deprecated_attr("passes = \"...\"", &diag); - &mut manual_passes - }, - Some("plugins") => { - report_deprecated_attr("plugins = \"...\"", &diag); - &mut plugins - }, - _ => continue, - }; - sink.extend(value.as_str().split_whitespace().map(|p| p.to_string())); - } - - if attr.is_word() && name == Some("document_private_items") { - if default_passes == passes::DefaultPassOption::Default { - default_passes = passes::DefaultPassOption::Private; - } - } - } - - let mut passes: Vec = - passes::defaults(default_passes).iter().map(|p| p.to_string()).collect(); - passes.extend(manual_passes); - if !plugins.is_empty() { eprintln!("WARNING: --plugins no longer functions; see CVE-2018-1000622"); } @@ -752,7 +698,7 @@ where R: 'static + Send, for pass in &passes { // determine if we know about this pass - let pass = match passes::PASSES.iter().find(|p| p.name() == pass) { + let pass = match passes::find_pass(pass) { Some(pass) => if let Some(pass) = pass.late_fn() { pass } else { diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index e3f867d7fb1..329e489f7b7 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -8,12 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Contains information about "passes", used to modify crate information during the documentation +//! process. + use rustc::hir::def_id::DefId; use rustc::middle::privacy::AccessLevels; use rustc::util::nodemap::DefIdSet; use std::mem; +use std::fmt; use clean::{self, GetDefId, Item}; +use core::DocContext; use fold; use fold::StripItem; @@ -35,41 +40,87 @@ pub use self::unindent_comments::UNINDENT_COMMENTS; mod propagate_doc_cfg; pub use self::propagate_doc_cfg::PROPAGATE_DOC_CFG; -#[derive(Copy, Clone, Debug)] +/// Represents a single pass. +#[derive(Copy, Clone)] pub enum Pass { + /// An "early pass" is run in the compiler context, and can gather information about types and + /// traits and the like. + EarlyPass { + name: &'static str, + pass: fn(clean::Crate, &DocContext) -> clean::Crate, + description: &'static str, + }, + /// A "late pass" is run between crate cleaning and page generation. LatePass { name: &'static str, pass: fn(clean::Crate) -> clean::Crate, description: &'static str, + }, +} + +impl fmt::Debug for Pass { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut dbg = match *self { + Pass::EarlyPass { .. } => f.debug_struct("EarlyPass"), + Pass::LatePass { .. } => f.debug_struct("LatePass"), + }; + + dbg.field("name", &self.name()) + .field("pass", &"...") + .field("description", &self.description()) + .finish() } } impl Pass { + /// Constructs a new early pass. + pub const fn early(name: &'static str, + pass: fn(clean::Crate, &DocContext) -> clean::Crate, + description: &'static str) -> Pass { + Pass::EarlyPass { name, pass, description } + } + + /// Constructs a new late pass. pub const fn late(name: &'static str, pass: fn(clean::Crate) -> clean::Crate, description: &'static str) -> Pass { Pass::LatePass { name, pass, description } } + /// Returns the name of this pass. pub fn name(self) -> &'static str { match self { - Pass::LatePass { name, .. } => name, + Pass::EarlyPass { name, .. } | + Pass::LatePass { name, .. } => name, } } + /// Returns the description of this pass. pub fn description(self) -> &'static str { match self { - Pass::LatePass { description, .. } => description, + Pass::EarlyPass { description, .. } | + Pass::LatePass { description, .. } => description, } } + /// If this pass is an early pass, returns the pointer to its function. + pub fn early_fn(self) -> Option clean::Crate> { + match self { + Pass::EarlyPass { pass, .. } => Some(pass), + _ => None, + } + } + + /// If this pass is a late pass, returns the pointer to its function. pub fn late_fn(self) -> Option clean::Crate> { match self { Pass::LatePass { pass, .. } => Some(pass), + _ => None, } } } +/// The full list of passes. pub const PASSES: &'static [Pass] = &[ STRIP_HIDDEN, UNINDENT_COMMENTS, @@ -79,6 +130,7 @@ pub const PASSES: &'static [Pass] = &[ PROPAGATE_DOC_CFG, ]; +/// The list of passes run by default. pub const DEFAULT_PASSES: &'static [&'static str] = &[ "strip-hidden", "strip-private", @@ -87,6 +139,7 @@ pub const DEFAULT_PASSES: &'static [&'static str] = &[ "propagate-doc-cfg", ]; +/// The list of default passes run with `--document-private-items` is passed to rustdoc. pub const DEFAULT_PRIVATE_PASSES: &'static [&'static str] = &[ "strip-priv-imports", "collapse-docs", @@ -94,6 +147,8 @@ pub const DEFAULT_PRIVATE_PASSES: &'static [&'static str] = &[ "propagate-doc-cfg", ]; +/// A shorthand way to refer to which set of passes to use, based on the presence of +/// `--no-defaults` or `--document-private-items`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DefaultPassOption { Default, @@ -101,6 +156,7 @@ pub enum DefaultPassOption { None, } +/// Returns the given default set of passes. pub fn defaults(default_set: DefaultPassOption) -> &'static [&'static str] { match default_set { DefaultPassOption::Default => DEFAULT_PASSES, @@ -109,6 +165,11 @@ pub fn defaults(default_set: DefaultPassOption) -> &'static [&'static str] { } } +/// If the given name matches a known pass, returns its information. +pub fn find_pass(pass_name: &str) -> Option { + PASSES.iter().find(|p| p.name() == pass_name).cloned() +} + struct Stripper<'a> { retained: &'a mut DefIdSet, access_levels: &'a AccessLevels, diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 2093da41222..cc0b6fb6d67 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -13,17 +13,18 @@ use std::mem; use clean::{self, AttributesExt, NestedAttributesExt}; use clean::Item; +use core::DocContext; use fold; use fold::DocFolder; use fold::StripItem; use passes::{ImplStripper, Pass}; pub const STRIP_HIDDEN: Pass = - Pass::late("strip-hidden", strip_hidden, - "strips all doc(hidden) items from the output"); + Pass::early("strip-hidden", strip_hidden, + "strips all doc(hidden) items from the output"); /// Strip items marked `#[doc(hidden)]` -pub fn strip_hidden(krate: clean::Crate) -> clean::Crate { +pub fn strip_hidden(krate: clean::Crate, _: &DocContext) -> clean::Crate { let mut retained = DefIdSet(); // strip all #[doc(hidden)] items diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 62f8b1092c7..f01c333d742 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -9,12 +9,13 @@ // except according to those terms. use clean; +use core::DocContext; use fold::DocFolder; use passes::{ImportStripper, Pass}; -pub const STRIP_PRIV_IMPORTS: Pass = Pass::late("strip-priv-imports", strip_priv_imports, +pub const STRIP_PRIV_IMPORTS: Pass = Pass::early("strip-priv-imports", strip_priv_imports, "strips all private import statements (`use`, `extern crate`) from a crate"); -pub fn strip_priv_imports(krate: clean::Crate) -> clean::Crate { +pub fn strip_priv_imports(krate: clean::Crate, _: &DocContext) -> clean::Crate { ImportStripper.fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 0011aedd80a..3b17a768ffd 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -11,17 +11,18 @@ use rustc::util::nodemap::DefIdSet; use clean; +use core::DocContext; use fold::DocFolder; use passes::{ImplStripper, ImportStripper, Stripper, Pass}; pub const STRIP_PRIVATE: Pass = - Pass::late("strip-private", strip_private, + Pass::early("strip-private", strip_private, "strips all private items from a crate which cannot be seen externally, \ implies strip-priv-imports"); /// Strip private items from the point of view of a crate or externally from a /// crate, specified by the `xcrate` flag. -pub fn strip_private(mut krate: clean::Crate) -> clean::Crate { +pub fn strip_private(mut krate: clean::Crate, _: &DocContext) -> clean::Crate { // This stripper collects all *retained* nodes. let mut retained = DefIdSet(); let access_levels = krate.access_levels.clone();