diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 1a00a72d81497..25ebc8affb6d0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -36,6 +36,7 @@ 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}; use crate::errors::FileWriteFail; pub use crate::options::*; use crate::search_paths::SearchPath; @@ -45,37 +46,9 @@ use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; mod cfg; mod externs; mod native_libs; +mod print_request; pub mod sigpipe; -pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ - // tidy-alphabetical-start - ("all-target-specs-json", PrintKind::AllTargetSpecsJson), - ("calling-conventions", PrintKind::CallingConventions), - ("cfg", PrintKind::Cfg), - ("check-cfg", PrintKind::CheckCfg), - ("code-models", PrintKind::CodeModels), - ("crate-name", PrintKind::CrateName), - ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), - ("deployment-target", PrintKind::DeploymentTarget), - ("file-names", PrintKind::FileNames), - ("host-tuple", PrintKind::HostTuple), - ("link-args", PrintKind::LinkArgs), - ("native-static-libs", PrintKind::NativeStaticLibs), - ("relocation-models", PrintKind::RelocationModels), - ("split-debuginfo", PrintKind::SplitDebuginfo), - ("stack-protector-strategies", PrintKind::StackProtectorStrategies), - ("supported-crate-types", PrintKind::SupportedCrateTypes), - ("sysroot", PrintKind::Sysroot), - ("target-cpus", PrintKind::TargetCPUs), - ("target-features", PrintKind::TargetFeatures), - ("target-libdir", PrintKind::TargetLibdir), - ("target-list", PrintKind::TargetList), - ("target-spec-json", PrintKind::TargetSpecJson), - ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), - ("tls-models", PrintKind::TlsModels), - // tidy-alphabetical-end -]; - /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -1015,42 +988,6 @@ impl ExternEntry { } } -#[derive(Clone, PartialEq, Debug)] -pub struct PrintRequest { - pub kind: PrintKind, - pub out: OutFileName, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintKind { - // tidy-alphabetical-start - AllTargetSpecsJson, - CallingConventions, - Cfg, - CheckCfg, - CodeModels, - CrateName, - CrateRootLintLevels, - DeploymentTarget, - FileNames, - HostTuple, - LinkArgs, - NativeStaticLibs, - RelocationModels, - SplitDebuginfo, - StackProtectorStrategies, - SupportedCrateTypes, - Sysroot, - TargetCPUs, - TargetFeatures, - TargetLibdir, - TargetList, - TargetSpecJson, - TargetSpecJsonSchema, - TlsModels, - // tidy-alphabetical-end -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] pub struct NextSolverConfig { /// Whether the new trait solver should be enabled in coherence. @@ -1794,14 +1731,6 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE ) }); -static PRINT_HELP: LazyLock = LazyLock::new(|| { - format!( - "Compiler information to print on stdout (or to a file)\n\ - INFO may be one of <{}>.", - PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::>().join("|") - ) -}); - static EMIT_HELP: LazyLock = LazyLock::new(|| { let mut result = String::from("Comma separated list of types of output for the compiler to emit.\n"); @@ -1868,7 +1797,7 @@ pub fn rustc_optgroups() -> Vec { opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", ""), opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), opt(Stable, Multi, "", "emit", &EMIT_HELP, "[=]"), - opt(Stable, Multi, "", "print", &PRINT_HELP, "[=]"), + opt(Stable, Multi, "", "print", &print_request::PRINT_HELP, "[=]"), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), opt(Stable, Opt, "o", "", "Write output to FILENAME", ""), @@ -2316,108 +2245,6 @@ fn should_override_cgus_and_disable_thinlto( (disable_local_thinlto, codegen_units) } -fn collect_print_requests( - early_dcx: &EarlyDiagCtxt, - cg: &mut CodegenOptions, - unstable_opts: &UnstableOptions, - matches: &getopts::Matches, -) -> Vec { - let mut prints = Vec::::new(); - if cg.target_cpu.as_deref() == Some("help") { - prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); - cg.target_cpu = None; - }; - if cg.target_feature == "help" { - prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); - cg.target_feature = String::new(); - } - - // We disallow reusing the same path in multiple prints, such as `--print - // cfg=output.txt --print link-args=output.txt`, because outputs are printed - // by disparate pieces of the compiler, and keeping track of which files - // need to be overwritten vs appended to is annoying. - let mut printed_paths = FxHashSet::default(); - - prints.extend(matches.opt_strs("print").into_iter().map(|req| { - let (req, out) = split_out_file_name(&req); - - let kind = if let Some((print_name, print_kind)) = - PRINT_KINDS.iter().find(|&&(name, _)| name == req) - { - check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); - *print_kind - } else { - let is_nightly = nightly_options::match_is_nightly_build(matches); - emit_unknown_print_request_help(early_dcx, req, is_nightly) - }; - - let out = out.unwrap_or(OutFileName::Stdout); - if let OutFileName::Real(path) = &out { - if !printed_paths.insert(path.clone()) { - early_dcx.early_fatal(format!( - "cannot print multiple outputs to the same path: {}", - path.display(), - )); - } - } - - PrintRequest { kind, out } - })); - - prints -} - -fn check_print_request_stability( - early_dcx: &EarlyDiagCtxt, - unstable_opts: &UnstableOptions, - (print_name, print_kind): (&str, PrintKind), -) { - if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { - early_dcx.early_fatal(format!( - "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ - print option" - )); - } -} - -fn is_print_request_stable(print_kind: PrintKind) -> bool { - match print_kind { - PrintKind::AllTargetSpecsJson - | PrintKind::CheckCfg - | PrintKind::CrateRootLintLevels - | PrintKind::SupportedCrateTypes - | PrintKind::TargetSpecJson - | PrintKind::TargetSpecJsonSchema => false, - _ => true, - } -} - -fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { - let prints = PRINT_KINDS - .iter() - .filter_map(|(name, kind)| { - // If we're not on nightly, we don't want to print unstable options - if !is_nightly && !is_print_request_stable(*kind) { - None - } else { - Some(format!("`{name}`")) - } - }) - .collect::>(); - let prints = prints.join(", "); - - let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); - #[allow(rustc::diagnostic_outside_of_impl)] - diag.help(format!("valid print requests are: {prints}")); - - if req == "lints" { - diag.help(format!("use `-Whelp` to print a list of lints")); - } - - 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() -} - pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple { match matches.opt_str("target") { Some(target) if target.ends_with(".json") => { @@ -2842,7 +2669,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + let prints = print_request::collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); // -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 new file mode 100644 index 0000000000000..ef5a6410a603a --- /dev/null +++ b/compiler/rustc_session/src/config/print_request.rs @@ -0,0 +1,222 @@ +//! Code for dealing with `--print` requests. + +use std::fmt; +use std::sync::LazyLock; + +use rustc_data_structures::fx::FxHashSet; + +use crate::EarlyDiagCtxt; +use crate::config::{ + CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name, +}; +use crate::macros::AllVariants; + +#[derive(Clone, PartialEq, Debug)] +pub struct PrintRequest { + pub kind: PrintKind, + pub out: OutFileName, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(AllVariants)] +pub enum PrintKind { + // tidy-alphabetical-start + AllTargetSpecsJson, + CallingConventions, + Cfg, + CheckCfg, + CodeModels, + CrateName, + CrateRootLintLevels, + DeploymentTarget, + FileNames, + HostTuple, + LinkArgs, + NativeStaticLibs, + RelocationModels, + SplitDebuginfo, + StackProtectorStrategies, + SupportedCrateTypes, + Sysroot, + TargetCPUs, + TargetFeatures, + TargetLibdir, + TargetList, + TargetSpecJson, + TargetSpecJsonSchema, + TlsModels, + // tidy-alphabetical-end +} + +impl PrintKind { + /// FIXME: rust-analyzer doesn't support `#![feature(macro_derive)]` yet + /// (), which breaks autocomplete. + /// Work around that by aliasing the trait constant to a regular constant. + const ALL_VARIANTS: &[Self] = ::ALL_VARIANTS; + + fn name(self) -> &'static str { + use PrintKind::*; + match self { + // tidy-alphabetical-start + AllTargetSpecsJson => "all-target-specs-json", + CallingConventions => "calling-conventions", + Cfg => "cfg", + CheckCfg => "check-cfg", + CodeModels => "code-models", + CrateName => "crate-name", + CrateRootLintLevels => "crate-root-lint-levels", + DeploymentTarget => "deployment-target", + FileNames => "file-names", + HostTuple => "host-tuple", + LinkArgs => "link-args", + NativeStaticLibs => "native-static-libs", + RelocationModels => "relocation-models", + SplitDebuginfo => "split-debuginfo", + StackProtectorStrategies => "stack-protector-strategies", + SupportedCrateTypes => "supported-crate-types", + Sysroot => "sysroot", + TargetCPUs => "target-cpus", + TargetFeatures => "target-features", + TargetLibdir => "target-libdir", + TargetList => "target-list", + TargetSpecJson => "target-spec-json", + TargetSpecJsonSchema => "target-spec-json-schema", + TlsModels => "tls-models", + // tidy-alphabetical-end + } + } + + fn is_stable(self) -> bool { + use PrintKind::*; + match self { + // Stable values: + CallingConventions + | Cfg + | CodeModels + | CrateName + | DeploymentTarget + | FileNames + | HostTuple + | LinkArgs + | NativeStaticLibs + | RelocationModels + | SplitDebuginfo + | StackProtectorStrategies + | Sysroot + | TargetCPUs + | TargetFeatures + | TargetLibdir + | TargetList + | TlsModels => true, + + // Unstable values: + AllTargetSpecsJson => false, + CheckCfg => false, + CrateRootLintLevels => false, + SupportedCrateTypes => false, + TargetSpecJson => false, + TargetSpecJsonSchema => false, + } + } + + fn from_str(s: &str) -> Option { + Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied() + } +} + +impl fmt::Display for PrintKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name().fmt(f) + } +} + +pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { + let print_kinds = + PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::>().join("|"); + format!( + "Compiler information to print on stdout (or to a file)\n\ + INFO may be one of <{print_kinds}>.", + ) +}); + +pub(crate) fn collect_print_requests( + early_dcx: &EarlyDiagCtxt, + cg: &mut CodegenOptions, + unstable_opts: &UnstableOptions, + matches: &getopts::Matches, +) -> Vec { + let mut prints = Vec::::new(); + if cg.target_cpu.as_deref() == Some("help") { + prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); + cg.target_feature = String::new(); + } + + // We disallow reusing the same path in multiple prints, such as `--print + // cfg=output.txt --print link-args=output.txt`, because outputs are printed + // by disparate pieces of the compiler, and keeping track of which files + // need to be overwritten vs appended to is annoying. + let mut printed_paths = FxHashSet::default(); + + prints.extend(matches.opt_strs("print").into_iter().map(|req| { + let (req, out) = split_out_file_name(&req); + + let kind = if let Some(print_kind) = PrintKind::from_str(req) { + check_print_request_stability(early_dcx, unstable_opts, print_kind); + print_kind + } else { + let is_nightly = nightly_options::match_is_nightly_build(matches); + emit_unknown_print_request_help(early_dcx, req, is_nightly) + }; + + let out = out.unwrap_or(OutFileName::Stdout); + if let OutFileName::Real(path) = &out { + if !printed_paths.insert(path.clone()) { + early_dcx.early_fatal(format!( + "cannot print multiple outputs to the same path: {}", + path.display(), + )); + } + } + + PrintRequest { kind, out } + })); + + prints +} + +fn check_print_request_stability( + early_dcx: &EarlyDiagCtxt, + unstable_opts: &UnstableOptions, + print_kind: PrintKind, +) { + if !print_kind.is_stable() && !unstable_opts.unstable_options { + early_dcx.early_fatal(format!( + "the `-Z unstable-options` flag must also be passed to enable the `{print_kind}` print option" + )); + } +} + +fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { + 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()) + .map(|kind| format!("`{}`", kind.name())) + .collect::>() + .join(", "); + + let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); + #[allow(rustc::diagnostic_outside_of_impl)] + diag.help(format!("valid print requests are: {prints}")); + + if req == "lints" { + diag.help(format!("use `-Whelp` to print a list of lints")); + } + + 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/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 5e5872ee06815..90108e9110440 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -2,6 +2,7 @@ #![allow(internal_features)] #![feature(default_field_values)] #![feature(iter_intersperse)] +#![feature(macro_derive)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). @@ -20,6 +21,7 @@ pub mod code_stats; pub mod config; pub mod cstore; pub mod filesearch; +mod macros; mod options; pub mod search_paths; diff --git a/compiler/rustc_session/src/macros.rs b/compiler/rustc_session/src/macros.rs new file mode 100644 index 0000000000000..8f9a12d00cc7f --- /dev/null +++ b/compiler/rustc_session/src/macros.rs @@ -0,0 +1,27 @@ +/// Derivable trait for enums with no fields (i.e. C-style enums) that want to +/// allow iteration over a list of all variant values. +pub(crate) trait AllVariants: Copy + 'static { + const ALL_VARIANTS: &[Self]; +} + +macro_rules! AllVariantsDerive { + derive() ( + $(#[$meta:meta])* + $vis:vis enum $Type:ident { + $( + $(#[$varmeta:meta])* + $Variant:ident $( = $value:literal )? + ), *$(,)? + } + ) => { + impl $crate::macros::AllVariants for $Type { + const ALL_VARIANTS: &[$Type] = &[ + $( $Type::$Variant, )* + ]; + } + }; +} + +// For some reason the compiler won't allow `pub(crate) use AllVariants` due +// to a conflict with the trait of the same name, but will allow this form. +pub(crate) use AllVariantsDerive as AllVariants;