mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #124611 - Urgau:rustdoc-stdin, r=GuillaumeGomez
Add `-` (stdin) support in rustdoc This PR adds support for the special `-` input which threats the input as coming from *stdin* instead of being a filepath. Doing this also makes `rustdoc` consistent with `rustc` and ~~every~~ other tools. Full [motivation](https://github.com/rust-lang/rust/pull/124611#issuecomment-2094234876). Fixes https://github.com/rust-lang/rust/issues/123671 r? `@fmease`
This commit is contained in:
commit
bb97203e37
@ -798,6 +798,7 @@ pub enum DumpSolverProofTree {
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Input {
|
||||
/// Load source code from a file.
|
||||
File(PathBuf),
|
||||
|
@ -417,6 +417,12 @@ When `rustdoc` receives this flag, it will print an extra "Version (version)" in
|
||||
the crate root's docs. You can use this flag to differentiate between different versions of your
|
||||
library's documentation.
|
||||
|
||||
## `-`: load source code from the standard input
|
||||
|
||||
If you specify `-` as the INPUT on the command line, then `rustdoc` will read the
|
||||
source code from stdin (standard input stream) until the EOF, instead of the file
|
||||
system with an otherwise specified path.
|
||||
|
||||
## `@path`: load command-line flags from a path
|
||||
|
||||
If you specify `@path` on the command-line, then it will open `path` and read
|
||||
|
@ -1,6 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -9,14 +12,14 @@ use rustc_session::config::{
|
||||
self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType,
|
||||
};
|
||||
use rustc_session::config::{get_cmd_lint_options, nightly_options};
|
||||
use rustc_session::config::{
|
||||
CodegenOptions, ErrorOutputType, Externs, JsonUnusedExterns, UnstableOptions,
|
||||
};
|
||||
use rustc_session::config::{CodegenOptions, ErrorOutputType, Externs, Input};
|
||||
use rustc_session::config::{JsonUnusedExterns, UnstableOptions};
|
||||
use rustc_session::getopts;
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::search_paths::SearchPath;
|
||||
use rustc_session::EarlyDiagCtxt;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::FileName;
|
||||
use rustc_target::spec::TargetTriple;
|
||||
|
||||
use crate::core::new_dcx;
|
||||
@ -60,7 +63,7 @@ impl TryFrom<&str> for OutputFormat {
|
||||
pub(crate) struct Options {
|
||||
// Basic options / Options passed directly to rustc
|
||||
/// The crate root or Markdown file to load.
|
||||
pub(crate) input: PathBuf,
|
||||
pub(crate) input: Input,
|
||||
/// The name of the crate being documented.
|
||||
pub(crate) crate_name: Option<String>,
|
||||
/// Whether or not this is a bin crate
|
||||
@ -179,7 +182,7 @@ impl fmt::Debug for Options {
|
||||
}
|
||||
|
||||
f.debug_struct("Options")
|
||||
.field("input", &self.input)
|
||||
.field("input", &self.input.source_name())
|
||||
.field("crate_name", &self.crate_name)
|
||||
.field("bin_crate", &self.bin_crate)
|
||||
.field("proc_macro_crate", &self.proc_macro_crate)
|
||||
@ -320,6 +323,23 @@ impl RenderOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the input (string or file path)
|
||||
///
|
||||
/// Warning: Return an unrecoverable error in case of error!
|
||||
fn make_input(early_dcx: &EarlyDiagCtxt, input: &str) -> Input {
|
||||
if input == "-" {
|
||||
let mut src = String::new();
|
||||
if io::stdin().read_to_string(&mut src).is_err() {
|
||||
// Immediately stop compilation if there was an issue reading
|
||||
// the input (for example if the input stream is not UTF-8).
|
||||
early_dcx.early_fatal("couldn't read from stdin, as it did not contain valid UTF-8");
|
||||
}
|
||||
Input::Str { name: FileName::anon_source_code(&src), input: src }
|
||||
} else {
|
||||
Input::File(PathBuf::from(input))
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -447,15 +467,16 @@ impl Options {
|
||||
|
||||
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
|
||||
|
||||
let input = PathBuf::from(if describe_lints {
|
||||
let input = if describe_lints {
|
||||
"" // dummy, this won't be used
|
||||
} else if matches.free.is_empty() {
|
||||
dcx.fatal("missing file operand");
|
||||
} else if matches.free.len() > 1 {
|
||||
dcx.fatal("too many file operands");
|
||||
} else {
|
||||
&matches.free[0]
|
||||
});
|
||||
match matches.free.as_slice() {
|
||||
[] => dcx.fatal("missing file operand"),
|
||||
[input] => input,
|
||||
_ => dcx.fatal("too many file operands"),
|
||||
}
|
||||
};
|
||||
let input = make_input(early_dcx, &input);
|
||||
|
||||
let externs = parse_externs(early_dcx, matches, &unstable_opts);
|
||||
let extern_html_root_urls = match parse_extern_html_roots(matches) {
|
||||
@ -792,8 +813,10 @@ impl Options {
|
||||
}
|
||||
|
||||
/// Returns `true` if the file given as `self.input` is a Markdown file.
|
||||
pub(crate) fn markdown_input(&self) -> bool {
|
||||
self.input.extension().is_some_and(|e| e == "md" || e == "markdown")
|
||||
pub(crate) fn markdown_input(&self) -> Option<&Path> {
|
||||
self.input
|
||||
.opt_path()
|
||||
.filter(|p| matches!(p.extension(), Some(e) if e == "md" || e == "markdown"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
|
||||
use crate::formats::cache::Cache;
|
||||
use crate::passes::{self, Condition::*};
|
||||
|
||||
pub(crate) use rustc_session::config::{Input, Options, UnstableOptions};
|
||||
pub(crate) use rustc_session::config::{Options, UnstableOptions};
|
||||
|
||||
pub(crate) struct DocContext<'tcx> {
|
||||
pub(crate) tcx: TyCtxt<'tcx>,
|
||||
@ -204,8 +204,6 @@ pub(crate) fn create_config(
|
||||
// Add the doc cfg into the doc build.
|
||||
cfgs.push("doc".to_string());
|
||||
|
||||
let input = Input::File(input);
|
||||
|
||||
// By default, rustdoc ignores all lints.
|
||||
// Specifically unblock lints relevant to documentation or the lint machinery itself.
|
||||
let mut lints_to_show = vec![
|
||||
|
@ -93,8 +93,6 @@ pub(crate) fn run(
|
||||
dcx: &rustc_errors::DiagCtxt,
|
||||
options: RustdocOptions,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let input = config::Input::File(options.input.clone());
|
||||
|
||||
let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
|
||||
|
||||
// See core::create_config for what's going on here.
|
||||
@ -140,7 +138,7 @@ pub(crate) fn run(
|
||||
opts: sessopts,
|
||||
crate_cfg: cfgs,
|
||||
crate_check_cfg: options.check_cfgs.clone(),
|
||||
input,
|
||||
input: options.input.clone(),
|
||||
output_file: None,
|
||||
output_dir: None,
|
||||
file_loader: None,
|
||||
|
@ -730,10 +730,10 @@ fn main_args(
|
||||
core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
|
||||
|
||||
match (options.should_test, options.markdown_input()) {
|
||||
(true, true) => return wrap_return(&diag, markdown::test(options)),
|
||||
(true, false) => return doctest::run(&diag, options),
|
||||
(false, true) => {
|
||||
let input = options.input.clone();
|
||||
(true, Some(_)) => return wrap_return(&diag, markdown::test(options)),
|
||||
(true, None) => return doctest::run(&diag, options),
|
||||
(false, Some(input)) => {
|
||||
let input = input.to_owned();
|
||||
let edition = options.edition;
|
||||
let config = core::create_config(options, &render_options, using_internal_features);
|
||||
|
||||
@ -747,7 +747,7 @@ fn main_args(
|
||||
}),
|
||||
);
|
||||
}
|
||||
(false, false) => {}
|
||||
(false, None) => {}
|
||||
}
|
||||
|
||||
// need to move these items separately because we lose them by the time the closure is called,
|
||||
|
@ -144,8 +144,14 @@ pub(crate) fn render<P: AsRef<Path>>(
|
||||
|
||||
/// Runs any tests/code examples in the markdown file `input`.
|
||||
pub(crate) fn test(options: Options) -> Result<(), String> {
|
||||
let input_str = read_to_string(&options.input)
|
||||
.map_err(|err| format!("{input}: {err}", input = options.input.display()))?;
|
||||
use rustc_session::config::Input;
|
||||
let input_str = match &options.input {
|
||||
Input::File(path) => {
|
||||
read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))?
|
||||
}
|
||||
Input::Str { name: _, input } => input.clone(),
|
||||
};
|
||||
|
||||
let mut opts = GlobalTestOptions::default();
|
||||
opts.no_crate_inject = true;
|
||||
|
||||
@ -155,12 +161,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
|
||||
generate_args_file(&file_path, &options)?;
|
||||
|
||||
let mut collector = Collector::new(
|
||||
options.input.display().to_string(),
|
||||
options.input.filestem().to_string(),
|
||||
options.clone(),
|
||||
true,
|
||||
opts,
|
||||
None,
|
||||
Some(options.input),
|
||||
options.input.opt_path().map(ToOwned::to_owned),
|
||||
options.enable_per_target_ignores,
|
||||
file_path,
|
||||
);
|
||||
|
25
tests/run-make/stdin-rustdoc/rmake.rs
Normal file
25
tests/run-make/stdin-rustdoc/rmake.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! This test checks rustdoc `-` (stdin) handling
|
||||
|
||||
use run_make_support::{rustdoc, tmp_dir};
|
||||
|
||||
static INPUT: &str = r#"
|
||||
//! ```
|
||||
//! dbg!(());
|
||||
//! ```
|
||||
pub struct F;
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let tmp_dir = tmp_dir();
|
||||
let out_dir = tmp_dir.join("doc");
|
||||
|
||||
// rustdoc -
|
||||
rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run();
|
||||
assert!(out_dir.join("rust_out/struct.F.html").try_exists().unwrap());
|
||||
|
||||
// rustdoc --test -
|
||||
rustdoc().arg("--test").arg("-").stdin(INPUT).run();
|
||||
|
||||
// rustdoc file.rs -
|
||||
rustdoc().arg("file.rs").arg("-").run_fail();
|
||||
}
|
Loading…
Reference in New Issue
Block a user