diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 54a1babbaae72..ade737fe1a4a6 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -616,7 +616,7 @@ fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { } } -fn print_crate_info( +pub fn print_crate_info( codegen_backend: &dyn CodegenBackend, sess: &Session, parse_attrs: bool, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 95f6348cfbdbb..9a0620caf1058 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -35,8 +35,11 @@ use tracing::debug; pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; use crate::config::native_libs::parse_native_libs; -pub use crate::config::print_request::{PrintKind, PrintRequest}; +pub use crate::config::print_request::{ + PrintCategory, PrintKind, PrintRequest, collect_print_requests, +}; use crate::diagnostics::FileWriteFail; +use crate::macros::AllVariants; pub use crate::options::*; use crate::search_paths::SearchPath; use crate::utils::CanonicalizedPath; @@ -1414,12 +1417,11 @@ impl Sysroot { } } +/// Get the host triple out of the build environment. This ensures that our +/// idea of the host triple is the same as for the set of libraries we've +/// actually built. We can't just take LLVM's host triple because they +/// normalize all ix86 architectures to i386. pub fn host_tuple() -> &'static str { - // Get the host triple out of the build environment. This ensures that our - // idea of the host triple is the same as for the set of libraries we've - // actually built. We can't just take LLVM's host triple because they - // normalize all ix86 architectures to i386. - // // Instead of grabbing the host triple (for the current host), we grab (at // compile time) the target triple that this rustc is built with and // calling that (at runtime) the host triple. @@ -2898,7 +2900,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = print_request::collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + let prints = print_request::collect_print_requests( + early_dcx, + &mut cg, + &unstable_opts, + matches, + PrintCategory::ALL_VARIANTS, + ); // -Zretpoline-external-thunk also requires -Zretpoline if unstable_opts.retpoline_external_thunk { diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index 8201e1bfdd9a7..b41ceb1699eaa 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -22,36 +22,98 @@ pub struct PrintRequest { #[derive(AllVariants)] pub enum PrintKind { // tidy-alphabetical-start + /// All target JSON specifications. AllTargetSpecsJson, + + /// Does the backend supports the [`PrintRequest::arg`] `asm!()` mnemonic? (perma-unstable) BackendHasMnemonic, + + /// Does the backend supports Zstd compression? (perma-unstable) BackendHasZstd, + + /// List of all calling conventions supported by rustc. CallingConventions, + + /// List of cfg values. Cfg, + + /// List of check-cfg values. CheckCfg, + + /// List of available code models for the current backend. CodeModels, + + /// Name of the crate being compiled. CrateName, + + /// Lint levels of the crate's root module. CrateRootLintLevels, + + /// The current selected deployment target. (Apple only) DeploymentTarget, + + /// The names of the files created by the `--emit=link` option. (e.g. `libfoo.a`) FileNames, + + /// Target-tuple of the host compiler. HostTuple, + + /// Linker invocations. LinkArgs, + + /// When compiling a `staticlib` crate, print the linker flags used. NativeStaticLibs, + + /// List of available relocation models for the current backend. RelocationModels, + + /// List of available split debuginfos for the current target. SplitDebuginfo, + + /// List of available stack protector strategies for the current backend. StackProtectorStrategies, + + /// List of available crate types for the current target. SupportedCrateTypes, + + /// Path to the sysroot. Sysroot, + + /// List of available CPU values for the current target. TargetCPUs, + + /// List of available target features for the current target. TargetFeatures, + + /// Path to the target libdir. TargetLibdir, + + /// List of supported targets. TargetList, + + /// Current target JSON specification. TargetSpecJson, + + /// Target JSON specification schema. TargetSpecJsonSchema, + + /// List of available TLS models for the current backend. TlsModels, + + /// Target-tuple for WebAssembly's proc-macro crates. WasmProcMacroTuple, // tidy-alphabetical-end } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(AllVariants)] +pub enum PrintCategory { + Target, + Codegen, + Linker, + Crate, +} + impl PrintKind { fn name(self) -> &'static str { use PrintKind::*; @@ -88,6 +150,28 @@ impl PrintKind { } } + fn category(self) -> PrintCategory { + use PrintKind::*; + match self { + TargetList | TargetSpecJsonSchema | AllTargetSpecsJson | TargetSpecJson + | TargetCPUs | TargetFeatures | DeploymentTarget | HostTuple | SupportedCrateTypes + | Sysroot | TargetLibdir | Cfg | CheckCfg | WasmProcMacroTuple => PrintCategory::Target, + + BackendHasMnemonic + | BackendHasZstd + | CallingConventions + | CodeModels + | SplitDebuginfo + | StackProtectorStrategies + | TlsModels + | RelocationModels => PrintCategory::Codegen, + + LinkArgs | NativeStaticLibs => PrintCategory::Linker, + + CrateName | CrateRootLintLevels | FileNames => PrintCategory::Crate, + } + } + fn is_stable(self) -> bool { use PrintKind::*; match self { @@ -144,11 +228,12 @@ pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { ) }); -pub(crate) fn collect_print_requests( +pub fn collect_print_requests( early_dcx: &EarlyDiagCtxt, cg: &mut CodegenOptions, unstable_opts: &UnstableOptions, matches: &getopts::Matches, + allowed: &[PrintCategory], ) -> Vec { let mut prints = Vec::::new(); if cg.target_cpu.as_deref() == Some("help") { @@ -190,12 +275,14 @@ pub(crate) fn collect_print_requests( for example: `--print=backend-has-mnemonic:RET`", ); } - } else if let Some(print_kind) = PrintKind::from_str(req) { + } else if let Some(print_kind) = PrintKind::from_str(req) + && allowed.contains(&print_kind.category()) + { check_print_request_stability(early_dcx, unstable_opts, print_kind); (print_kind, None) } else { let is_nightly = nightly_options::match_is_nightly_build(matches); - emit_unknown_print_request_help(early_dcx, req, is_nightly) + emit_unknown_print_request_help(early_dcx, req, is_nightly, allowed) }; let out = out.unwrap_or(OutFileName::Stdout); @@ -226,11 +313,17 @@ fn check_print_request_stability( } } -fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { +fn emit_unknown_print_request_help( + early_dcx: &EarlyDiagCtxt, + req: &str, + is_nightly: bool, + allowed: &[PrintCategory], +) -> ! { let prints = PrintKind::ALL_VARIANTS .iter() // If we're not on nightly, we don't want to print unstable options .filter(|kind| is_nightly || kind.is_stable()) + .filter(|kind| allowed.contains(&kind.category())) .map(|kind| format!("`{kind}`")) .collect::>() .join(", "); @@ -239,9 +332,12 @@ fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nigh diag.help(format!("valid print requests are: {prints}")); if req == "lints" { - diag.help(format!("use `-Whelp` to print a list of lints")); + diag.help("use `-Whelp` to print a list of lints"); + } + + if allowed == PrintCategory::ALL_VARIANTS { + diag.help("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"); } - diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); diag.emit() } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 941632f0d283a..c2c58a345fa22 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -10,8 +10,9 @@ use rustc_errors::DiagCtxtHandle; use rustc_lint::Level; use rustc_session::config::{ self, CodegenOptions, ErrorOutputType, Externs, Input, JsonUnusedExterns, - OptionsTargetModifiers, OutFileName, Sysroot, UnstableOptions, get_cmd_lint_options, - nightly_options, parse_crate_types_from_list, parse_externs, parse_target_triple, + OptionsTargetModifiers, OutFileName, PrintCategory, PrintRequest, Sysroot, UnstableOptions, + collect_print_requests, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, + parse_externs, parse_target_triple, }; use rustc_session::search_paths::SearchPath; use rustc_session::{EarlyDiagCtxt, getopts}; @@ -105,6 +106,8 @@ pub(crate) struct Options { pub(crate) describe_lints: bool, /// What level to cap lints at. pub(crate) lint_cap: Option, + /// Print requests to hand to the compiler. + pub(crate) prints: Vec, // Options specific to running doctests /// Whether we should run doctests instead of generating docs. @@ -198,6 +201,7 @@ impl fmt::Debug for Options { .field("lint_opts", &self.lint_opts) .field("describe_lints", &self.describe_lints) .field("lint_cap", &self.lint_cap) + .field("prints", &self.prints) .field("should_test", &self.should_test) .field("test_args", &self.test_args) .field("test_run_directory", &self.test_run_directory) @@ -336,7 +340,6 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - // modern choices "html-static-files" => Ok(Self::HtmlStaticFiles), "html-non-static-files" => Ok(Self::HtmlNonStaticFiles), "dep-info" => Ok(Self::DepInfo(None)), @@ -409,7 +412,7 @@ impl Options { let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let mut collected_options = Default::default(); - let codegen_options = CodegenOptions::build(early_dcx, matches, &mut collected_options); + let mut codegen_options = CodegenOptions::build(early_dcx, matches, &mut collected_options); let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut collected_options); let remap_path_prefix = match parse_remap_path_prefix(matches) { @@ -571,6 +574,14 @@ impl Options { Err(err) => dcx.fatal(err), }; + let prints = collect_print_requests( + early_dcx, + &mut codegen_options, + &unstable_opts, + matches, + &[PrintCategory::Target, PrintCategory::Crate], + ); + let mut parts_out_dir = match matches.opt_str("write-doc-meta-dir").map(PathToParts::from_flag).transpose() { Ok(parts_out_dir) => parts_out_dir, @@ -906,6 +917,7 @@ impl Options { lint_opts, describe_lints, lint_cap, + prints, should_test, test_args, show_coverage, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index ad6718e75466e..db5e281f376ad 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -224,6 +224,7 @@ pub(crate) fn create_config( lint_opts, describe_lints, lint_cap, + prints, scrape_examples_options, remap_path_prefix, remap_path_scope, @@ -284,6 +285,7 @@ pub(crate) fn create_config( diagnostic_width, edition, describe_lints, + prints, crate_name, test, remap_path_prefix, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index d8064cec13b96..80affbd132bfe 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -176,6 +176,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions unstable_opts: options.unstable_opts.clone(), error_format: options.error_format.clone(), target_modifiers: options.target_modifiers.clone(), + describe_lints: options.describe_lints, ..config::Options::default() }; @@ -215,8 +216,17 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let extract_doctests = options.output_format == OutputFormat::Doctest; let save_temps = options.codegen_options.save_temps; + let registered_lints = config.register_lints.is_some(); let result = interface::run_compiler(config, |compiler| { - let krate = rustc_interface::passes::parse(&compiler.sess); + let sess = &compiler.sess; + + // -W help + if sess.opts.describe_lints { + rustc_driver::describe_lints(sess, registered_lints); + return Ok(None); + } + + let krate = rustc_interface::passes::parse(sess); let (collector, _incr_comp_session) = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c4f7d2c361952..fd50a7b306783 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -542,6 +542,14 @@ fn opts() -> Vec { "Comma separated list of types of output for rustdoc to emit", "[html-static-files,html-non-static-files,dep-info]", ), + opt( + Unstable, + Multi, + "", + "print", + "Rustdoc information to print on stdout (or to a file)", + "[=]", + ), opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""), opt( Unstable, @@ -841,6 +849,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let input = match input { config::InputMode::HasFile(input) => input, config::InputMode::NoInputMergeFinalize => { + if !options.prints.is_empty() { + dcx.fatal("`--print` is not supported for the `--write-doc-meta-dir` option"); + } + let config = core::create_config( Input::Str { name: rustc_span::FileName::Custom(String::new()), @@ -858,32 +870,53 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { ); } }; + let md_input = config::markdown_input(&input); - let output_format = options.output_format; + if options.should_test || options.output_format == config::OutputFormat::Doctest { + if !options.prints.is_empty() { + dcx.fatal(format!( + "`--print` is not yet supported for the `{}` option", + if options.should_test { "--test" } else { "--output-format=doctest" } + )); + } + + return match md_input { + Some(_) => wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), + None => doctest::run(dcx, input, options), + }; + } - match ( - options.should_test || output_format == config::OutputFormat::Doctest, - config::markdown_input(&input), - ) { - (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), - (true, None) => return doctest::run(dcx, input, options), - (false, Some(md_input)) => { + if let Some(md_input) = md_input { + if !options.prints.is_empty() { + dcx.fatal("`--print` is not yet supported for standalone Markdown files"); + } + + return { let md_input = md_input.to_owned(); let edition = options.edition; let config = core::create_config(input, options, &render_options); + let registered_lints = config.register_lints.is_some(); // `markdown::render` can invoke `doctest::make_test`, which // requires session globals and a thread pool, so we use // `run_compiler`. - return wrap_return( + wrap_return( dcx, interface::run_compiler(config, |compiler| { + let sess = &compiler.sess; + + // -W help + if sess.opts.describe_lints { + rustc_driver::describe_lints(sess, registered_lints); + return Ok(()); + } + // construct a phony "crate" without actually running the parser // allows us to use other compiler infrastructure like dep-info - let file = - compiler.sess.source_map().load_file(&md_input).map_err(|e| { - format!("{md_input}: {e}", md_input = md_input.display()) - })?; + let file = sess + .source_map() + .load_file(&md_input) + .map_err(|e| format!("{md_input}: {e}", md_input = md_input.display()))?; let inner_span = Span::new( file.start_pos, BytePos(file.start_pos.0 + file.normalized_source_len.0), @@ -916,9 +949,8 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }); res }), - ); - } - (false, None) => {} + ) + }; } // need to move these items separately because we lose them by the time the closure is called, @@ -940,7 +972,6 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let output_format = options.output_format; let config = core::create_config(input, options, &render_options); - let registered_lints = config.register_lints.is_some(); interface::run_compiler(config, |compiler| { @@ -952,11 +983,19 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let _ = sess.source_map().load_binary_file(external_path); } + // -W help if sess.opts.describe_lints { rustc_driver::describe_lints(sess, registered_lints); return; } + // --print + if rustc_driver::print_crate_info(&*compiler.codegen_backend, sess, true) + == rustc_driver::Compilation::Stop + { + return; + } + let krate = rustc_interface::passes::parse(sess); rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { if sess.dcx().has_errors().is_some() { diff --git a/tests/run-make/rustdoc/default-output/output-default.stdout b/tests/run-make/rustdoc/default-output/output-default.stdout index 78dfbf03c1b10..3093a01ec79f7 100644 --- a/tests/run-make/rustdoc/default-output/output-default.stdout +++ b/tests/run-make/rustdoc/default-output/output-default.stdout @@ -155,6 +155,8 @@ Options: --emit [html-static-files,html-non-static-files,dep-info] Comma separated list of types of output for rustdoc to emit + --print [=] + Rustdoc information to print on stdout (or to a file) --no-run Compile doctests without running them --merge-doctests yes|no|auto Force all doctests to be compiled as a single binary, diff --git a/tests/run-make/rustdoc/doctest/markdown/bad.md b/tests/run-make/rustdoc/doctest/markdown/bad.md new file mode 100644 index 0000000000000..3d43232cd61f1 --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/bad.md @@ -0,0 +1,9 @@ +# Cool Title + +``` +assert!(true); +``` + +``` +assert_eq!("foo", "bar"); +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/extadd.rs b/tests/run-make/rustdoc/doctest/markdown/extadd.rs new file mode 100644 index 0000000000000..77a01ad3ca498 --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/extadd.rs @@ -0,0 +1,3 @@ +pub fn add(x: i32, y: i32) -> i32 { + x + y +} diff --git a/tests/run-make/rustdoc/doctest/markdown/extern.md b/tests/run-make/rustdoc/doctest/markdown/extern.md new file mode 100644 index 0000000000000..27a2a0e28588a --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/extern.md @@ -0,0 +1,7 @@ +# With extern crate + +``` +# extern crate aux; + +assert_eq!(aux::add(3, 4), 7); +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/good.md b/tests/run-make/rustdoc/doctest/markdown/good.md new file mode 100644 index 0000000000000..db7e85d3fc51f --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/good.md @@ -0,0 +1,23 @@ +# Title + +Some text + +``` +assert_eq!(0, 0); +``` + +```text +Some more text +``` + +```ignore (example) +assert_eq!(0, 1; +``` + +```no_run +assert_eq!(0, 1); +``` + +```rust,compile_fail +Something +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/rmake.rs b/tests/run-make/rustdoc/doctest/markdown/rmake.rs new file mode 100644 index 0000000000000..39e6b7e75885c --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/rmake.rs @@ -0,0 +1,34 @@ +// Doctests need std and executables that can run on the host +//@ needs-target-std +//@ ignore-wasm +//@ ignore-sgx +//@ ignore-pauthtest +//@ ignore-remote (e.g. armhf-gnu) + +use run_make_support::{rust_lib_name, rustc, rustdoc}; + +fn main() { + rustdoc().arg("--test").input("good.md").run().assert_exit_code(0).assert_stdout_contains( + "test result: ok. 3 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out;", + ); + + rustdoc() + .arg("--test") + .input("bad.md") + .run_fail() + .assert_exit_code(101) + .assert_stdout_contains( + "test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out;", + ); + + rustc().input("extadd.rs").crate_type("rlib").run(); + rustdoc() + .arg("--test") + .extern_("aux", rust_lib_name("extadd")) + .input("extern.md") + .run() + .assert_exit_code(0) + .assert_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out;", + ); +} diff --git a/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs b/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs new file mode 100644 index 0000000000000..a4ec391dda950 --- /dev/null +++ b/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs @@ -0,0 +1,6 @@ +#![allow(rustdoc::private_doc_tests)] +#![forbid(rustdoc::private_intra_doc_links)] +#![expect(unused_mut)] + +#[deny(unknown_lints)] +mod my_mod {} diff --git a/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs b/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs new file mode 100644 index 0000000000000..4f7d3e129b41c --- /dev/null +++ b/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs @@ -0,0 +1,124 @@ +//! This checks the output of `--print=crate-root-lint-levels` + +use std::collections::HashSet; +use std::iter::FromIterator; + +use run_make_support::rustdoc; + +struct CrateRootLintLevels { + args: &'static [&'static str], + contains: Contains, +} + +struct Contains { + contains: &'static [&'static str], + doesnt_contain: &'static [&'static str], +} + +fn main() { + // rustdoc don't run rustc lints, and ignores rustc lint check attributes + check(CrateRootLintLevels { + args: &[], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "unused_mut=allow", + "warnings=warn", + "stable_features=warn", + "unknown_lints=warn", + "rustdoc::broken_intra_doc_links=warn", + "rustdoc::private_intra_doc_links=forbid", + "rustdoc::missing_crate_level_docs=allow", + ], + doesnt_contain: &["rustdoc::private_doc_tests=warn", "unused_mut=expect"], + }, + }); + check(CrateRootLintLevels { + args: &["-Wrustdoc::private_doc_tests"], + contains: Contains { + contains: &["rustdoc::private_doc_tests=allow", "warnings=warn"], + doesnt_contain: &["rustdoc::private_doc_tests=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings"], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "warnings=deny", + "stable_features=deny", + "unknown_lints=deny", + ], + doesnt_contain: &["warnings=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dstable_features"], + contains: Contains { + contains: &[ + "warnings=warn", + "stable_features=deny", + "rustdoc::private_doc_tests=allow", + ], + doesnt_contain: &["warnings=deny"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings", "--force-warn=stable_features"], + contains: Contains { + contains: &["warnings=deny", "stable_features=force-warn", "unknown_lints=deny"], + doesnt_contain: &["warnings=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings", "--cap-lints=warn"], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "warnings=warn", + "stable_features=warn", + "unknown_lints=warn", + ], + doesnt_contain: &["warnings=deny"], + }, + }); +} + +#[track_caller] +fn check(CrateRootLintLevels { args, contains }: CrateRootLintLevels) { + let output = rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--print=crate-root-lint-levels") + .args(args) + .run(); + + let stdout = output.stdout_utf8(); + + let mut found = HashSet::::new(); + + for l in stdout.lines() { + assert!(l == l.trim()); + if let Some((left, right)) = l.split_once('=') { + assert!(!left.contains("\"")); + assert!(!right.contains("\"")); + } else { + assert!(l.contains('=')); + } + assert!(found.insert(l.to_string()), "{}", &l); + } + + let Contains { contains, doesnt_contain } = contains; + + { + let should_found = HashSet::::from_iter(contains.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_found.difference(&found).collect(); + assert!(diff.is_empty(), "should found: {:?}, didn't found {:?}", &should_found, &diff); + } + { + let should_not_find = + HashSet::::from_iter(doesnt_contain.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_not_find.intersection(&found).collect(); + assert!(diff.is_empty(), "should not find {:?}, did found {:?}", &should_not_find, &diff); + } +} diff --git a/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err new file mode 100644 index 0000000000000..02d14190030bb --- /dev/null +++ b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err @@ -0,0 +1,4 @@ +error: unknown print request: `xxx` + | + = help: valid print requests are: `all-target-specs-json`, `cfg`, `check-cfg`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `wasm-proc-macro-tuple` + diff --git a/tests/run-make/rustdoc/print-request-help/rmake.rs b/tests/run-make/rustdoc/print-request-help/rmake.rs new file mode 100644 index 0000000000000..bfa494ca2ccb6 --- /dev/null +++ b/tests/run-make/rustdoc/print-request-help/rmake.rs @@ -0,0 +1,10 @@ +use run_make_support::{diff, rustdoc}; + +fn main() { + let invalid_print_request_help = + rustdoc().arg("-Zunstable-options").arg("--print=xxx").run_fail().stderr_utf8(); + diff() + .expected_file("invalid-print-request-help.err") + .actual_text("invalid_print_request_help", &invalid_print_request_help) + .run(); +}