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