rustc_session/config/
print_request.rs

1//! Code for dealing with `--print` requests.
2
3use std::fmt;
4use std::sync::LazyLock;
5
6use rustc_data_structures::fx::FxHashSet;
7
8use crate::EarlyDiagCtxt;
9use crate::config::{
10    CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name,
11};
12use crate::macros::AllVariants;
13
14#[derive(Clone, PartialEq, Debug)]
15pub struct PrintRequest {
16    pub kind: PrintKind,
17    pub out: OutFileName,
18}
19
20#[derive(Copy, Clone, PartialEq, Eq, Debug)]
21#[derive(AllVariants)]
22pub enum PrintKind {
23    // tidy-alphabetical-start
24    AllTargetSpecsJson,
25    BackendHasZstd,
26    CallingConventions,
27    Cfg,
28    CheckCfg,
29    CodeModels,
30    CrateName,
31    CrateRootLintLevels,
32    DeploymentTarget,
33    FileNames,
34    HostTuple,
35    LinkArgs,
36    NativeStaticLibs,
37    RelocationModels,
38    SplitDebuginfo,
39    StackProtectorStrategies,
40    SupportedCrateTypes,
41    Sysroot,
42    TargetCPUs,
43    TargetFeatures,
44    TargetLibdir,
45    TargetList,
46    TargetSpecJson,
47    TargetSpecJsonSchema,
48    TlsModels,
49    // tidy-alphabetical-end
50}
51
52impl PrintKind {
53    /// FIXME: rust-analyzer doesn't support `#![feature(macro_derive)]` yet
54    /// (<https://github.com/rust-lang/rust-analyzer/issues/21043>), which breaks autocomplete.
55    /// Work around that by aliasing the trait constant to a regular constant.
56    const ALL_VARIANTS: &[Self] = <Self as AllVariants>::ALL_VARIANTS;
57
58    fn name(self) -> &'static str {
59        use PrintKind::*;
60        match self {
61            // tidy-alphabetical-start
62            AllTargetSpecsJson => "all-target-specs-json",
63            BackendHasZstd => "backend-has-zstd",
64            CallingConventions => "calling-conventions",
65            Cfg => "cfg",
66            CheckCfg => "check-cfg",
67            CodeModels => "code-models",
68            CrateName => "crate-name",
69            CrateRootLintLevels => "crate-root-lint-levels",
70            DeploymentTarget => "deployment-target",
71            FileNames => "file-names",
72            HostTuple => "host-tuple",
73            LinkArgs => "link-args",
74            NativeStaticLibs => "native-static-libs",
75            RelocationModels => "relocation-models",
76            SplitDebuginfo => "split-debuginfo",
77            StackProtectorStrategies => "stack-protector-strategies",
78            SupportedCrateTypes => "supported-crate-types",
79            Sysroot => "sysroot",
80            TargetCPUs => "target-cpus",
81            TargetFeatures => "target-features",
82            TargetLibdir => "target-libdir",
83            TargetList => "target-list",
84            TargetSpecJson => "target-spec-json",
85            TargetSpecJsonSchema => "target-spec-json-schema",
86            TlsModels => "tls-models",
87            // tidy-alphabetical-end
88        }
89    }
90
91    fn is_stable(self) -> bool {
92        use PrintKind::*;
93        match self {
94            // Stable values:
95            CallingConventions
96            | Cfg
97            | CodeModels
98            | CrateName
99            | DeploymentTarget
100            | FileNames
101            | HostTuple
102            | LinkArgs
103            | NativeStaticLibs
104            | RelocationModels
105            | SplitDebuginfo
106            | StackProtectorStrategies
107            | Sysroot
108            | TargetCPUs
109            | TargetFeatures
110            | TargetLibdir
111            | TargetList
112            | TlsModels => true,
113
114            // Unstable values:
115            AllTargetSpecsJson => false,
116            BackendHasZstd => false, // (perma-unstable, for use by compiletest)
117            CheckCfg => false,
118            CrateRootLintLevels => false,
119            SupportedCrateTypes => false,
120            TargetSpecJson => false,
121            TargetSpecJsonSchema => false,
122        }
123    }
124
125    fn from_str(s: &str) -> Option<Self> {
126        Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied()
127    }
128}
129
130impl fmt::Display for PrintKind {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        self.name().fmt(f)
133    }
134}
135
136pub(crate) static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
137    let print_kinds =
138        PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::<Vec<_>>().join("|");
139    format!(
140        "Compiler information to print on stdout (or to a file)\n\
141        INFO may be one of <{print_kinds}>.",
142    )
143});
144
145pub(crate) fn collect_print_requests(
146    early_dcx: &EarlyDiagCtxt,
147    cg: &mut CodegenOptions,
148    unstable_opts: &UnstableOptions,
149    matches: &getopts::Matches,
150) -> Vec<PrintRequest> {
151    let mut prints = Vec::<PrintRequest>::new();
152    if cg.target_cpu.as_deref() == Some("help") {
153        prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
154        cg.target_cpu = None;
155    };
156    if cg.target_feature == "help" {
157        prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
158        cg.target_feature = String::new();
159    }
160
161    // We disallow reusing the same path in multiple prints, such as `--print
162    // cfg=output.txt --print link-args=output.txt`, because outputs are printed
163    // by disparate pieces of the compiler, and keeping track of which files
164    // need to be overwritten vs appended to is annoying.
165    let mut printed_paths = FxHashSet::default();
166
167    prints.extend(matches.opt_strs("print").into_iter().map(|req| {
168        let (req, out) = split_out_file_name(&req);
169
170        let kind = if let Some(print_kind) = PrintKind::from_str(req) {
171            check_print_request_stability(early_dcx, unstable_opts, print_kind);
172            print_kind
173        } else {
174            let is_nightly = nightly_options::match_is_nightly_build(matches);
175            emit_unknown_print_request_help(early_dcx, req, is_nightly)
176        };
177
178        let out = out.unwrap_or(OutFileName::Stdout);
179        if let OutFileName::Real(path) = &out {
180            if !printed_paths.insert(path.clone()) {
181                early_dcx.early_fatal(format!(
182                    "cannot print multiple outputs to the same path: {}",
183                    path.display(),
184                ));
185            }
186        }
187
188        PrintRequest { kind, out }
189    }));
190
191    prints
192}
193
194fn check_print_request_stability(
195    early_dcx: &EarlyDiagCtxt,
196    unstable_opts: &UnstableOptions,
197    print_kind: PrintKind,
198) {
199    if !print_kind.is_stable() && !unstable_opts.unstable_options {
200        early_dcx.early_fatal(format!(
201            "the `-Z unstable-options` flag must also be passed to enable the `{print_kind}` print option"
202        ));
203    }
204}
205
206fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
207    let prints = PrintKind::ALL_VARIANTS
208        .iter()
209        // If we're not on nightly, we don't want to print unstable options
210        .filter(|kind| is_nightly || kind.is_stable())
211        .map(|kind| format!("`{kind}`"))
212        .collect::<Vec<_>>()
213        .join(", ");
214
215    let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
216    #[allow(rustc::diagnostic_outside_of_impl)]
217    diag.help(format!("valid print requests are: {prints}"));
218
219    if req == "lints" {
220        diag.help(format!("use `-Whelp` to print a list of lints"));
221    }
222
223    diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"));
224    diag.emit()
225}