]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #55515 - QuietMisdreavus:rustdoc-config, r=GuillaumeGomez
authorbors <bors@rust-lang.org>
Mon, 5 Nov 2018 09:48:46 +0000 (09:48 +0000)
committerbors <bors@rust-lang.org>
Mon, 5 Nov 2018 09:48:46 +0000 (09:48 +0000)
rustdoc: refactor: centralize all command-line argument parsing

This is something i've wanted to do for a while, since we keep having to add new arguments to places like `rust_input` or `core::run_core` whenever we add a new CLI flag or the like. Those functions have inflated up to 11-19, and in some cases hiding away the locations where some CLI flags were being parsed, obscuring their use. Now, we have a central place where all command-line configuration occurs, including argument validation.

One note about the design: i grouped together all the arguments that `html::render::run` needed, so that i could pass them on from compilation in one lump instead of trying to thread through individual items or clone the entire blob ahead of time.

One other thing this adds is that rustdoc also now recognizes all the `-Z` options that rustc does, since we were manually grabbing a few previously. Now we parse a full `DebuggingOptions` struct and hand it directly to rustc when scraping docs.

src/librustdoc/config.rs [new file with mode: 0644]
src/librustdoc/core.rs
src/librustdoc/externalfiles.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render.rs
src/librustdoc/lib.rs
src/librustdoc/markdown.rs
src/librustdoc/test.rs
src/test/rustdoc-ui/failed-doctest-output.stdout

diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
new file mode 100644 (file)
index 0000000..903aafe
--- /dev/null
@@ -0,0 +1,560 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::{BTreeMap, BTreeSet};
+use std::fmt;
+use std::path::PathBuf;
+
+use errors;
+use errors::emitter::ColorConfig;
+use getopts;
+use rustc::lint::Level;
+use rustc::session::early_error;
+use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
+use rustc::session::config::{nightly_options, build_codegen_options, build_debugging_options,
+                             get_cmd_lint_options};
+use rustc::session::search_paths::SearchPaths;
+use rustc_driver;
+use rustc_target::spec::TargetTriple;
+use syntax::edition::Edition;
+
+use core::new_handler;
+use externalfiles::ExternalHtml;
+use html;
+use html::markdown::IdMap;
+use opts;
+use passes::{self, DefaultPassOption};
+use theme;
+
+/// Configuration options for rustdoc.
+#[derive(Clone)]
+pub struct Options {
+    // Basic options / Options passed directly to rustc
+
+    /// The crate root or Markdown file to load.
+    pub input: PathBuf,
+    /// The name of the crate being documented.
+    pub crate_name: Option<String>,
+    /// How to format errors and warnings.
+    pub error_format: ErrorOutputType,
+    /// Library search paths to hand to the compiler.
+    pub libs: SearchPaths,
+    /// The list of external crates to link against.
+    pub externs: Externs,
+    /// List of `cfg` flags to hand to the compiler. Always includes `rustdoc`.
+    pub cfgs: Vec<String>,
+    /// Codegen options to hand to the compiler.
+    pub codegen_options: CodegenOptions,
+    /// Debugging (`-Z`) options to pass to the compiler.
+    pub debugging_options: DebuggingOptions,
+    /// The target used to compile the crate against.
+    pub target: Option<TargetTriple>,
+    /// Edition used when reading the crate. Defaults to "2015". Also used by default when
+    /// compiling doctests from the crate.
+    pub edition: Edition,
+    /// The path to the sysroot. Used during the compilation process.
+    pub maybe_sysroot: Option<PathBuf>,
+    /// Linker to use when building doctests.
+    pub linker: Option<PathBuf>,
+    /// Lint information passed over the command-line.
+    pub lint_opts: Vec<(String, Level)>,
+    /// Whether to ask rustc to describe the lints it knows. Practically speaking, this will not be
+    /// used, since we abort if we have no input file, but it's included for completeness.
+    pub describe_lints: bool,
+    /// What level to cap lints at.
+    pub lint_cap: Option<Level>,
+
+    // Options specific to running doctests
+
+    /// Whether we should run doctests instead of generating docs.
+    pub should_test: bool,
+    /// List of arguments to pass to the test harness, if running tests.
+    pub test_args: Vec<String>,
+
+    // Options that affect the documentation process
+
+    /// The selected default set of passes to use.
+    ///
+    /// Be aware: This option can come both from the CLI and from crate attributes!
+    pub default_passes: DefaultPassOption,
+    /// Any passes manually selected by the user.
+    ///
+    /// Be aware: This option can come both from the CLI and from crate attributes!
+    pub manual_passes: Vec<String>,
+    /// Whether to display warnings during doc generation or while gathering doctests. By default,
+    /// all non-rustdoc-specific lints are allowed when generating docs.
+    pub display_warnings: bool,
+
+    // Options that alter generated documentation pages
+
+    /// Crate version to note on the sidebar of generated docs.
+    pub crate_version: Option<String>,
+    /// Collected options specific to outputting final pages.
+    pub render_options: RenderOptions,
+}
+
+impl fmt::Debug for Options {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        struct FmtExterns<'a>(&'a Externs);
+
+        impl<'a> fmt::Debug for FmtExterns<'a> {
+            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                f.debug_map()
+                    .entries(self.0.iter())
+                    .finish()
+            }
+        }
+
+        f.debug_struct("Options")
+            .field("input", &self.input)
+            .field("crate_name", &self.crate_name)
+            .field("error_format", &self.error_format)
+            .field("libs", &self.libs)
+            .field("externs", &FmtExterns(&self.externs))
+            .field("cfgs", &self.cfgs)
+            .field("codegen_options", &"...")
+            .field("debugging_options", &"...")
+            .field("target", &self.target)
+            .field("edition", &self.edition)
+            .field("maybe_sysroot", &self.maybe_sysroot)
+            .field("linker", &self.linker)
+            .field("lint_opts", &self.lint_opts)
+            .field("describe_lints", &self.describe_lints)
+            .field("lint_cap", &self.lint_cap)
+            .field("should_test", &self.should_test)
+            .field("test_args", &self.test_args)
+            .field("default_passes", &self.default_passes)
+            .field("manual_passes", &self.manual_passes)
+            .field("display_warnings", &self.display_warnings)
+            .field("crate_version", &self.crate_version)
+            .field("render_options", &self.render_options)
+            .finish()
+    }
+}
+
+/// Configuration options for the HTML page-creation process.
+#[derive(Clone, Debug)]
+pub struct RenderOptions {
+    /// Output directory to generate docs into. Defaults to `doc`.
+    pub output: PathBuf,
+    /// External files to insert into generated pages.
+    pub external_html: ExternalHtml,
+    /// A pre-populated `IdMap` with the default headings and any headings added by Markdown files
+    /// processed by `external_html`.
+    pub id_map: IdMap,
+    /// If present, playground URL to use in the "Run" button added to code samples.
+    ///
+    /// Be aware: This option can come both from the CLI and from crate attributes!
+    pub playground_url: Option<String>,
+    /// Whether to sort modules alphabetically on a module page instead of using declaration order.
+    /// `true` by default.
+    ///
+    /// FIXME(misdreavus): the flag name is `--sort-modules-by-appearance` but the meaning is
+    /// inverted once read
+    pub sort_modules_alphabetically: bool,
+    /// List of themes to extend the docs with. Original argument name is included to assist in
+    /// displaying errors if it fails a theme check.
+    pub themes: Vec<PathBuf>,
+    /// If present, CSS file that contains rules to add to the default CSS.
+    pub extension_css: Option<PathBuf>,
+    /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`.
+    pub extern_html_root_urls: BTreeMap<String, String>,
+    /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
+    pub resource_suffix: String,
+    /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
+    /// default.
+    ///
+    /// FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted
+    /// once read
+    pub enable_minification: bool,
+    /// Whether to create an index page in the root of the output directory. If this is true but
+    /// `enable_index_page` is None, generate a static listing of crates instead.
+    pub enable_index_page: bool,
+    /// A file to use as the index page at the root of the output directory. Overrides
+    /// `enable_index_page` to be true if set.
+    pub index_page: Option<PathBuf>,
+
+    // Options specific to reading standalone Markdown files
+
+    /// Whether to generate a table of contents on the output file when reading a standalone
+    /// Markdown file.
+    pub markdown_no_toc: bool,
+    /// Additional CSS files to link in pages generated from standlone Markdown files.
+    pub markdown_css: Vec<String>,
+    /// If present, playground URL to use in the "Run" button added to code samples generated from
+    /// standalone Markdown files. If not present, `playground_url` is used.
+    pub markdown_playground_url: Option<String>,
+}
+
+impl Options {
+    /// Parses the given command-line for options. If an error message or other early-return has
+    /// been printed, returns `Err` with the exit code.
+    pub fn from_matches(matches: &getopts::Matches) -> Result<Options, isize> {
+        // Check for unstable options.
+        nightly_options::check_nightly_options(&matches, &opts());
+
+        if matches.opt_present("h") || matches.opt_present("help") {
+            ::usage("rustdoc");
+            return Err(0);
+        } else if matches.opt_present("version") {
+            rustc_driver::version("rustdoc", &matches);
+            return Err(0);
+        }
+
+        if matches.opt_strs("passes") == ["list"] {
+            println!("Available passes for running rustdoc:");
+            for pass in passes::PASSES {
+                println!("{:>20} - {}", pass.name(), pass.description());
+            }
+            println!("\nDefault passes for rustdoc:");
+            for &name in passes::DEFAULT_PASSES {
+                println!("{:>20}", name);
+            }
+            println!("\nPasses run with `--document-private-items`:");
+            for &name in passes::DEFAULT_PRIVATE_PASSES {
+                println!("{:>20}", name);
+            }
+            return Err(0);
+        }
+
+        let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
+            Some("auto") => ColorConfig::Auto,
+            Some("always") => ColorConfig::Always,
+            Some("never") => ColorConfig::Never,
+            None => ColorConfig::Auto,
+            Some(arg) => {
+                early_error(ErrorOutputType::default(),
+                            &format!("argument for --color must be `auto`, `always` or `never` \
+                                      (instead was `{}`)", arg));
+            }
+        };
+        let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
+            Some("human") => ErrorOutputType::HumanReadable(color),
+            Some("json") => ErrorOutputType::Json(false),
+            Some("pretty-json") => ErrorOutputType::Json(true),
+            Some("short") => ErrorOutputType::Short(color),
+            None => ErrorOutputType::HumanReadable(color),
+            Some(arg) => {
+                early_error(ErrorOutputType::default(),
+                            &format!("argument for --error-format must be `human`, `json` or \
+                                      `short` (instead was `{}`)", arg));
+            }
+        };
+
+        let codegen_options = build_codegen_options(matches, error_format);
+        let debugging_options = build_debugging_options(matches, error_format);
+
+        let diag = new_handler(error_format,
+                               None,
+                               debugging_options.treat_err_as_bug,
+                               debugging_options.ui_testing);
+
+        // check for deprecated options
+        check_deprecated_options(&matches, &diag);
+
+        let to_check = matches.opt_strs("theme-checker");
+        if !to_check.is_empty() {
+            let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
+            let mut errors = 0;
+
+            println!("rustdoc: [theme-checker] Starting tests!");
+            for theme_file in to_check.iter() {
+                print!(" - Checking \"{}\"...", theme_file);
+                let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
+                if !differences.is_empty() || !success {
+                    println!(" FAILED");
+                    errors += 1;
+                    if !differences.is_empty() {
+                        println!("{}", differences.join("\n"));
+                    }
+                } else {
+                    println!(" OK");
+                }
+            }
+            if errors != 0 {
+                return Err(1);
+            }
+            return Err(0);
+        }
+
+        if matches.free.is_empty() {
+            diag.struct_err("missing file operand").emit();
+            return Err(1);
+        }
+        if matches.free.len() > 1 {
+            diag.struct_err("too many file operands").emit();
+            return Err(1);
+        }
+        let input = PathBuf::from(&matches.free[0]);
+
+        let mut libs = SearchPaths::new();
+        for s in &matches.opt_strs("L") {
+            libs.add_path(s, error_format);
+        }
+        let externs = match parse_externs(&matches) {
+            Ok(ex) => ex,
+            Err(err) => {
+                diag.struct_err(&err).emit();
+                return Err(1);
+            }
+        };
+        let extern_html_root_urls = match parse_extern_html_roots(&matches) {
+            Ok(ex) => ex,
+            Err(err) => {
+                diag.struct_err(err).emit();
+                return Err(1);
+            }
+        };
+
+        let test_args = matches.opt_strs("test-args");
+        let test_args: Vec<String> = test_args.iter()
+                                              .flat_map(|s| s.split_whitespace())
+                                              .map(|s| s.to_string())
+                                              .collect();
+
+        let should_test = matches.opt_present("test");
+
+        let output = matches.opt_str("o")
+                            .map(|s| PathBuf::from(&s))
+                            .unwrap_or_else(|| PathBuf::from("doc"));
+        let mut cfgs = matches.opt_strs("cfg");
+        cfgs.push("rustdoc".to_string());
+
+        let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s));
+
+        if let Some(ref p) = extension_css {
+            if !p.is_file() {
+                diag.struct_err("option --extend-css argument must be a file").emit();
+                return Err(1);
+            }
+        }
+
+        let mut themes = Vec::new();
+        if matches.opt_present("themes") {
+            let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
+
+            for (theme_file, theme_s) in matches.opt_strs("themes")
+                                                .iter()
+                                                .map(|s| (PathBuf::from(&s), s.to_owned())) {
+                if !theme_file.is_file() {
+                    diag.struct_err("option --themes arguments must all be files").emit();
+                    return Err(1);
+                }
+                let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
+                if !success || !ret.is_empty() {
+                    diag.struct_err(&format!("invalid theme: \"{}\"", theme_s))
+                        .help("check what's wrong with the --theme-checker option")
+                        .emit();
+                    return Err(1);
+                }
+                themes.push(theme_file);
+            }
+        }
+
+        let mut id_map = html::markdown::IdMap::new();
+        id_map.populate(html::render::initial_ids());
+        let external_html = match ExternalHtml::load(
+                &matches.opt_strs("html-in-header"),
+                &matches.opt_strs("html-before-content"),
+                &matches.opt_strs("html-after-content"),
+                &matches.opt_strs("markdown-before-content"),
+                &matches.opt_strs("markdown-after-content"), &diag, &mut id_map) {
+            Some(eh) => eh,
+            None => return Err(3),
+        };
+
+        let edition = matches.opt_str("edition").unwrap_or("2015".to_string());
+        let edition = match edition.parse() {
+            Ok(e) => e,
+            Err(_) => {
+                diag.struct_err("could not parse edition").emit();
+                return Err(1);
+            }
+        };
+
+        match matches.opt_str("r").as_ref().map(|s| &**s) {
+            Some("rust") | None => {}
+            Some(s) => {
+                diag.struct_err(&format!("unknown input format: {}", s)).emit();
+                return Err(1);
+            }
+        }
+
+        match matches.opt_str("w").as_ref().map(|s| &**s) {
+            Some("html") | None => {}
+            Some(s) => {
+                diag.struct_err(&format!("unknown output format: {}", s)).emit();
+                return Err(1);
+            }
+        }
+
+        let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s));
+        if let Some(ref index_page) = index_page {
+            if !index_page.is_file() {
+                diag.struct_err("option `--index-page` argument must be a file").emit();
+                return Err(1);
+            }
+        }
+
+        let target = matches.opt_str("target").map(|target| {
+            if target.ends_with(".json") {
+                TargetTriple::TargetPath(PathBuf::from(target))
+            } else {
+                TargetTriple::TargetTriple(target)
+            }
+        });
+
+        let default_passes = if matches.opt_present("no-defaults") {
+            passes::DefaultPassOption::None
+        } else if matches.opt_present("document-private-items") {
+            passes::DefaultPassOption::Private
+        } else {
+            passes::DefaultPassOption::Default
+        };
+        let manual_passes = matches.opt_strs("passes");
+
+        let crate_name = matches.opt_str("crate-name");
+        let playground_url = matches.opt_str("playground-url");
+        let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
+        let display_warnings = matches.opt_present("display-warnings");
+        let linker = matches.opt_str("linker").map(PathBuf::from);
+        let sort_modules_alphabetically = !matches.opt_present("sort-modules-by-appearance");
+        let resource_suffix = matches.opt_str("resource-suffix").unwrap_or_default();
+        let enable_minification = !matches.opt_present("disable-minification");
+        let markdown_no_toc = matches.opt_present("markdown-no-toc");
+        let markdown_css = matches.opt_strs("markdown-css");
+        let markdown_playground_url = matches.opt_str("markdown-playground-url");
+        let crate_version = matches.opt_str("crate-version");
+        let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some();
+
+        let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
+
+        Ok(Options {
+            input,
+            crate_name,
+            error_format,
+            libs,
+            externs,
+            cfgs,
+            codegen_options,
+            debugging_options,
+            target,
+            edition,
+            maybe_sysroot,
+            linker,
+            lint_opts,
+            describe_lints,
+            lint_cap,
+            should_test,
+            test_args,
+            default_passes,
+            manual_passes,
+            display_warnings,
+            crate_version,
+            render_options: RenderOptions {
+                output,
+                external_html,
+                id_map,
+                playground_url,
+                sort_modules_alphabetically,
+                themes,
+                extension_css,
+                extern_html_root_urls,
+                resource_suffix,
+                enable_minification,
+                enable_index_page,
+                index_page,
+                markdown_no_toc,
+                markdown_css,
+                markdown_playground_url,
+            }
+        })
+    }
+
+    /// Returns whether the file given as `self.input` is a Markdown file.
+    pub fn markdown_input(&self) -> bool {
+        self.input.extension()
+            .map_or(false, |e| e == "md" || e == "markdown")
+    }
+}
+
+/// Prints deprecation warnings for deprecated options
+fn check_deprecated_options(matches: &getopts::Matches, diag: &errors::Handler) {
+    let deprecated_flags = [
+       "input-format",
+       "output-format",
+       "no-defaults",
+       "passes",
+    ];
+
+    for flag in deprecated_flags.into_iter() {
+        if matches.opt_present(flag) {
+            let mut err = diag.struct_warn(&format!("the '{}' flag is considered deprecated",
+                                                    flag));
+            err.warn("please see https://github.com/rust-lang/rust/issues/44136");
+
+            if *flag == "no-defaults" {
+                err.help("you may want to use --document-private-items");
+            }
+
+            err.emit();
+        }
+    }
+
+    let removed_flags = [
+        "plugins",
+        "plugin-path",
+    ];
+
+    for &flag in removed_flags.iter() {
+        if matches.opt_present(flag) {
+            diag.struct_warn(&format!("the '{}' flag no longer functions", flag))
+                .warn("see CVE-2018-1000622")
+                .emit();
+        }
+    }
+}
+
+/// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
+/// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
+/// describing the issue.
+fn parse_extern_html_roots(
+    matches: &getopts::Matches,
+) -> Result<BTreeMap<String, String>, &'static str> {
+    let mut externs = BTreeMap::new();
+    for arg in &matches.opt_strs("extern-html-root-url") {
+        let mut parts = arg.splitn(2, '=');
+        let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
+        let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
+        externs.insert(name.to_string(), url.to_string());
+    }
+
+    Ok(externs)
+}
+
+/// Extracts `--extern CRATE=PATH` arguments from `matches` and
+/// returns a map mapping crate names to their paths or else an
+/// error message.
+// FIXME(eddyb) This shouldn't be duplicated with `rustc::session`.
+fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
+    let mut externs: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
+    for arg in &matches.opt_strs("extern") {
+        let mut parts = arg.splitn(2, '=');
+        let name = parts.next().ok_or("--extern value must not be empty".to_string())?;
+        let location = parts.next().map(|s| s.to_string());
+        if location.is_none() && !nightly_options::is_unstable_enabled(matches) {
+            return Err("the `-Z unstable-options` flag must also be passed to \
+                        enable `--extern crate_name` without `=path`".to_string());
+        }
+        let name = name.to_string();
+        externs.entry(name).or_default().insert(location);
+    }
+    Ok(Externs::new(externs))
+}
index d6b0127e44d019e02faf3de39f5b359b9808b9c6..0bd6f6bf8a2f42495640ebfee3ad3676a1783a26 100644 (file)
@@ -28,7 +28,6 @@
 
 use syntax::ast::{self, Ident, NodeId};
 use syntax::source_map;
-use syntax::edition::Edition;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::json::JsonEmitter;
 use syntax::ptr::P;
@@ -43,9 +42,9 @@
 use rustc_data_structures::sync::{self, Lrc};
 use std::rc::Rc;
 use std::sync::Arc;
-use std::path::PathBuf;
 
 use visit_ast::RustdocVisitor;
+use config::{Options as RustdocOptions, RenderOptions};
 use clean;
 use clean::{get_path_for_type, Clean, MAX_DEF_ID, AttributesExt};
 use html::render::RenderInfo;
@@ -320,32 +319,33 @@ pub fn new_handler(error_format: ErrorOutputType,
     )
 }
 
-pub fn run_core(search_paths: SearchPaths,
-                cfgs: Vec<String>,
-                externs: config::Externs,
-                input: Input,
-                triple: Option<TargetTriple>,
-                maybe_sysroot: Option<PathBuf>,
-                allow_warnings: bool,
-                crate_name: Option<String>,
-                force_unstable_if_unmarked: bool,
-                edition: Edition,
-                cg: CodegenOptions,
-                error_format: ErrorOutputType,
-                cmd_lints: Vec<(String, lint::Level)>,
-                lint_cap: Option<lint::Level>,
-                describe_lints: bool,
-                mut manual_passes: Vec<String>,
-                mut default_passes: passes::DefaultPassOption,
-                treat_err_as_bug: bool,
-                ui_testing: bool,
-) -> (clean::Crate, RenderInfo, Vec<String>) {
+pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions, Vec<String>) {
     // Parse, resolve, and typecheck the given crate.
 
-    let cpath = match input {
-        Input::File(ref p) => Some(p.clone()),
-        _ => None
-    };
+    let RustdocOptions {
+        input,
+        crate_name,
+        error_format,
+        libs,
+        externs,
+        cfgs,
+        codegen_options,
+        debugging_options,
+        target,
+        edition,
+        maybe_sysroot,
+        lint_opts,
+        describe_lints,
+        lint_cap,
+        mut default_passes,
+        mut manual_passes,
+        display_warnings,
+        render_options,
+        ..
+    } = options;
+
+    let cpath = Some(input.clone());
+    let input = Input::File(input);
 
     let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name;
     let warnings_lint_name = lint::builtin::WARNINGS.name;
@@ -359,7 +359,7 @@ pub fn run_core(search_paths: SearchPaths,
                                      missing_docs.to_owned(),
                                      missing_doc_example.to_owned()];
 
-    whitelisted_lints.extend(cmd_lints.iter().map(|(lint, _)| lint).cloned());
+    whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
 
     let lints = lint::builtin::HardwiredLints.get_lints()
                     .into_iter()
@@ -372,33 +372,28 @@ pub fn run_core(search_paths: SearchPaths,
                             Some((lint.name_lower(), lint::Allow))
                         }
                     })
-                    .chain(cmd_lints.into_iter())
+                    .chain(lint_opts.into_iter())
                     .collect::<Vec<_>>();
 
     let host_triple = TargetTriple::from_triple(config::host_triple());
     // plays with error output here!
     let sessopts = config::Options {
         maybe_sysroot,
-        search_paths,
+        search_paths: libs,
         crate_types: vec![config::CrateType::Rlib],
-        lint_opts: if !allow_warnings {
+        lint_opts: if !display_warnings {
             lints
         } else {
             vec![]
         },
         lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
-        cg,
+        cg: codegen_options,
         externs,
-        target_triple: triple.unwrap_or(host_triple),
+        target_triple: target.unwrap_or(host_triple),
         // Ensure that rustdoc works even if rustc is feature-staged
         unstable_features: UnstableFeatures::Allow,
         actually_rustdoc: true,
-        debugging_opts: config::DebuggingOptions {
-            force_unstable_if_unmarked,
-            treat_err_as_bug,
-            ui_testing,
-            ..config::basic_debugging_options()
-        },
+        debugging_opts: debugging_options.clone(),
         error_format,
         edition,
         describe_lints,
@@ -408,8 +403,8 @@ pub fn run_core(search_paths: SearchPaths,
         let source_map = Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping()));
         let diagnostic_handler = new_handler(error_format,
                                              Some(source_map.clone()),
-                                             treat_err_as_bug,
-                                             ui_testing);
+                                             debugging_options.treat_err_as_bug,
+                                             debugging_options.ui_testing);
 
         let mut sess = session::build_session_(
             sessopts, cpath, diagnostic_handler, source_map,
@@ -621,7 +616,7 @@ fn report_deprecated_attr(name: &str, diag: &errors::Handler) {
 
             ctxt.sess().abort_if_errors();
 
-            (krate, ctxt.renderinfo.into_inner(), passes)
+            (krate, ctxt.renderinfo.into_inner(), render_options, passes)
         }), &sess)
     })
 }
index 9631ea059cc4300f0337ac755716024033e3133a..c7a2dd6da3f754126b6861eef4e2a0d6cdf33f20 100644 (file)
@@ -16,7 +16,7 @@
 use html::markdown::{IdMap, ErrorCodes, Markdown};
 use std::cell::RefCell;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct ExternalHtml {
     /// Content that will be included inline in the <head> section of a
     /// rendered Markdown file or generated documentation
index 22fa887c358145865fc03c5ece73b93eb2644472..649a5c7ff33b698f7e6912bf5ca93e5476296ab2 100644 (file)
@@ -905,7 +905,7 @@ pub fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
     links
 }
 
-#[derive(Default)]
+#[derive(Clone, Default, Debug)]
 pub struct IdMap {
     map: FxHashMap<String, usize>,
 }
index 8ba299d229885932e10da7ca1be49c3891178f7e..efd71ad0763e0e61abcbeb638f1e15c6118952d7 100644 (file)
 use std::sync::Arc;
 use std::rc::Rc;
 
-use externalfiles::ExternalHtml;
-
 use errors;
-use getopts;
-
 use serialize::json::{ToJson, Json, as_json};
 use syntax::ast;
 use syntax::ext::base::MacroKind;
@@ -70,6 +66,7 @@
 use rustc_data_structures::flock;
 
 use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability};
+use config::RenderOptions;
 use doctree;
 use fold::DocFolder;
 use html::escape::Escape;
@@ -109,8 +106,6 @@ struct Context {
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
-    pub enable_index_page: bool,
-    pub index_page: Option<PathBuf>,
 }
 
 struct SharedContext {
@@ -495,23 +490,25 @@ pub fn initial_ids() -> Vec<String> {
 
 /// Generates the documentation for `crate` into the directory `dst`
 pub fn run(mut krate: clean::Crate,
-           extern_urls: BTreeMap<String, String>,
-           external_html: &ExternalHtml,
-           playground_url: Option<String>,
-           dst: PathBuf,
-           resource_suffix: String,
+           options: RenderOptions,
            passes: FxHashSet<String>,
-           css_file_extension: Option<PathBuf>,
            renderinfo: RenderInfo,
-           sort_modules_alphabetically: bool,
-           themes: Vec<PathBuf>,
-           enable_minification: bool,
-           id_map: IdMap,
-           enable_index_page: bool,
-           index_page: Option<PathBuf>,
-           matches: &getopts::Matches,
-           diag: &errors::Handler,
-) -> Result<(), Error> {
+           diag: &errors::Handler) -> Result<(), Error> {
+    // need to save a copy of the options for rendering the index page
+    let md_opts = options.clone();
+    let RenderOptions {
+        output,
+        external_html,
+        id_map,
+        playground_url,
+        sort_modules_alphabetically,
+        themes,
+        extension_css,
+        extern_html_root_urls,
+        resource_suffix,
+        ..
+    } = options;
+
     let src_root = match krate.src {
         FileName::Real(ref p) => match p.parent() {
             Some(p) => p.to_path_buf(),
@@ -528,10 +525,10 @@ pub fn run(mut krate: clean::Crate,
         layout: layout::Layout {
             logo: String::new(),
             favicon: String::new(),
-            external_html: external_html.clone(),
+            external_html,
             krate: krate.name.clone(),
         },
-        css_file_extension,
+        css_file_extension: extension_css,
         created_dirs: Default::default(),
         sort_modules_alphabetically,
         themes,
@@ -573,6 +570,7 @@ pub fn run(mut krate: clean::Crate,
             }
         }
     }
+    let dst = output;
     try_err!(fs::create_dir_all(&dst), &dst);
     krate = render_sources(&dst, &mut scx, krate)?;
     let cx = Context {
@@ -582,8 +580,6 @@ pub fn run(mut krate: clean::Crate,
         codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
-        enable_index_page,
-        index_page,
     };
 
     // Crawl the crate to build various caches used for the output
@@ -637,7 +633,7 @@ pub fn run(mut krate: clean::Crate,
             },
             _ => PathBuf::new(),
         };
-        let extern_url = extern_urls.get(&e.name).map(|u| &**u);
+        let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u);
         cache.extern_locations.insert(n, (e.name.clone(), src_root,
                                           extern_location(e, extern_url, &cx.dst)));
 
@@ -678,7 +674,7 @@ pub fn run(mut krate: clean::Crate,
     CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone());
     CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear());
 
-    write_shared(&cx, &krate, &*cache, index, enable_minification, matches, diag)?;
+    write_shared(&cx, &krate, &*cache, index, &md_opts, diag)?;
 
     // And finally render the whole crate's documentation
     cx.krate(krate)
@@ -759,8 +755,7 @@ fn write_shared(
     krate: &clean::Crate,
     cache: &Cache,
     search_index: String,
-    enable_minification: bool,
-    matches: &getopts::Matches,
+    options: &RenderOptions,
     diag: &errors::Handler,
 ) -> Result<(), Error> {
     // Write out the shared files. Note that these are shared among all rustdoc
@@ -773,10 +768,10 @@ fn write_shared(
 
     write_minify(cx.dst.join(&format!("rustdoc{}.css", cx.shared.resource_suffix)),
                  include_str!("static/rustdoc.css"),
-                 enable_minification)?;
+                 options.enable_minification)?;
     write_minify(cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)),
                  include_str!("static/settings.css"),
-                 enable_minification)?;
+                 options.enable_minification)?;
 
     // To avoid "light.css" to be overwritten, we'll first run over the received themes and only
     // then we'll run over the "official" styles.
@@ -800,11 +795,11 @@ fn write_shared(
           include_bytes!("static/wheel.svg"))?;
     write_minify(cx.dst.join(&format!("light{}.css", cx.shared.resource_suffix)),
                  include_str!("static/themes/light.css"),
-                 enable_minification)?;
+                 options.enable_minification)?;
     themes.insert("light".to_owned());
     write_minify(cx.dst.join(&format!("dark{}.css", cx.shared.resource_suffix)),
                  include_str!("static/themes/dark.css"),
-                 enable_minification)?;
+                 options.enable_minification)?;
     themes.insert("dark".to_owned());
 
     let mut themes: Vec<&String> = themes.iter().collect();
@@ -860,10 +855,10 @@ fn write_shared(
 
     write_minify(cx.dst.join(&format!("main{}.js", cx.shared.resource_suffix)),
                  include_str!("static/main.js"),
-                 enable_minification)?;
+                 options.enable_minification)?;
     write_minify(cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
                  include_str!("static/settings.js"),
-                 enable_minification)?;
+                 options.enable_minification)?;
 
     {
         let mut data = format!("var resourcesSuffix = \"{}\";\n",
@@ -871,24 +866,24 @@ fn write_shared(
         data.push_str(include_str!("static/storage.js"));
         write_minify(cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)),
                      &data,
-                     enable_minification)?;
+                     options.enable_minification)?;
     }
 
     if let Some(ref css) = cx.shared.css_file_extension {
         let out = cx.dst.join(&format!("theme{}.css", cx.shared.resource_suffix));
-        if !enable_minification {
+        if !options.enable_minification {
             try_err!(fs::copy(css, out), css);
         } else {
             let mut f = try_err!(File::open(css), css);
             let mut buffer = String::with_capacity(1000);
 
             try_err!(f.read_to_string(&mut buffer), css);
-            write_minify(out, &buffer, enable_minification)?;
+            write_minify(out, &buffer, options.enable_minification)?;
         }
     }
     write_minify(cx.dst.join(&format!("normalize{}.css", cx.shared.resource_suffix)),
                  include_str!("static/normalize.css"),
-                 enable_minification)?;
+                 options.enable_minification)?;
     write(cx.dst.join("FiraSans-Regular.woff"),
           include_bytes!("static/FiraSans-Regular.woff"))?;
     write(cx.dst.join("FiraSans-Medium.woff"),
@@ -984,19 +979,19 @@ fn show_item(item: &IndexItem, krate: &str) -> String {
     let mut w = try_err!(File::create(&dst), &dst);
     try_err!(writeln!(&mut w, "var N = null;var searchIndex = {{}};"), &dst);
     for index in &all_indexes {
-        try_err!(write_minify_replacer(&mut w, &*index, enable_minification,
+        try_err!(write_minify_replacer(&mut w, &*index, options.enable_minification,
                                        &[(minifier::js::Keyword::Null, "N")]),
                  &dst);
     }
     try_err!(writeln!(&mut w, "initSearch(searchIndex);"), &dst);
 
-    if cx.enable_index_page == true {
-        if let Some(ref index_page) = cx.index_page {
-            ::markdown::render(index_page,
-                               cx.dst.clone(),
-                               &matches, &(*cx.shared).layout.external_html,
-                               !matches.opt_present("markdown-no-toc"),
-                               diag);
+    if options.enable_index_page {
+        if let Some(index_page) = options.index_page.clone() {
+            let mut md_opts = options.clone();
+            md_opts.output = cx.dst.clone();
+            md_opts.external_html = (*cx.shared).layout.external_html.clone();
+
+            ::markdown::render(index_page, md_opts, diag);
         } else {
             let dst = cx.dst.join("index.html");
             let mut w = BufWriter::new(try_err!(File::create(&dst), &dst));
index e1cb96edd482a4c0b68254e7c9438db7b3942de3..f0f36f0355ed6bef960b767854a52b5b8abf9fae 100644 (file)
 
 extern crate serialize as rustc_serialize; // used by deriving
 
-use errors::ColorConfig;
-
-use std::collections::{BTreeMap, BTreeSet};
 use std::default::Default;
 use std::env;
 use std::panic;
-use std::path::{Path, PathBuf};
 use std::process;
 use std::sync::mpsc::channel;
 
-use syntax::edition::Edition;
-use externalfiles::ExternalHtml;
 use rustc::session::{early_warn, early_error};
-use rustc::session::search_paths::SearchPaths;
-use rustc::session::config::{ErrorOutputType, RustcOptGroup, Externs, CodegenOptions};
-use rustc::session::config::{nightly_options, build_codegen_options};
-use rustc_target::spec::TargetTriple;
-use rustc::session::config::get_cmd_lint_options;
+use rustc::session::config::{ErrorOutputType, RustcOptGroup};
 
 #[macro_use]
 mod externalfiles;
 
 mod clean;
+mod config;
 mod core;
 mod doctree;
 mod fold;
@@ -99,6 +90,7 @@ pub mod html {
 struct Output {
     krate: clean::Crate,
     renderinfo: html::render::RenderInfo,
+    renderopts: config::RenderOptions,
     passes: Vec<String>,
 }
 
@@ -367,383 +359,57 @@ fn main_args(args: &[String]) -> isize {
             early_error(ErrorOutputType::default(), &err.to_string());
         }
     };
-    // Check for unstable options.
-    nightly_options::check_nightly_options(&matches, &opts());
-
-    if matches.opt_present("h") || matches.opt_present("help") {
-        usage("rustdoc");
-        return 0;
-    } else if matches.opt_present("version") {
-        rustc_driver::version("rustdoc", &matches);
-        return 0;
-    }
-
-    if matches.opt_strs("passes") == ["list"] {
-        println!("Available passes for running rustdoc:");
-        for pass in passes::PASSES {
-            println!("{:>20} - {}", pass.name(), pass.description());
-        }
-        println!("\nDefault passes for rustdoc:");
-        for &name in passes::DEFAULT_PASSES {
-            println!("{:>20}", name);
-        }
-        println!("\nPasses run with `--document-private-items`:");
-        for &name in passes::DEFAULT_PRIVATE_PASSES {
-            println!("{:>20}", name);
-        }
-        return 0;
-    }
-
-    let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
-        Some("auto") => ColorConfig::Auto,
-        Some("always") => ColorConfig::Always,
-        Some("never") => ColorConfig::Never,
-        None => ColorConfig::Auto,
-        Some(arg) => {
-            early_error(ErrorOutputType::default(),
-                        &format!("argument for --color must be `auto`, `always` or `never` \
-                                  (instead was `{}`)", arg));
-        }
+    let options = match config::Options::from_matches(&matches) {
+        Ok(opts) => opts,
+        Err(code) => return code,
     };
-    let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
-        Some("human") => ErrorOutputType::HumanReadable(color),
-        Some("json") => ErrorOutputType::Json(false),
-        Some("pretty-json") => ErrorOutputType::Json(true),
-        Some("short") => ErrorOutputType::Short(color),
-        None => ErrorOutputType::HumanReadable(color),
-        Some(arg) => {
-            early_error(ErrorOutputType::default(),
-                        &format!("argument for --error-format must be `human`, `json` or \
-                                  `short` (instead was `{}`)", arg));
-        }
-    };
-    let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| {
-        *x == "treat-err-as-bug"
-    });
-    let ui_testing = matches.opt_strs("Z").iter().any(|x| {
-        *x == "ui-testing"
-    });
-
-    let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
-
-    // check for deprecated options
-    check_deprecated_options(&matches, &diag);
-
-    let to_check = matches.opt_strs("theme-checker");
-    if !to_check.is_empty() {
-        let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
-        let mut errors = 0;
-
-        println!("rustdoc: [theme-checker] Starting tests!");
-        for theme_file in to_check.iter() {
-            print!(" - Checking \"{}\"...", theme_file);
-            let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
-            if !differences.is_empty() || !success {
-                println!(" FAILED");
-                errors += 1;
-                if !differences.is_empty() {
-                    println!("{}", differences.join("\n"));
-                }
-            } else {
-                println!(" OK");
-            }
-        }
-        if errors != 0 {
-            return 1;
-        }
-        return 0;
-    }
-
-    if matches.free.is_empty() {
-        diag.struct_err("missing file operand").emit();
-        return 1;
-    }
-    if matches.free.len() > 1 {
-        diag.struct_err("too many file operands").emit();
-        return 1;
-    }
-    let input = matches.free[0].clone();
-
-    let mut libs = SearchPaths::new();
-    for s in &matches.opt_strs("L") {
-        libs.add_path(s, error_format);
-    }
-    let externs = match parse_externs(&matches) {
-        Ok(ex) => ex,
-        Err(err) => {
-            diag.struct_err(&err).emit();
-            return 1;
-        }
-    };
-    let extern_urls = match parse_extern_html_roots(&matches) {
-        Ok(ex) => ex,
-        Err(err) => {
-            diag.struct_err(err).emit();
-            return 1;
-        }
-    };
-
-    let test_args = matches.opt_strs("test-args");
-    let test_args: Vec<String> = test_args.iter()
-                                          .flat_map(|s| s.split_whitespace())
-                                          .map(|s| s.to_string())
-                                          .collect();
-
-    let should_test = matches.opt_present("test");
-    let markdown_input = Path::new(&input).extension()
-        .map_or(false, |e| e == "md" || e == "markdown");
-
-    let output = matches.opt_str("o").map(|s| PathBuf::from(&s));
-    let css_file_extension = matches.opt_str("e").map(|s| PathBuf::from(&s));
-    let mut cfgs = matches.opt_strs("cfg");
-    cfgs.push("rustdoc".to_string());
-
-    if let Some(ref p) = css_file_extension {
-        if !p.is_file() {
-            diag.struct_err("option --extend-css argument must be a file").emit();
-            return 1;
-        }
-    }
-
-    let mut themes = Vec::new();
-    if matches.opt_present("themes") {
-        let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
-
-        for (theme_file, theme_s) in matches.opt_strs("themes")
-                                            .iter()
-                                            .map(|s| (PathBuf::from(&s), s.to_owned())) {
-            if !theme_file.is_file() {
-                diag.struct_err("option --themes arguments must all be files").emit();
-                return 1;
-            }
-            let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
-            if !success || !ret.is_empty() {
-                diag.struct_err(&format!("invalid theme: \"{}\"", theme_s))
-                    .help("check what's wrong with the --theme-checker option")
-                    .emit();
-                return 1;
-            }
-            themes.push(theme_file);
-        }
-    }
-
-    let mut id_map = html::markdown::IdMap::new();
-    id_map.populate(html::render::initial_ids());
-    let external_html = match ExternalHtml::load(
-            &matches.opt_strs("html-in-header"),
-            &matches.opt_strs("html-before-content"),
-            &matches.opt_strs("html-after-content"),
-            &matches.opt_strs("markdown-before-content"),
-            &matches.opt_strs("markdown-after-content"), &diag, &mut id_map) {
-        Some(eh) => eh,
-        None => return 3,
-    };
-    let crate_name = matches.opt_str("crate-name");
-    let playground_url = matches.opt_str("playground-url");
-    let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
-    let display_warnings = matches.opt_present("display-warnings");
-    let linker = matches.opt_str("linker").map(PathBuf::from);
-    let sort_modules_alphabetically = !matches.opt_present("sort-modules-by-appearance");
-    let resource_suffix = matches.opt_str("resource-suffix");
-    let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s));
-    let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some();
-    let enable_minification = !matches.opt_present("disable-minification");
-
-    let edition = matches.opt_str("edition").unwrap_or("2015".to_string());
-    let edition = match edition.parse() {
-        Ok(e) => e,
-        Err(_) => {
-            diag.struct_err("could not parse edition").emit();
-            return 1;
-        }
-    };
-    if let Some(ref index_page) = index_page {
-        if !index_page.is_file() {
-            diag.struct_err("option `--index-page` argument must be a file").emit();
-            return 1;
-        }
-    }
 
-    let cg = build_codegen_options(&matches, ErrorOutputType::default());
+    let diag = core::new_handler(options.error_format,
+                                 None,
+                                 options.debugging_options.treat_err_as_bug,
+                                 options.debugging_options.ui_testing);
 
-    match (should_test, markdown_input) {
-        (true, true) => {
-            return markdown::test(&input, cfgs, libs, externs, test_args, maybe_sysroot,
-                                  display_warnings, linker, edition, cg, &diag)
-        }
-        (true, false) => {
-            return test::run(Path::new(&input), cfgs, libs, externs, test_args, crate_name,
-                             maybe_sysroot, display_warnings, linker, edition, cg)
-        }
-        (false, true) => return markdown::render(Path::new(&input),
-                                                 output.unwrap_or(PathBuf::from("doc")),
-                                                 &matches, &external_html,
-                                                 !matches.opt_present("markdown-no-toc"), &diag),
+    match (options.should_test, options.markdown_input()) {
+        (true, true) => return markdown::test(options, &diag),
+        (true, false) => return test::run(options),
+        (false, true) => return markdown::render(options.input, options.render_options, &diag),
         (false, false) => {}
     }
 
-    let output_format = matches.opt_str("w");
-
-    let res = acquire_input(PathBuf::from(input), externs, edition, cg, matches, error_format,
-                            move |out, matches| {
-        let Output { krate, passes, renderinfo } = out;
-        let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
+    // need to move these items separately because we lose them by the time the closure is called,
+    // but we can't crates the Handler ahead of time because it's not Send
+    let diag_opts = (options.error_format,
+                     options.debugging_options.treat_err_as_bug,
+                     options.debugging_options.ui_testing);
+    rust_input(options, move |out| {
+        let Output { krate, passes, renderinfo, renderopts } = out;
         info!("going to format");
-        match output_format.as_ref().map(|s| &**s) {
-            Some("html") | None => {
-                html::render::run(krate, extern_urls, &external_html, playground_url,
-                                  output.unwrap_or(PathBuf::from("doc")),
-                                  resource_suffix.unwrap_or(String::new()),
-                                  passes.into_iter().collect(),
-                                  css_file_extension,
-                                  renderinfo,
-                                  sort_modules_alphabetically,
-                                  themes,
-                                  enable_minification, id_map,
-                                  enable_index_page, index_page,
-                                  &matches,
-                                  &diag)
-                    .expect("failed to generate documentation");
-                0
-            }
-            Some(s) => {
-                diag.struct_err(&format!("unknown output format: {}", s)).emit();
-                1
-            }
-        }
-    });
-    res.unwrap_or_else(|s| {
-        diag.struct_err(&format!("input error: {}", s)).emit();
-        1
+        let (error_format, treat_err_as_bug, ui_testing) = diag_opts;
+        let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
+        html::render::run(krate, renderopts, passes.into_iter().collect(), renderinfo, &diag)
+            .expect("failed to generate documentation");
+        0
     })
 }
 
-/// Looks inside the command line arguments to extract the relevant input format
-/// and files and then generates the necessary rustdoc output for formatting.
-fn acquire_input<R, F>(input: PathBuf,
-                       externs: Externs,
-                       edition: Edition,
-                       cg: CodegenOptions,
-                       matches: getopts::Matches,
-                       error_format: ErrorOutputType,
-                       f: F)
-                       -> Result<R, String>
-where R: 'static + Send, F: 'static + Send + FnOnce(Output, &getopts::Matches) -> R {
-    match matches.opt_str("r").as_ref().map(|s| &**s) {
-        Some("rust") => Ok(rust_input(input, externs, edition, cg, matches, error_format, f)),
-        Some(s) => Err(format!("unknown input format: {}", s)),
-        None => Ok(rust_input(input, externs, edition, cg, matches, error_format, f))
-    }
-}
-
-/// Extracts `--extern CRATE=PATH` arguments from `matches` and
-/// returns a map mapping crate names to their paths or else an
-/// error message.
-// FIXME(eddyb) This shouldn't be duplicated with `rustc::session`.
-fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
-    let mut externs: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
-    for arg in &matches.opt_strs("extern") {
-        let mut parts = arg.splitn(2, '=');
-        let name = parts.next().ok_or("--extern value must not be empty".to_string())?;
-        let location = parts.next().map(|s| s.to_string());
-        if location.is_none() && !nightly_options::is_unstable_enabled(matches) {
-            return Err("the `-Z unstable-options` flag must also be passed to \
-                        enable `--extern crate_name` without `=path`".to_string());
-        }
-        let name = name.to_string();
-        externs.entry(name).or_default().insert(location);
-    }
-    Ok(Externs::new(externs))
-}
-
-/// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
-/// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
-/// describing the issue.
-fn parse_extern_html_roots(matches: &getopts::Matches)
-    -> Result<BTreeMap<String, String>, &'static str>
-{
-    let mut externs = BTreeMap::new();
-    for arg in &matches.opt_strs("extern-html-root-url") {
-        let mut parts = arg.splitn(2, '=');
-        let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
-        let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
-        externs.insert(name.to_string(), url.to_string());
-    }
-
-    Ok(externs)
-}
-
 /// Interprets the input file as a rust source file, passing it through the
 /// compiler all the way through the analysis passes. The rustdoc output is then
 /// generated from the cleaned AST of the crate.
 ///
 /// This form of input will run all of the plug/cleaning passes
-fn rust_input<R, F>(cratefile: PathBuf,
-                    externs: Externs,
-                    edition: Edition,
-                    cg: CodegenOptions,
-                    matches: getopts::Matches,
-                    error_format: ErrorOutputType,
-                    f: F) -> R
+fn rust_input<R, F>(options: config::Options, f: F) -> R
 where R: 'static + Send,
-      F: 'static + Send + FnOnce(Output, &getopts::Matches) -> R
+      F: 'static + Send + FnOnce(Output) -> R
 {
-    let default_passes = if matches.opt_present("no-defaults") {
-        passes::DefaultPassOption::None
-    } else if matches.opt_present("document-private-items") {
-        passes::DefaultPassOption::Private
-    } else {
-        passes::DefaultPassOption::Default
-    };
-
-    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();
-    for s in &matches.opt_strs("L") {
-        paths.add_path(s, ErrorOutputType::default());
-    }
-    let mut cfgs = matches.opt_strs("cfg");
-    cfgs.push("rustdoc".to_string());
-    let triple = matches.opt_str("target").map(|target| {
-        if target.ends_with(".json") {
-            TargetTriple::TargetPath(PathBuf::from(target))
-        } else {
-            TargetTriple::TargetTriple(target)
-        }
-    });
-    let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
-    let crate_name = matches.opt_str("crate-name");
-    let crate_version = matches.opt_str("crate-version");
-    let plugin_path = matches.opt_str("plugin-path");
-
     info!("starting to run rustc");
-    let display_warnings = matches.opt_present("display-warnings");
-
-    let force_unstable_if_unmarked = matches.opt_strs("Z").iter().any(|x| {
-        *x == "force-unstable-if-unmarked"
-    });
-    let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| {
-        *x == "treat-err-as-bug"
-    });
-    let ui_testing = matches.opt_strs("Z").iter().any(|x| {
-        *x == "ui-testing"
-    });
-
-    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(&matches, error_format);
 
     let (tx, rx) = channel();
 
     let result = rustc_driver::monitor(move || syntax::with_globals(move || {
-        use rustc::session::config::Input;
-
-        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, manual_passes, default_passes,
-                           treat_err_as_bug, ui_testing);
+        let crate_name = options.crate_name.clone();
+        let crate_version = options.crate_version.clone();
+        let (mut krate, renderinfo, renderopts, passes) = core::run_core(options);
 
         info!("finished with rustc");
 
@@ -753,14 +419,6 @@ fn rust_input<R, F>(cratefile: PathBuf,
 
         krate.version = crate_version;
 
-        if !plugins.is_empty() {
-            eprintln!("WARNING: --plugins no longer functions; see CVE-2018-1000622");
-        }
-
-        if !plugin_path.is_none() {
-            eprintln!("WARNING: --plugin-path no longer functions; see CVE-2018-1000622");
-        }
-
         info!("Executing passes");
 
         for pass in &passes {
@@ -783,8 +441,12 @@ fn rust_input<R, F>(cratefile: PathBuf,
             krate = pass(krate);
         }
 
-        tx.send(f(Output { krate: krate, renderinfo: renderinfo, passes: passes },
-                  &matches)).unwrap();
+        tx.send(f(Output {
+            krate: krate,
+            renderinfo: renderinfo,
+            renderopts,
+            passes: passes
+        })).unwrap();
     }));
 
     match result {
@@ -792,27 +454,3 @@ fn rust_input<R, F>(cratefile: PathBuf,
         Err(_) => panic::resume_unwind(Box::new(errors::FatalErrorMarker)),
     }
 }
-
-/// Prints deprecation warnings for deprecated options
-fn check_deprecated_options(matches: &getopts::Matches, diag: &errors::Handler) {
-    let deprecated_flags = [
-       "input-format",
-       "output-format",
-       "no-defaults",
-       "passes",
-    ];
-
-    for flag in deprecated_flags.into_iter() {
-        if matches.opt_present(flag) {
-            let mut err = diag.struct_warn(&format!("the '{}' flag is considered deprecated",
-                                                    flag));
-            err.warn("please see https://github.com/rust-lang/rust/issues/44136");
-
-            if *flag == "no-defaults" {
-                err.help("you may want to use --document-private-items");
-            }
-
-            err.emit();
-        }
-    }
-}
index 0084c0f859281861952cdcfdb747b1c85007c9cf..8008f8848d45d054f0c80b68820350005f8a6e98 100644 (file)
 use std::default::Default;
 use std::fs::File;
 use std::io::prelude::*;
-use std::path::{PathBuf, Path};
+use std::path::PathBuf;
 use std::cell::RefCell;
 
 use errors;
-use getopts;
 use testing;
-use rustc::session::search_paths::SearchPaths;
-use rustc::session::config::{Externs, CodegenOptions};
 use syntax::source_map::DUMMY_SP;
 use syntax::feature_gate::UnstableFeatures;
-use syntax::edition::Edition;
 
-use externalfiles::{ExternalHtml, LoadStringError, load_string};
+use externalfiles::{LoadStringError, load_string};
 
+use config::{Options, RenderOptions};
 use html::escape::Escape;
 use html::markdown;
 use html::markdown::{ErrorCodes, IdMap, Markdown, MarkdownWithToc, find_testable_code};
@@ -51,24 +48,25 @@ fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) {
 
 /// Render `input` (e.g. "foo.md") into an HTML file in `output`
 /// (e.g. output = "bar" => "bar/foo.html").
-pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches,
-              external_html: &ExternalHtml, include_toc: bool, diag: &errors::Handler) -> isize {
+pub fn render(input: PathBuf, options: RenderOptions, diag: &errors::Handler) -> isize {
+    let mut output = options.output;
     output.push(input.file_stem().unwrap());
     output.set_extension("html");
 
     let mut css = String::new();
-    for name in &matches.opt_strs("markdown-css") {
+    for name in &options.markdown_css {
         let s = format!("<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">\n", name);
         css.push_str(&s)
     }
 
-    let input_str = match load_string(input, diag) {
+    let input_str = match load_string(&input, diag) {
         Ok(s) => s,
         Err(LoadStringError::ReadFail) => return 1,
         Err(LoadStringError::BadUtf8) => return 2,
     };
-    if let Some(playground) = matches.opt_str("markdown-playground-url").or(
-                              matches.opt_str("playground-url")) {
+    let playground_url = options.markdown_playground_url
+                            .or(options.playground_url);
+    if let Some(playground) = playground_url {
         markdown::PLAYGROUND.with(|s| { *s.borrow_mut() = Some((None, playground)); });
     }
 
@@ -89,7 +87,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches,
 
     let mut ids = IdMap::new();
     let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
-    let text = if include_toc {
+    let text = if !options.markdown_no_toc {
         MarkdownWithToc(text, RefCell::new(&mut ids), error_codes).to_string()
     } else {
         Markdown(text, &[], RefCell::new(&mut ids), error_codes).to_string()
@@ -124,10 +122,10 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches,
 </html>"#,
         title = Escape(title),
         css = css,
-        in_header = external_html.in_header,
-        before_content = external_html.before_content,
+        in_header = options.external_html.in_header,
+        before_content = options.external_html.before_content,
         text = text,
-        after_content = external_html.after_content,
+        after_content = options.external_html.after_content,
     );
 
     match err {
@@ -140,11 +138,8 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches,
 }
 
 /// Run any tests/code examples in the markdown file `input`.
-pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
-            mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>,
-            display_warnings: bool, linker: Option<PathBuf>, edition: Edition,
-            cg: CodegenOptions, diag: &errors::Handler) -> isize {
-    let input_str = match load_string(input, diag) {
+pub fn test(mut options: Options, diag: &errors::Handler) -> isize {
+    let input_str = match load_string(&options.input, diag) {
         Ok(s) => s,
         Err(LoadStringError::ReadFail) => return 1,
         Err(LoadStringError::BadUtf8) => return 2,
@@ -152,19 +147,20 @@ pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
 
     let mut opts = TestOptions::default();
     opts.no_crate_inject = true;
-    opts.display_warnings = display_warnings;
-    let mut collector = Collector::new(input.to_owned(), cfgs, libs, cg, externs,
-                                       true, opts, maybe_sysroot, None,
-                                       Some(PathBuf::from(input)),
-                                       linker, edition);
+    opts.display_warnings = options.display_warnings;
+    let mut collector = Collector::new(options.input.display().to_string(), options.cfgs,
+                                       options.libs, options.codegen_options, options.externs,
+                                       true, opts, options.maybe_sysroot, None,
+                                       Some(options.input),
+                                       options.linker, options.edition);
     collector.set_position(DUMMY_SP);
     let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
     let res = find_testable_code(&input_str, &mut collector, codes);
     if let Err(err) = res {
         diag.span_warn(DUMMY_SP, &err.to_string());
     }
-    test_args.insert(0, "rustdoctest".to_string());
-    testing::test_main(&test_args, collector.tests,
-                       testing::Options::new().display_output(display_warnings));
+    options.test_args.insert(0, "rustdoctest".to_string());
+    testing::test_main(&options.test_args, collector.tests,
+                       testing::Options::new().display_output(options.display_warnings));
     0
 }
index 06cb4fbd71602787933804c4a9375caa888723bd..d9bab91fd0c7892b8b79e70333f7a16b84035e90 100644 (file)
@@ -12,7 +12,7 @@
 use std::ffi::OsString;
 use std::io::prelude::*;
 use std::io;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 use std::panic::{self, AssertUnwindSafe};
 use std::process::Command;
 use std::str;
@@ -42,6 +42,7 @@
 use errors::emitter::ColorConfig;
 
 use clean::Attributes;
+use config::Options;
 use html::markdown::{self, ErrorCodes, LangString};
 
 #[derive(Clone, Default)]
@@ -55,34 +56,23 @@ pub struct TestOptions {
     pub attrs: Vec<String>,
 }
 
-pub fn run(input_path: &Path,
-           cfgs: Vec<String>,
-           libs: SearchPaths,
-           externs: Externs,
-           mut test_args: Vec<String>,
-           crate_name: Option<String>,
-           maybe_sysroot: Option<PathBuf>,
-           display_warnings: bool,
-           linker: Option<PathBuf>,
-           edition: Edition,
-           cg: CodegenOptions)
-           -> isize {
-    let input = config::Input::File(input_path.to_owned());
+pub fn run(mut options: Options) -> isize {
+    let input = config::Input::File(options.input.clone());
 
     let sessopts = config::Options {
-        maybe_sysroot: maybe_sysroot.clone().or_else(
+        maybe_sysroot: options.maybe_sysroot.clone().or_else(
             || Some(env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_path_buf())),
-        search_paths: libs.clone(),
+        search_paths: options.libs.clone(),
         crate_types: vec![config::CrateType::Dylib],
-        cg: cg.clone(),
-        externs: externs.clone(),
+        cg: options.codegen_options.clone(),
+        externs: options.externs.clone(),
         unstable_features: UnstableFeatures::from_environment(),
         lint_cap: Some(::rustc::lint::Level::Allow),
         actually_rustdoc: true,
         debugging_opts: config::DebuggingOptions {
             ..config::basic_debugging_options()
         },
-        edition,
+        edition: options.edition,
         ..config::Options::default()
     };
     driver::spawn_thread_pool(sessopts, |sessopts| {
@@ -93,13 +83,14 @@ pub fn run(input_path: &Path,
                                             Some(source_map.clone()));
 
         let mut sess = session::build_session_(
-            sessopts, Some(input_path.to_owned()), handler, source_map.clone(),
+            sessopts, Some(options.input), handler, source_map.clone(),
         );
         let codegen_backend = rustc_driver::get_codegen_backend(&sess);
         let cstore = CStore::new(codegen_backend.metadata_loader());
         rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
-        let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone()));
+        let mut cfg = config::build_configuration(&sess,
+                                                  config::parse_cfgspecs(options.cfgs.clone()));
         target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
         sess.parse_sess.config = cfg;
 
@@ -119,24 +110,24 @@ pub fn run(input_path: &Path,
             ).expect("phase_2_configure_and_expand aborted in rustdoc!")
         };
 
-        let crate_name = crate_name.unwrap_or_else(|| {
+        let crate_name = options.crate_name.unwrap_or_else(|| {
             ::rustc_codegen_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input)
         });
         let mut opts = scrape_test_config(hir_forest.krate());
-        opts.display_warnings |= display_warnings;
+        opts.display_warnings |= options.display_warnings;
         let mut collector = Collector::new(
             crate_name,
-            cfgs,
-            libs,
-            cg,
-            externs,
+            options.cfgs,
+            options.libs,
+            options.codegen_options,
+            options.externs,
             false,
             opts,
-            maybe_sysroot,
+            options.maybe_sysroot,
             Some(source_map),
-             None,
-            linker,
-            edition
+            None,
+            options.linker,
+            options.edition
         );
 
         {
@@ -153,11 +144,11 @@ pub fn run(input_path: &Path,
             });
         }
 
-        test_args.insert(0, "rustdoctest".to_string());
+        options.test_args.insert(0, "rustdoctest".to_string());
 
-        testing::test_main(&test_args,
+        testing::test_main(&options.test_args,
                         collector.tests.into_iter().collect(),
-                        testing::Options::new().display_output(display_warnings));
+                        testing::Options::new().display_output(options.display_warnings));
         0
     })
 }
index 876f6c0a80b1da7485360a9f62dbb118f3759c08..527f1355a9ef716970daf46650e748d219a4a8ba 100644 (file)
@@ -12,7 +12,7 @@ error[E0425]: cannot find value `no` in this scope
 3 | no
   | ^^ not found in this scope
 
-thread '$DIR/failed-doctest-output.rs - OtherStruct (line 27)' panicked at 'couldn't compile the test', librustdoc/test.rs:332:13
+thread '$DIR/failed-doctest-output.rs - OtherStruct (line 27)' panicked at 'couldn't compile the test', librustdoc/test.rs:323:13
 note: Run with `RUST_BACKTRACE=1` for a backtrace.
 
 ---- $DIR/failed-doctest-output.rs - SomeStruct (line 21) stdout ----
@@ -21,7 +21,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 21)' panicked at 'test
 thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1
 note: Run with `RUST_BACKTRACE=1` for a backtrace.
 
-', librustdoc/test.rs:367:17
+', librustdoc/test.rs:358:17
 
 
 failures: