rustc_session/
config.rs

1//! Contains infrastructure for configuring the compiler, including parsing
2//! command-line options.
3
4#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
5
6use std::collections::btree_map::{
7    Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
8};
9use std::collections::{BTreeMap, BTreeSet};
10use std::ffi::OsStr;
11use std::hash::Hash;
12use std::path::{Path, PathBuf};
13use std::str::{self, FromStr};
14use std::sync::LazyLock;
15use std::{cmp, fmt, fs, iter};
16
17use externs::{ExternOpt, split_extern_opt};
18use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
19use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
20use rustc_errors::emitter::HumanReadableErrorType;
21use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg};
22use rustc_feature::UnstableFeatures;
23use rustc_macros::{Decodable, Encodable, HashStable_Generic};
24use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION};
25use rustc_span::source_map::FilePathMapping;
26use rustc_span::{
27    FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName,
28    SourceFileHashAlgorithm, Symbol, sym,
29};
30use rustc_target::spec::{
31    FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple,
32};
33use tracing::debug;
34
35pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues};
36use crate::config::native_libs::parse_native_libs;
37use crate::errors::FileWriteFail;
38pub use crate::options::*;
39use crate::search_paths::SearchPath;
40use crate::utils::CanonicalizedPath;
41use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint};
42
43mod cfg;
44mod externs;
45mod native_libs;
46pub mod sigpipe;
47
48pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
49    // tidy-alphabetical-start
50    ("all-target-specs-json", PrintKind::AllTargetSpecsJson),
51    ("calling-conventions", PrintKind::CallingConventions),
52    ("cfg", PrintKind::Cfg),
53    ("check-cfg", PrintKind::CheckCfg),
54    ("code-models", PrintKind::CodeModels),
55    ("crate-name", PrintKind::CrateName),
56    ("crate-root-lint-levels", PrintKind::CrateRootLintLevels),
57    ("deployment-target", PrintKind::DeploymentTarget),
58    ("file-names", PrintKind::FileNames),
59    ("host-tuple", PrintKind::HostTuple),
60    ("link-args", PrintKind::LinkArgs),
61    ("native-static-libs", PrintKind::NativeStaticLibs),
62    ("relocation-models", PrintKind::RelocationModels),
63    ("split-debuginfo", PrintKind::SplitDebuginfo),
64    ("stack-protector-strategies", PrintKind::StackProtectorStrategies),
65    ("supported-crate-types", PrintKind::SupportedCrateTypes),
66    ("sysroot", PrintKind::Sysroot),
67    ("target-cpus", PrintKind::TargetCPUs),
68    ("target-features", PrintKind::TargetFeatures),
69    ("target-libdir", PrintKind::TargetLibdir),
70    ("target-list", PrintKind::TargetList),
71    ("target-spec-json", PrintKind::TargetSpecJson),
72    ("tls-models", PrintKind::TlsModels),
73    // tidy-alphabetical-end
74];
75
76/// The different settings that the `-C strip` flag can have.
77#[derive(Clone, Copy, PartialEq, Hash, Debug)]
78pub enum Strip {
79    /// Do not strip at all.
80    None,
81
82    /// Strip debuginfo.
83    Debuginfo,
84
85    /// Strip all symbols.
86    Symbols,
87}
88
89/// The different settings that the `-C control-flow-guard` flag can have.
90#[derive(Clone, Copy, PartialEq, Hash, Debug)]
91pub enum CFGuard {
92    /// Do not emit Control Flow Guard metadata or checks.
93    Disabled,
94
95    /// Emit Control Flow Guard metadata but no checks.
96    NoChecks,
97
98    /// Emit Control Flow Guard metadata and checks.
99    Checks,
100}
101
102/// The different settings that the `-Z cf-protection` flag can have.
103#[derive(Clone, Copy, PartialEq, Hash, Debug)]
104pub enum CFProtection {
105    /// Do not enable control-flow protection
106    None,
107
108    /// Emit control-flow protection for branches (enables indirect branch tracking).
109    Branch,
110
111    /// Emit control-flow protection for returns.
112    Return,
113
114    /// Emit control-flow protection for both branches and returns.
115    Full,
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)]
119pub enum OptLevel {
120    /// `-Copt-level=0`
121    No,
122    /// `-Copt-level=1`
123    Less,
124    /// `-Copt-level=2`
125    More,
126    /// `-Copt-level=3` / `-O`
127    Aggressive,
128    /// `-Copt-level=s`
129    Size,
130    /// `-Copt-level=z`
131    SizeMin,
132}
133
134/// This is what the `LtoCli` values get mapped to after resolving defaults and
135/// and taking other command line options into account.
136///
137/// Note that linker plugin-based LTO is a different mechanism entirely.
138#[derive(Clone, PartialEq)]
139pub enum Lto {
140    /// Don't do any LTO whatsoever.
141    No,
142
143    /// Do a full-crate-graph (inter-crate) LTO with ThinLTO.
144    Thin,
145
146    /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is
147    /// only relevant if multiple CGUs are used.
148    ThinLocal,
149
150    /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO.
151    Fat,
152}
153
154/// The different settings that the `-C lto` flag can have.
155#[derive(Clone, Copy, PartialEq, Hash, Debug)]
156pub enum LtoCli {
157    /// `-C lto=no`
158    No,
159    /// `-C lto=yes`
160    Yes,
161    /// `-C lto`
162    NoParam,
163    /// `-C lto=thin`
164    Thin,
165    /// `-C lto=fat`
166    Fat,
167    /// No `-C lto` flag passed
168    Unspecified,
169}
170
171/// The different settings that the `-C instrument-coverage` flag can have.
172#[derive(Clone, Copy, PartialEq, Hash, Debug)]
173pub enum InstrumentCoverage {
174    /// `-C instrument-coverage=no` (or `off`, `false` etc.)
175    No,
176    /// `-C instrument-coverage` or `-C instrument-coverage=yes`
177    Yes,
178}
179
180/// Individual flag values controlled by `-Zcoverage-options`.
181#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
182pub struct CoverageOptions {
183    pub level: CoverageLevel,
184
185    /// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans
186    /// from MIR statements/terminators, making it easier to inspect/debug
187    /// branch and MC/DC coverage mappings.
188    ///
189    /// For internal debugging only. If other code changes would make it hard
190    /// to keep supporting this flag, remove it.
191    pub no_mir_spans: bool,
192
193    /// `-Zcoverage-options=discard-all-spans-in-codegen`: During codgen,
194    /// discard all coverage spans as though they were invalid. Needed by
195    /// regression tests for #133606, because we don't have an easy way to
196    /// reproduce it from actual source code.
197    pub discard_all_spans_in_codegen: bool,
198}
199
200/// Controls whether branch coverage or MC/DC coverage is enabled.
201#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
202pub enum CoverageLevel {
203    /// Instrument for coverage at the MIR block level.
204    #[default]
205    Block,
206    /// Also instrument branch points (includes block coverage).
207    Branch,
208    /// Same as branch coverage, but also adds branch instrumentation for
209    /// certain boolean expressions that are not directly used for branching.
210    ///
211    /// For example, in the following code, `b` does not directly participate
212    /// in a branch, but condition coverage will instrument it as its own
213    /// artificial branch:
214    /// ```
215    /// # let (a, b) = (false, true);
216    /// let x = a && b;
217    /// //           ^ last operand
218    /// ```
219    ///
220    /// This level is mainly intended to be a stepping-stone towards full MC/DC
221    /// instrumentation, so it might be removed in the future when MC/DC is
222    /// sufficiently complete, or if it is making MC/DC changes difficult.
223    Condition,
224    /// Instrument for MC/DC. Mostly a superset of condition coverage, but might
225    /// differ in some corner cases.
226    Mcdc,
227}
228
229/// The different settings that the `-Z autodiff` flag can have.
230#[derive(Clone, Copy, PartialEq, Hash, Debug)]
231pub enum AutoDiff {
232    /// Enable the autodiff opt pipeline
233    Enable,
234
235    /// Print TypeAnalysis information
236    PrintTA,
237    /// Print ActivityAnalysis Information
238    PrintAA,
239    /// Print Performance Warnings from Enzyme
240    PrintPerf,
241    /// Print intermediate IR generation steps
242    PrintSteps,
243    /// Print the module, before running autodiff.
244    PrintModBefore,
245    /// Print the module after running autodiff.
246    PrintModAfter,
247    /// Print the module after running autodiff and optimizations.
248    PrintModFinal,
249
250    /// Print all passes scheduled by LLVM
251    PrintPasses,
252    /// Disable extra opt run after running autodiff
253    NoPostopt,
254    /// Enzyme's loose type debug helper (can cause incorrect gradients!!)
255    /// Usable in cases where Enzyme errors with `can not deduce type of X`.
256    LooseTypes,
257    /// Runs Enzyme's aggressive inlining
258    Inline,
259}
260
261/// Settings for `-Z instrument-xray` flag.
262#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
263pub struct InstrumentXRay {
264    /// `-Z instrument-xray=always`, force instrumentation
265    pub always: bool,
266    /// `-Z instrument-xray=never`, disable instrumentation
267    pub never: bool,
268    /// `-Z instrument-xray=ignore-loops`, ignore presence of loops,
269    /// instrument functions based only on instruction count
270    pub ignore_loops: bool,
271    /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold
272    /// for instrumentation, or `None` to use compiler's default
273    pub instruction_threshold: Option<usize>,
274    /// `-Z instrument-xray=skip-entry`, do not instrument function entry
275    pub skip_entry: bool,
276    /// `-Z instrument-xray=skip-exit`, do not instrument function exit
277    pub skip_exit: bool,
278}
279
280#[derive(Clone, PartialEq, Hash, Debug)]
281pub enum LinkerPluginLto {
282    LinkerPlugin(PathBuf),
283    LinkerPluginAuto,
284    Disabled,
285}
286
287impl LinkerPluginLto {
288    pub fn enabled(&self) -> bool {
289        match *self {
290            LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
291            LinkerPluginLto::Disabled => false,
292        }
293    }
294}
295
296/// The different values `-C link-self-contained` can take: a list of individually enabled or
297/// disabled components used during linking, coming from the rustc distribution, instead of being
298/// found somewhere on the host system.
299///
300/// They can be set in bulk via `-C link-self-contained=yes|y|on` or `-C
301/// link-self-contained=no|n|off`, and those boolean values are the historical defaults.
302///
303/// But each component is fine-grained, and can be unstably targeted, to use:
304/// - some CRT objects
305/// - the libc static library
306/// - libgcc/libunwind libraries
307/// - a linker we distribute
308/// - some sanitizer runtime libraries
309/// - all other MinGW libraries and Windows import libs
310///
311#[derive(Default, Clone, PartialEq, Debug)]
312pub struct LinkSelfContained {
313    /// Whether the user explicitly set `-C link-self-contained` on or off, the historical values.
314    /// Used for compatibility with the existing opt-in and target inference.
315    pub explicitly_set: Option<bool>,
316
317    /// The components that are enabled on the CLI, using the `+component` syntax or one of the
318    /// `true` shortcuts.
319    enabled_components: LinkSelfContainedComponents,
320
321    /// The components that are disabled on the CLI, using the `-component` syntax or one of the
322    /// `false` shortcuts.
323    disabled_components: LinkSelfContainedComponents,
324}
325
326impl LinkSelfContained {
327    /// Incorporates an enabled or disabled component as specified on the CLI, if possible.
328    /// For example: `+linker`, and `-crto`.
329    pub(crate) fn handle_cli_component(&mut self, component: &str) -> Option<()> {
330        // Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit
331        // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
332        // set in bulk with its historical values, then manually setting a component clears that
333        // `explicitly_set` state.
334        if let Some(component_to_enable) = component.strip_prefix('+') {
335            self.explicitly_set = None;
336            self.enabled_components
337                .insert(LinkSelfContainedComponents::from_str(component_to_enable)?);
338            Some(())
339        } else if let Some(component_to_disable) = component.strip_prefix('-') {
340            self.explicitly_set = None;
341            self.disabled_components
342                .insert(LinkSelfContainedComponents::from_str(component_to_disable)?);
343            Some(())
344        } else {
345            None
346        }
347    }
348
349    /// Turns all components on or off and records that this was done explicitly for compatibility
350    /// purposes.
351    pub(crate) fn set_all_explicitly(&mut self, enabled: bool) {
352        self.explicitly_set = Some(enabled);
353
354        if enabled {
355            self.enabled_components = LinkSelfContainedComponents::all();
356            self.disabled_components = LinkSelfContainedComponents::empty();
357        } else {
358            self.enabled_components = LinkSelfContainedComponents::empty();
359            self.disabled_components = LinkSelfContainedComponents::all();
360        }
361    }
362
363    /// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests.
364    pub fn on() -> Self {
365        let mut on = LinkSelfContained::default();
366        on.set_all_explicitly(true);
367        on
368    }
369
370    /// To help checking CLI usage while some of the values are unstable: returns whether one of the
371    /// components was set individually. This would also require the `-Zunstable-options` flag, to
372    /// be allowed.
373    fn are_unstable_variants_set(&self) -> bool {
374        let any_component_set =
375            !self.enabled_components.is_empty() || !self.disabled_components.is_empty();
376        self.explicitly_set.is_none() && any_component_set
377    }
378
379    /// Returns whether the self-contained linker component was enabled on the CLI, using the
380    /// `-C link-self-contained=+linker` syntax, or one of the `true` shortcuts.
381    pub fn is_linker_enabled(&self) -> bool {
382        self.enabled_components.contains(LinkSelfContainedComponents::LINKER)
383    }
384
385    /// Returns whether the self-contained linker component was disabled on the CLI, using the
386    /// `-C link-self-contained=-linker` syntax, or one of the `false` shortcuts.
387    pub fn is_linker_disabled(&self) -> bool {
388        self.disabled_components.contains(LinkSelfContainedComponents::LINKER)
389    }
390
391    /// Returns CLI inconsistencies to emit errors: individual components were both enabled and
392    /// disabled.
393    fn check_consistency(&self) -> Option<LinkSelfContainedComponents> {
394        if self.explicitly_set.is_some() {
395            None
396        } else {
397            let common = self.enabled_components.intersection(self.disabled_components);
398            if common.is_empty() { None } else { Some(common) }
399        }
400    }
401}
402
403/// The different values that `-Z linker-features` can take on the CLI: a list of individually
404/// enabled or disabled features used during linking.
405///
406/// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be
407/// used to turn `LinkerFeatures` on or off, without needing to change the linker flavor:
408/// - using the system lld, or the self-contained `rust-lld` linker
409/// - using a C/C++ compiler to drive the linker (not yet exposed on the CLI)
410/// - etc.
411#[derive(Default, Copy, Clone, PartialEq, Debug)]
412pub struct LinkerFeaturesCli {
413    /// The linker features that are enabled on the CLI, using the `+feature` syntax.
414    pub enabled: LinkerFeatures,
415
416    /// The linker features that are disabled on the CLI, using the `-feature` syntax.
417    pub disabled: LinkerFeatures,
418}
419
420impl LinkerFeaturesCli {
421    /// Accumulates an enabled or disabled feature as specified on the CLI, if possible.
422    /// For example: `+lld`, and `-lld`.
423    pub(crate) fn handle_cli_feature(&mut self, feature: &str) -> Option<()> {
424        // Duplicate flags are reduced as we go, the last occurrence wins:
425        // `+feature,-feature,+feature` only enables the feature, and does not record it as both
426        // enabled and disabled on the CLI.
427        // We also only expose `+/-lld` at the moment, as it's currently the only implemented linker
428        // feature and toggling `LinkerFeatures::CC` would be a noop.
429        match feature {
430            "+lld" => {
431                self.enabled.insert(LinkerFeatures::LLD);
432                self.disabled.remove(LinkerFeatures::LLD);
433                Some(())
434            }
435            "-lld" => {
436                self.disabled.insert(LinkerFeatures::LLD);
437                self.enabled.remove(LinkerFeatures::LLD);
438                Some(())
439            }
440            _ => None,
441        }
442    }
443}
444
445/// Used with `-Z assert-incr-state`.
446#[derive(Clone, Copy, PartialEq, Hash, Debug)]
447pub enum IncrementalStateAssertion {
448    /// Found and loaded an existing session directory.
449    ///
450    /// Note that this says nothing about whether any particular query
451    /// will be found to be red or green.
452    Loaded,
453    /// Did not load an existing session directory.
454    NotLoaded,
455}
456
457/// The different settings that can be enabled via the `-Z location-detail` flag.
458#[derive(Copy, Clone, PartialEq, Hash, Debug)]
459pub struct LocationDetail {
460    pub file: bool,
461    pub line: bool,
462    pub column: bool,
463}
464
465impl LocationDetail {
466    pub(crate) fn all() -> Self {
467        Self { file: true, line: true, column: true }
468    }
469}
470
471/// Values for the `-Z fmt-debug` flag.
472#[derive(Copy, Clone, PartialEq, Hash, Debug)]
473pub enum FmtDebug {
474    /// Derive fully-featured implementation
475    Full,
476    /// Print only type name, without fields
477    Shallow,
478    /// `#[derive(Debug)]` and `{:?}` are no-ops
479    None,
480}
481
482impl FmtDebug {
483    pub(crate) fn all() -> [Symbol; 3] {
484        [sym::full, sym::none, sym::shallow]
485    }
486}
487
488#[derive(Clone, PartialEq, Hash, Debug)]
489pub enum SwitchWithOptPath {
490    Enabled(Option<PathBuf>),
491    Disabled,
492}
493
494impl SwitchWithOptPath {
495    pub fn enabled(&self) -> bool {
496        match *self {
497            SwitchWithOptPath::Enabled(_) => true,
498            SwitchWithOptPath::Disabled => false,
499        }
500    }
501}
502
503#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic)]
504#[derive(Encodable, Decodable)]
505pub enum SymbolManglingVersion {
506    Legacy,
507    V0,
508    Hashed,
509}
510
511#[derive(Clone, Copy, Debug, PartialEq, Hash)]
512pub enum DebugInfo {
513    None,
514    LineDirectivesOnly,
515    LineTablesOnly,
516    Limited,
517    Full,
518}
519
520#[derive(Clone, Copy, Debug, PartialEq, Hash)]
521pub enum DebugInfoCompression {
522    None,
523    Zlib,
524    Zstd,
525}
526
527impl ToString for DebugInfoCompression {
528    fn to_string(&self) -> String {
529        match self {
530            DebugInfoCompression::None => "none",
531            DebugInfoCompression::Zlib => "zlib",
532            DebugInfoCompression::Zstd => "zstd",
533        }
534        .to_owned()
535    }
536}
537
538#[derive(Clone, Copy, Debug, PartialEq, Hash)]
539pub enum MirStripDebugInfo {
540    None,
541    LocalsInTinyFunctions,
542    AllLocals,
543}
544
545/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
546/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
547/// uses DWARF for debug-information.
548///
549/// Some debug-information requires link-time relocation and some does not. LLVM can partition
550/// the debuginfo into sections depending on whether or not it requires link-time relocation. Split
551/// DWARF provides a mechanism which allows the linker to skip the sections which don't require
552/// link-time relocation - either by putting those sections in DWARF object files, or by keeping
553/// them in the object file in such a way that the linker will skip them.
554#[derive(Clone, Copy, Debug, PartialEq, Hash)]
555pub enum SplitDwarfKind {
556    /// Sections which do not require relocation are written into object file but ignored by the
557    /// linker.
558    Single,
559    /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file
560    /// which is ignored by the linker.
561    Split,
562}
563
564impl FromStr for SplitDwarfKind {
565    type Err = ();
566
567    fn from_str(s: &str) -> Result<Self, ()> {
568        Ok(match s {
569            "single" => SplitDwarfKind::Single,
570            "split" => SplitDwarfKind::Split,
571            _ => return Err(()),
572        })
573    }
574}
575
576macro_rules! define_output_types {
577    (
578        $(
579            $(#[doc = $doc:expr])*
580            $Variant:ident => {
581                shorthand: $shorthand:expr,
582                extension: $extension:expr,
583                description: $description:expr,
584                default_filename: $default_filename:expr,
585                is_text: $is_text:expr,
586                compatible_with_cgus_and_single_output: $compatible:expr
587            }
588        ),* $(,)?
589    ) => {
590        #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
591        #[derive(Encodable, Decodable)]
592        pub enum OutputType {
593            $(
594                $(#[doc = $doc])*
595                $Variant,
596            )*
597        }
598
599
600        impl StableOrd for OutputType {
601            const CAN_USE_UNSTABLE_SORT: bool = true;
602
603            // Trivial C-Style enums have a stable sort order across compilation sessions.
604            const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
605        }
606
607        impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
608            type KeyType = Self;
609
610            fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
611                *self
612            }
613        }
614
615
616        impl OutputType {
617            pub fn iter_all() -> impl Iterator<Item = OutputType> {
618                static ALL_VARIANTS: &[OutputType] = &[
619                    $(
620                        OutputType::$Variant,
621                    )*
622                ];
623                ALL_VARIANTS.iter().copied()
624            }
625
626            fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
627                match *self {
628                    $(
629                        OutputType::$Variant => $compatible,
630                    )*
631                }
632            }
633
634            pub fn shorthand(&self) -> &'static str {
635                match *self {
636                    $(
637                        OutputType::$Variant => $shorthand,
638                    )*
639                }
640            }
641
642            fn from_shorthand(shorthand: &str) -> Option<Self> {
643                match shorthand {
644                    $(
645                        s if s == $shorthand => Some(OutputType::$Variant),
646                    )*
647                    _ => None,
648                }
649            }
650
651            fn shorthands_display() -> String {
652                let shorthands = vec![
653                    $(
654                        format!("`{}`", $shorthand),
655                    )*
656                ];
657                shorthands.join(", ")
658            }
659
660            pub fn extension(&self) -> &'static str {
661                match *self {
662                    $(
663                        OutputType::$Variant => $extension,
664                    )*
665                }
666            }
667
668            pub fn is_text_output(&self) -> bool {
669                match *self {
670                    $(
671                        OutputType::$Variant => $is_text,
672                    )*
673                }
674            }
675
676            pub fn description(&self) -> &'static str {
677                match *self {
678                    $(
679                        OutputType::$Variant => $description,
680                    )*
681                }
682            }
683
684            pub fn default_filename(&self) -> &'static str {
685                match *self {
686                    $(
687                        OutputType::$Variant => $default_filename,
688                    )*
689                }
690            }
691
692
693        }
694    }
695}
696
697define_output_types! {
698    Assembly => {
699        shorthand: "asm",
700        extension: "s",
701        description: "Generates a file with the crate's assembly code",
702        default_filename: "CRATE_NAME.s",
703        is_text: true,
704        compatible_with_cgus_and_single_output: false
705    },
706    #[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"]
707    #[doc = "depending on the specific request type."]
708    Bitcode => {
709        shorthand: "llvm-bc",
710        extension: "bc",
711        description: "Generates a binary file containing the LLVM bitcode",
712        default_filename: "CRATE_NAME.bc",
713        is_text: false,
714        compatible_with_cgus_and_single_output: false
715    },
716    DepInfo => {
717        shorthand: "dep-info",
718        extension: "d",
719        description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate",
720        default_filename: "CRATE_NAME.d",
721        is_text: true,
722        compatible_with_cgus_and_single_output: true
723    },
724    Exe => {
725        shorthand: "link",
726        extension: "",
727        description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified",
728        default_filename: "(platform and crate-type dependent)",
729        is_text: false,
730        compatible_with_cgus_and_single_output: true
731    },
732    LlvmAssembly => {
733        shorthand: "llvm-ir",
734        extension: "ll",
735        description: "Generates a file containing LLVM IR",
736        default_filename: "CRATE_NAME.ll",
737        is_text: true,
738        compatible_with_cgus_and_single_output: false
739    },
740    Metadata => {
741        shorthand: "metadata",
742        extension: "rmeta",
743        description: "Generates a file containing metadata about the crate",
744        default_filename: "libCRATE_NAME.rmeta",
745        is_text: false,
746        compatible_with_cgus_and_single_output: true
747    },
748    Mir => {
749        shorthand: "mir",
750        extension: "mir",
751        description: "Generates a file containing rustc's mid-level intermediate representation",
752        default_filename: "CRATE_NAME.mir",
753        is_text: true,
754        compatible_with_cgus_and_single_output: false
755    },
756    Object => {
757        shorthand: "obj",
758        extension: "o",
759        description: "Generates a native object file",
760        default_filename: "CRATE_NAME.o",
761        is_text: false,
762        compatible_with_cgus_and_single_output: false
763    },
764    #[doc = "This is the summary or index data part of the ThinLTO bitcode."]
765    ThinLinkBitcode => {
766        shorthand: "thin-link-bitcode",
767        extension: "indexing.o",
768        description: "Generates the ThinLTO summary as bitcode",
769        default_filename: "CRATE_NAME.indexing.o",
770        is_text: false,
771        compatible_with_cgus_and_single_output: false
772    },
773}
774
775/// The type of diagnostics output to generate.
776#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
777pub enum ErrorOutputType {
778    /// Output meant for the consumption of humans.
779    #[default]
780    HumanReadable {
781        kind: HumanReadableErrorType = HumanReadableErrorType::Default,
782        color_config: ColorConfig = ColorConfig::Auto,
783    },
784    /// Output that's consumed by other tools such as `rustfix` or the `RLS`.
785    Json {
786        /// Render the JSON in a human readable way (with indents and newlines).
787        pretty: bool,
788        /// The JSON output includes a `rendered` field that includes the rendered
789        /// human output.
790        json_rendered: HumanReadableErrorType,
791        color_config: ColorConfig,
792    },
793}
794
795#[derive(Clone, Hash, Debug)]
796pub enum ResolveDocLinks {
797    /// Do not resolve doc links.
798    None,
799    /// Resolve doc links on exported items only for crate types that have metadata.
800    ExportedMetadata,
801    /// Resolve doc links on exported items.
802    Exported,
803    /// Resolve doc links on all items.
804    All,
805}
806
807/// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
808/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break
809/// dependency tracking for command-line arguments. Also only hash keys, since tracking
810/// should only depend on the output types, not the paths they're written to.
811#[derive(Clone, Debug, Hash, HashStable_Generic, Encodable, Decodable)]
812pub struct OutputTypes(BTreeMap<OutputType, Option<OutFileName>>);
813
814impl OutputTypes {
815    pub fn new(entries: &[(OutputType, Option<OutFileName>)]) -> OutputTypes {
816        OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone()))))
817    }
818
819    pub(crate) fn get(&self, key: &OutputType) -> Option<&Option<OutFileName>> {
820        self.0.get(key)
821    }
822
823    pub fn contains_key(&self, key: &OutputType) -> bool {
824        self.0.contains_key(key)
825    }
826
827    /// Returns `true` if user specified a name and not just produced type
828    pub fn contains_explicit_name(&self, key: &OutputType) -> bool {
829        matches!(self.0.get(key), Some(Some(..)))
830    }
831
832    pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> {
833        self.0.iter()
834    }
835
836    pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<OutFileName>> {
837        self.0.keys()
838    }
839
840    pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<OutFileName>> {
841        self.0.values()
842    }
843
844    pub fn len(&self) -> usize {
845        self.0.len()
846    }
847
848    /// Returns `true` if any of the output types require codegen or linking.
849    pub fn should_codegen(&self) -> bool {
850        self.0.keys().any(|k| match *k {
851            OutputType::Bitcode
852            | OutputType::ThinLinkBitcode
853            | OutputType::Assembly
854            | OutputType::LlvmAssembly
855            | OutputType::Mir
856            | OutputType::Object
857            | OutputType::Exe => true,
858            OutputType::Metadata | OutputType::DepInfo => false,
859        })
860    }
861
862    /// Returns `true` if any of the output types require linking.
863    pub fn should_link(&self) -> bool {
864        self.0.keys().any(|k| match *k {
865            OutputType::Bitcode
866            | OutputType::ThinLinkBitcode
867            | OutputType::Assembly
868            | OutputType::LlvmAssembly
869            | OutputType::Mir
870            | OutputType::Metadata
871            | OutputType::Object
872            | OutputType::DepInfo => false,
873            OutputType::Exe => true,
874        })
875    }
876}
877
878/// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
879/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That
880/// would break dependency tracking for command-line arguments.
881#[derive(Clone)]
882pub struct Externs(BTreeMap<String, ExternEntry>);
883
884#[derive(Clone, Debug)]
885pub struct ExternEntry {
886    pub location: ExternLocation,
887    /// Indicates this is a "private" dependency for the
888    /// `exported_private_dependencies` lint.
889    ///
890    /// This can be set with the `priv` option like
891    /// `--extern priv:name=foo.rlib`.
892    pub is_private_dep: bool,
893    /// Add the extern entry to the extern prelude.
894    ///
895    /// This can be disabled with the `noprelude` option like
896    /// `--extern noprelude:name`.
897    pub add_prelude: bool,
898    /// The extern entry shouldn't be considered for unused dependency warnings.
899    ///
900    /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
901    /// suppress `unused-crate-dependencies` warnings.
902    pub nounused_dep: bool,
903    /// If the extern entry is not referenced in the crate, force it to be resolved anyway.
904    ///
905    /// Allows a dependency satisfying, for instance, a missing panic handler to be injected
906    /// without modifying source:
907    /// `--extern force:extras=/path/to/lib/libstd.rlib`
908    pub force: bool,
909}
910
911#[derive(Clone, Debug)]
912pub enum ExternLocation {
913    /// Indicates to look for the library in the search paths.
914    ///
915    /// Added via `--extern name`.
916    FoundInLibrarySearchDirectories,
917    /// The locations where this extern entry must be found.
918    ///
919    /// The `CrateLoader` is responsible for loading these and figuring out
920    /// which one to use.
921    ///
922    /// Added via `--extern prelude_name=some_file.rlib`
923    ExactPaths(BTreeSet<CanonicalizedPath>),
924}
925
926impl Externs {
927    /// Used for testing.
928    pub fn new(data: BTreeMap<String, ExternEntry>) -> Externs {
929        Externs(data)
930    }
931
932    pub fn get(&self, key: &str) -> Option<&ExternEntry> {
933        self.0.get(key)
934    }
935
936    pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
937        self.0.iter()
938    }
939}
940
941impl ExternEntry {
942    fn new(location: ExternLocation) -> ExternEntry {
943        ExternEntry {
944            location,
945            is_private_dep: false,
946            add_prelude: false,
947            nounused_dep: false,
948            force: false,
949        }
950    }
951
952    pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
953        match &self.location {
954            ExternLocation::ExactPaths(set) => Some(set.iter()),
955            _ => None,
956        }
957    }
958}
959
960#[derive(Clone, PartialEq, Debug)]
961pub struct PrintRequest {
962    pub kind: PrintKind,
963    pub out: OutFileName,
964}
965
966#[derive(Copy, Clone, PartialEq, Eq, Debug)]
967pub enum PrintKind {
968    // tidy-alphabetical-start
969    AllTargetSpecsJson,
970    CallingConventions,
971    Cfg,
972    CheckCfg,
973    CodeModels,
974    CrateName,
975    CrateRootLintLevels,
976    DeploymentTarget,
977    FileNames,
978    HostTuple,
979    LinkArgs,
980    NativeStaticLibs,
981    RelocationModels,
982    SplitDebuginfo,
983    StackProtectorStrategies,
984    SupportedCrateTypes,
985    Sysroot,
986    TargetCPUs,
987    TargetFeatures,
988    TargetLibdir,
989    TargetList,
990    TargetSpecJson,
991    TlsModels,
992    // tidy-alphabetical-end
993}
994
995#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)]
996pub struct NextSolverConfig {
997    /// Whether the new trait solver should be enabled in coherence.
998    pub coherence: bool = true,
999    /// Whether the new trait solver should be enabled everywhere.
1000    /// This is only `true` if `coherence` is also enabled.
1001    pub globally: bool = false,
1002}
1003
1004#[derive(Clone)]
1005pub enum Input {
1006    /// Load source code from a file.
1007    File(PathBuf),
1008    /// Load source code from a string.
1009    Str {
1010        /// A string that is shown in place of a filename.
1011        name: FileName,
1012        /// An anonymous string containing the source code.
1013        input: String,
1014    },
1015}
1016
1017impl Input {
1018    pub fn filestem(&self) -> &str {
1019        if let Input::File(ifile) = self {
1020            // If for some reason getting the file stem as a UTF-8 string fails,
1021            // then fallback to a fixed name.
1022            if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) {
1023                return name;
1024            }
1025        }
1026        "rust_out"
1027    }
1028
1029    pub fn source_name(&self) -> FileName {
1030        match *self {
1031            Input::File(ref ifile) => ifile.clone().into(),
1032            Input::Str { ref name, .. } => name.clone(),
1033        }
1034    }
1035
1036    pub fn opt_path(&self) -> Option<&Path> {
1037        match self {
1038            Input::File(file) => Some(file),
1039            Input::Str { name, .. } => match name {
1040                FileName::Real(real) => real.local_path(),
1041                FileName::CfgSpec(_) => None,
1042                FileName::Anon(_) => None,
1043                FileName::MacroExpansion(_) => None,
1044                FileName::ProcMacroSourceCode(_) => None,
1045                FileName::CliCrateAttr(_) => None,
1046                FileName::Custom(_) => None,
1047                FileName::DocTest(path, _) => Some(path),
1048                FileName::InlineAsm(_) => None,
1049            },
1050        }
1051    }
1052}
1053
1054#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Encodable, Decodable)]
1055pub enum OutFileName {
1056    Real(PathBuf),
1057    Stdout,
1058}
1059
1060impl OutFileName {
1061    pub fn parent(&self) -> Option<&Path> {
1062        match *self {
1063            OutFileName::Real(ref path) => path.parent(),
1064            OutFileName::Stdout => None,
1065        }
1066    }
1067
1068    pub fn filestem(&self) -> Option<&OsStr> {
1069        match *self {
1070            OutFileName::Real(ref path) => path.file_stem(),
1071            OutFileName::Stdout => Some(OsStr::new("stdout")),
1072        }
1073    }
1074
1075    pub fn is_stdout(&self) -> bool {
1076        match *self {
1077            OutFileName::Real(_) => false,
1078            OutFileName::Stdout => true,
1079        }
1080    }
1081
1082    pub fn is_tty(&self) -> bool {
1083        use std::io::IsTerminal;
1084        match *self {
1085            OutFileName::Real(_) => false,
1086            OutFileName::Stdout => std::io::stdout().is_terminal(),
1087        }
1088    }
1089
1090    pub fn as_path(&self) -> &Path {
1091        match *self {
1092            OutFileName::Real(ref path) => path.as_ref(),
1093            OutFileName::Stdout => Path::new("stdout"),
1094        }
1095    }
1096
1097    /// For a given output filename, return the actual name of the file that
1098    /// can be used to write codegen data of type `flavor`. For real-path
1099    /// output filenames, this would be trivial as we can just use the path.
1100    /// Otherwise for stdout, return a temporary path so that the codegen data
1101    /// may be later copied to stdout.
1102    pub fn file_for_writing(
1103        &self,
1104        outputs: &OutputFilenames,
1105        flavor: OutputType,
1106        codegen_unit_name: &str,
1107        invocation_temp: Option<&str>,
1108    ) -> PathBuf {
1109        match *self {
1110            OutFileName::Real(ref path) => path.clone(),
1111            OutFileName::Stdout => {
1112                outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp)
1113            }
1114        }
1115    }
1116
1117    pub fn overwrite(&self, content: &str, sess: &Session) {
1118        match self {
1119            OutFileName::Stdout => print!("{content}"),
1120            OutFileName::Real(path) => {
1121                if let Err(e) = fs::write(path, content) {
1122                    sess.dcx().emit_fatal(FileWriteFail { path, err: e.to_string() });
1123                }
1124            }
1125        }
1126    }
1127}
1128
1129#[derive(Clone, Hash, Debug, HashStable_Generic, Encodable, Decodable)]
1130pub struct OutputFilenames {
1131    pub(crate) out_directory: PathBuf,
1132    /// Crate name. Never contains '-'.
1133    crate_stem: String,
1134    /// Typically based on `.rs` input file name. Any '-' is preserved.
1135    filestem: String,
1136    pub single_output_file: Option<OutFileName>,
1137    temps_directory: Option<PathBuf>,
1138    pub outputs: OutputTypes,
1139}
1140
1141pub const RLINK_EXT: &str = "rlink";
1142pub const RUST_CGU_EXT: &str = "rcgu";
1143pub const DWARF_OBJECT_EXT: &str = "dwo";
1144
1145impl OutputFilenames {
1146    pub fn new(
1147        out_directory: PathBuf,
1148        out_crate_name: String,
1149        out_filestem: String,
1150        single_output_file: Option<OutFileName>,
1151        temps_directory: Option<PathBuf>,
1152        extra: String,
1153        outputs: OutputTypes,
1154    ) -> Self {
1155        OutputFilenames {
1156            out_directory,
1157            single_output_file,
1158            temps_directory,
1159            outputs,
1160            crate_stem: format!("{out_crate_name}{extra}"),
1161            filestem: format!("{out_filestem}{extra}"),
1162        }
1163    }
1164
1165    pub fn path(&self, flavor: OutputType) -> OutFileName {
1166        self.outputs
1167            .get(&flavor)
1168            .and_then(|p| p.to_owned())
1169            .or_else(|| self.single_output_file.clone())
1170            .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor)))
1171    }
1172
1173    pub fn interface_path(&self) -> PathBuf {
1174        self.out_directory.join(format!("lib{}.rs", self.crate_stem))
1175    }
1176
1177    /// Gets the output path where a compilation artifact of the given type
1178    /// should be placed on disk.
1179    fn output_path(&self, flavor: OutputType) -> PathBuf {
1180        let extension = flavor.extension();
1181        match flavor {
1182            OutputType::Metadata => {
1183                self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension))
1184            }
1185            _ => self.with_directory_and_extension(&self.out_directory, extension),
1186        }
1187    }
1188
1189    /// Gets the path where a compilation artifact of the given type for the
1190    /// given codegen unit should be placed on disk. If codegen_unit_name is
1191    /// None, a path distinct from those of any codegen unit will be generated.
1192    pub fn temp_path_for_cgu(
1193        &self,
1194        flavor: OutputType,
1195        codegen_unit_name: &str,
1196        invocation_temp: Option<&str>,
1197    ) -> PathBuf {
1198        let extension = flavor.extension();
1199        self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp)
1200    }
1201
1202    /// Like `temp_path`, but specifically for dwarf objects.
1203    pub fn temp_path_dwo_for_cgu(
1204        &self,
1205        codegen_unit_name: &str,
1206        invocation_temp: Option<&str>,
1207    ) -> PathBuf {
1208        self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp)
1209    }
1210
1211    /// Like `temp_path`, but also supports things where there is no corresponding
1212    /// OutputType, like noopt-bitcode or lto-bitcode.
1213    pub fn temp_path_ext_for_cgu(
1214        &self,
1215        ext: &str,
1216        codegen_unit_name: &str,
1217        invocation_temp: Option<&str>,
1218    ) -> PathBuf {
1219        let mut extension = codegen_unit_name.to_string();
1220
1221        // Append `.{invocation_temp}` to ensure temporary files are unique.
1222        if let Some(rng) = invocation_temp {
1223            extension.push('.');
1224            extension.push_str(rng);
1225        }
1226
1227        // FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty.
1228        // Append `.rcgu.{ext}`.
1229        if !ext.is_empty() {
1230            extension.push('.');
1231            extension.push_str(RUST_CGU_EXT);
1232            extension.push('.');
1233            extension.push_str(ext);
1234        }
1235
1236        let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
1237        self.with_directory_and_extension(temps_directory, &extension)
1238    }
1239
1240    pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf {
1241        let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
1242        self.with_directory_and_extension(temps_directory, &ext)
1243    }
1244
1245    pub fn with_extension(&self, extension: &str) -> PathBuf {
1246        self.with_directory_and_extension(&self.out_directory, extension)
1247    }
1248
1249    pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf {
1250        let mut path = directory.join(&self.filestem);
1251        path.set_extension(extension);
1252        path
1253    }
1254
1255    /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF
1256    /// mode is being used, which is the logic that this function is intended to encapsulate.
1257    pub fn split_dwarf_path(
1258        &self,
1259        split_debuginfo_kind: SplitDebuginfo,
1260        split_dwarf_kind: SplitDwarfKind,
1261        cgu_name: &str,
1262        invocation_temp: Option<&str>,
1263    ) -> Option<PathBuf> {
1264        let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp);
1265        let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp);
1266        match (split_debuginfo_kind, split_dwarf_kind) {
1267            (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
1268            // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
1269            // (pointing at the path which is being determined here). Use the path to the current
1270            // object file.
1271            (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
1272                Some(obj_out)
1273            }
1274            // Split mode emits the DWARF into a different file, use that path.
1275            (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
1276                Some(dwo_out)
1277            }
1278        }
1279    }
1280}
1281
1282bitflags::bitflags! {
1283    /// Scopes used to determined if it need to apply to --remap-path-prefix
1284    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
1285    pub struct RemapPathScopeComponents: u8 {
1286        /// Apply remappings to the expansion of std::file!() macro
1287        const MACRO = 1 << 0;
1288        /// Apply remappings to printed compiler diagnostics
1289        const DIAGNOSTICS = 1 << 1;
1290        /// Apply remappings to debug information
1291        const DEBUGINFO = 1 << 3;
1292
1293        /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled
1294        /// executables or libraries are remapped but not elsewhere.
1295        const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits();
1296    }
1297}
1298
1299pub fn host_tuple() -> &'static str {
1300    // Get the host triple out of the build environment. This ensures that our
1301    // idea of the host triple is the same as for the set of libraries we've
1302    // actually built. We can't just take LLVM's host triple because they
1303    // normalize all ix86 architectures to i386.
1304    //
1305    // Instead of grabbing the host triple (for the current host), we grab (at
1306    // compile time) the target triple that this rustc is built with and
1307    // calling that (at runtime) the host triple.
1308    (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
1309}
1310
1311fn file_path_mapping(
1312    remap_path_prefix: Vec<(PathBuf, PathBuf)>,
1313    unstable_opts: &UnstableOptions,
1314) -> FilePathMapping {
1315    FilePathMapping::new(
1316        remap_path_prefix.clone(),
1317        if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS)
1318            && !remap_path_prefix.is_empty()
1319        {
1320            FileNameDisplayPreference::Remapped
1321        } else {
1322            FileNameDisplayPreference::Local
1323        },
1324        if unstable_opts.remap_path_scope.is_all() {
1325            FileNameEmbeddablePreference::RemappedOnly
1326        } else {
1327            FileNameEmbeddablePreference::LocalAndRemapped
1328        },
1329    )
1330}
1331
1332impl Default for Options {
1333    fn default() -> Options {
1334        Options {
1335            assert_incr_state: None,
1336            crate_types: Vec::new(),
1337            optimize: OptLevel::No,
1338            debuginfo: DebugInfo::None,
1339            debuginfo_compression: DebugInfoCompression::None,
1340            lint_opts: Vec::new(),
1341            lint_cap: None,
1342            describe_lints: false,
1343            output_types: OutputTypes(BTreeMap::new()),
1344            search_paths: vec![],
1345            sysroot: filesearch::materialize_sysroot(None),
1346            target_triple: TargetTuple::from_tuple(host_tuple()),
1347            test: false,
1348            incremental: None,
1349            untracked_state_hash: Default::default(),
1350            unstable_opts: Default::default(),
1351            prints: Vec::new(),
1352            cg: Default::default(),
1353            error_format: ErrorOutputType::default(),
1354            diagnostic_width: None,
1355            externs: Externs(BTreeMap::new()),
1356            crate_name: None,
1357            libs: Vec::new(),
1358            unstable_features: UnstableFeatures::Disallow,
1359            debug_assertions: true,
1360            actually_rustdoc: false,
1361            resolve_doc_links: ResolveDocLinks::None,
1362            trimmed_def_paths: false,
1363            cli_forced_codegen_units: None,
1364            cli_forced_local_thinlto_off: false,
1365            remap_path_prefix: Vec::new(),
1366            real_rust_source_base_dir: None,
1367            real_rustc_dev_source_base_dir: None,
1368            edition: DEFAULT_EDITION,
1369            json_artifact_notifications: false,
1370            json_timings: false,
1371            json_unused_externs: JsonUnusedExterns::No,
1372            json_future_incompat: false,
1373            pretty: None,
1374            working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
1375            color: ColorConfig::Auto,
1376            logical_env: FxIndexMap::default(),
1377            verbose: false,
1378            target_modifiers: BTreeMap::default(),
1379        }
1380    }
1381}
1382
1383impl Options {
1384    /// Returns `true` if there is a reason to build the dep graph.
1385    pub fn build_dep_graph(&self) -> bool {
1386        self.incremental.is_some()
1387            || self.unstable_opts.dump_dep_graph
1388            || self.unstable_opts.query_dep_graph
1389    }
1390
1391    pub fn file_path_mapping(&self) -> FilePathMapping {
1392        file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts)
1393    }
1394
1395    /// Returns `true` if there will be an output file generated.
1396    pub fn will_create_output_file(&self) -> bool {
1397        !self.unstable_opts.parse_crate_root_only && // The file is just being parsed
1398            self.unstable_opts.ls.is_empty() // The file is just being queried
1399    }
1400
1401    #[inline]
1402    pub fn share_generics(&self) -> bool {
1403        match self.unstable_opts.share_generics {
1404            Some(setting) => setting,
1405            None => match self.optimize {
1406                OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
1407                OptLevel::More | OptLevel::Aggressive => false,
1408            },
1409        }
1410    }
1411
1412    pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
1413        self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
1414    }
1415}
1416
1417impl UnstableOptions {
1418    pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags {
1419        DiagCtxtFlags {
1420            can_emit_warnings,
1421            treat_err_as_bug: self.treat_err_as_bug,
1422            eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs,
1423            macro_backtrace: self.macro_backtrace,
1424            deduplicate_diagnostics: self.deduplicate_diagnostics,
1425            track_diagnostics: self.track_diagnostics,
1426        }
1427    }
1428
1429    pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm {
1430        self.src_hash_algorithm.unwrap_or_else(|| {
1431            if target.is_like_msvc {
1432                SourceFileHashAlgorithm::Sha256
1433            } else {
1434                SourceFileHashAlgorithm::Md5
1435            }
1436        })
1437    }
1438
1439    pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
1440        self.checksum_hash_algorithm
1441    }
1442}
1443
1444// The type of entry function, so users can have their own entry functions
1445#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
1446pub enum EntryFnType {
1447    Main {
1448        /// Specifies what to do with `SIGPIPE` before calling `fn main()`.
1449        ///
1450        /// What values that are valid and what they mean must be in sync
1451        /// across rustc and libstd, but we don't want it public in libstd,
1452        /// so we take a bit of an unusual approach with simple constants
1453        /// and an `include!()`.
1454        sigpipe: u8,
1455    },
1456}
1457
1458#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
1459#[derive(HashStable_Generic)]
1460pub enum CrateType {
1461    Executable,
1462    Dylib,
1463    Rlib,
1464    Staticlib,
1465    Cdylib,
1466    ProcMacro,
1467    Sdylib,
1468}
1469
1470impl CrateType {
1471    pub fn has_metadata(self) -> bool {
1472        match self {
1473            CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
1474            CrateType::Executable
1475            | CrateType::Cdylib
1476            | CrateType::Staticlib
1477            | CrateType::Sdylib => false,
1478        }
1479    }
1480}
1481
1482#[derive(Clone, Hash, Debug, PartialEq, Eq)]
1483pub enum Passes {
1484    Some(Vec<String>),
1485    All,
1486}
1487
1488impl Passes {
1489    fn is_empty(&self) -> bool {
1490        match *self {
1491            Passes::Some(ref v) => v.is_empty(),
1492            Passes::All => false,
1493        }
1494    }
1495
1496    pub(crate) fn extend(&mut self, passes: impl IntoIterator<Item = String>) {
1497        match *self {
1498            Passes::Some(ref mut v) => v.extend(passes),
1499            Passes::All => {}
1500        }
1501    }
1502}
1503
1504#[derive(Clone, Copy, Hash, Debug, PartialEq)]
1505pub enum PAuthKey {
1506    A,
1507    B,
1508}
1509
1510#[derive(Clone, Copy, Hash, Debug, PartialEq)]
1511pub struct PacRet {
1512    pub leaf: bool,
1513    pub pc: bool,
1514    pub key: PAuthKey,
1515}
1516
1517#[derive(Clone, Copy, Hash, Debug, PartialEq, Default)]
1518pub struct BranchProtection {
1519    pub bti: bool,
1520    pub pac_ret: Option<PacRet>,
1521}
1522
1523pub(crate) const fn default_lib_output() -> CrateType {
1524    CrateType::Rlib
1525}
1526
1527pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
1528    // First disallow some configuration given on the command line
1529    cfg::disallow_cfgs(sess, &user_cfg);
1530
1531    // Then combine the configuration requested by the session (command line) with
1532    // some default and generated configuration items.
1533    user_cfg.extend(cfg::default_configuration(sess));
1534    user_cfg
1535}
1536
1537pub fn build_target_config(
1538    early_dcx: &EarlyDiagCtxt,
1539    target: &TargetTuple,
1540    sysroot: &Path,
1541) -> Target {
1542    match Target::search(target, sysroot) {
1543        Ok((target, warnings)) => {
1544            for warning in warnings.warning_messages() {
1545                early_dcx.early_warn(warning)
1546            }
1547
1548            if !matches!(target.pointer_width, 16 | 32 | 64) {
1549                early_dcx.early_fatal(format!(
1550                    "target specification was invalid: unrecognized target-pointer-width {}",
1551                    target.pointer_width
1552                ))
1553            }
1554            target
1555        }
1556        Err(e) => {
1557            let mut err =
1558                early_dcx.early_struct_fatal(format!("error loading target specification: {e}"));
1559            err.help("run `rustc --print target-list` for a list of built-in targets");
1560            err.emit();
1561        }
1562    }
1563}
1564
1565#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1566pub enum OptionStability {
1567    Stable,
1568    Unstable,
1569}
1570
1571#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1572pub enum OptionKind {
1573    /// An option that takes a value, and cannot appear more than once (e.g. `--out-dir`).
1574    ///
1575    /// Corresponds to [`getopts::Options::optopt`].
1576    Opt,
1577
1578    /// An option that takes a value, and can appear multiple times (e.g. `--emit`).
1579    ///
1580    /// Corresponds to [`getopts::Options::optmulti`].
1581    Multi,
1582
1583    /// An option that does not take a value, and cannot appear more than once (e.g. `--help`).
1584    ///
1585    /// Corresponds to [`getopts::Options::optflag`].
1586    /// The `hint` string must be empty.
1587    Flag,
1588
1589    /// An option that does not take a value, and can appear multiple times (e.g. `-O`).
1590    ///
1591    /// Corresponds to [`getopts::Options::optflagmulti`].
1592    /// The `hint` string must be empty.
1593    FlagMulti,
1594}
1595
1596pub struct RustcOptGroup {
1597    /// The "primary" name for this option. Normally equal to `long_name`,
1598    /// except for options that don't have a long name, in which case
1599    /// `short_name` is used.
1600    ///
1601    /// This is needed when interacting with `getopts` in some situations,
1602    /// because if an option has both forms, that library treats the long name
1603    /// as primary and the short name as an alias.
1604    pub name: &'static str,
1605    stability: OptionStability,
1606    kind: OptionKind,
1607
1608    short_name: &'static str,
1609    long_name: &'static str,
1610    desc: &'static str,
1611    value_hint: &'static str,
1612
1613    /// If true, this option should not be printed by `rustc --help`, but
1614    /// should still be printed by `rustc --help -v`.
1615    pub is_verbose_help_only: bool,
1616}
1617
1618impl RustcOptGroup {
1619    pub fn is_stable(&self) -> bool {
1620        self.stability == OptionStability::Stable
1621    }
1622
1623    pub fn apply(&self, options: &mut getopts::Options) {
1624        let &Self { short_name, long_name, desc, value_hint, .. } = self;
1625        match self.kind {
1626            OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint),
1627            OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint),
1628            OptionKind::Flag => options.optflag(short_name, long_name, desc),
1629            OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
1630        };
1631    }
1632}
1633
1634pub fn make_opt(
1635    stability: OptionStability,
1636    kind: OptionKind,
1637    short_name: &'static str,
1638    long_name: &'static str,
1639    desc: &'static str,
1640    value_hint: &'static str,
1641) -> RustcOptGroup {
1642    // "Flag" options don't have a value, and therefore don't have a value hint.
1643    match kind {
1644        OptionKind::Opt | OptionKind::Multi => {}
1645        OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""),
1646    }
1647    RustcOptGroup {
1648        name: cmp::max_by_key(short_name, long_name, |s| s.len()),
1649        stability,
1650        kind,
1651        short_name,
1652        long_name,
1653        desc,
1654        value_hint,
1655        is_verbose_help_only: false,
1656    }
1657}
1658
1659static EDITION_STRING: LazyLock<String> = LazyLock::new(|| {
1660    format!(
1661        "Specify which edition of the compiler to use when compiling code. \
1662The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}."
1663    )
1664});
1665
1666static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
1667    format!(
1668        "Compiler information to print on stdout (or to a file)\n\
1669        INFO may be one of <{}>.",
1670        PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|")
1671    )
1672});
1673
1674static EMIT_HELP: LazyLock<String> = LazyLock::new(|| {
1675    let mut result =
1676        String::from("Comma separated list of types of output for the compiler to emit.\n");
1677    result.push_str("Each TYPE has the default FILE name:\n");
1678
1679    for output in OutputType::iter_all() {
1680        result.push_str(&format!("*  {} - {}\n", output.shorthand(), output.default_filename()));
1681    }
1682
1683    result
1684});
1685
1686/// Returns all rustc command line options, including metadata for
1687/// each option, such as whether the option is stable.
1688///
1689/// # Option style guidelines
1690///
1691/// - `<param>`: Indicates a required parameter
1692/// - `[param]`: Indicates an optional parameter
1693/// - `|`: Indicates a mutually exclusive option
1694/// - `*`: a list element with description
1695pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
1696    use OptionKind::{Flag, FlagMulti, Multi, Opt};
1697    use OptionStability::{Stable, Unstable};
1698
1699    use self::make_opt as opt;
1700
1701    let mut options = vec![
1702        opt(Stable, Flag, "h", "help", "Display this message", ""),
1703        opt(
1704            Stable,
1705            Multi,
1706            "",
1707            "cfg",
1708            "Configure the compilation environment.\n\
1709                SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.",
1710            "<SPEC>",
1711        ),
1712        opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"),
1713        opt(
1714            Stable,
1715            Multi,
1716            "L",
1717            "",
1718            "Add a directory to the library search path. \
1719                The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).",
1720            "[<KIND>=]<PATH>",
1721        ),
1722        opt(
1723            Stable,
1724            Multi,
1725            "l",
1726            "",
1727            "Link the generated crate(s) to the specified native\n\
1728                library NAME. The optional KIND can be one of\n\
1729                <static|framework|dylib> (default: dylib).\n\
1730                Optional comma separated MODIFIERS\n\
1731                <bundle|verbatim|whole-archive|as-needed>\n\
1732                may be specified each with a prefix of either '+' to\n\
1733                enable or '-' to disable.",
1734            "[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]",
1735        ),
1736        make_crate_type_option(),
1737        opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"),
1738        opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST),
1739        opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"),
1740        opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"),
1741        opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""),
1742        opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""),
1743        opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"),
1744        opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"),
1745        opt(
1746            Stable,
1747            Opt,
1748            "",
1749            "explain",
1750            "Provide a detailed explanation of an error message",
1751            "<OPT>",
1752        ),
1753        opt(Stable, Flag, "", "test", "Build a test harness", ""),
1754        opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"),
1755        opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"),
1756        opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"),
1757        opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"),
1758        opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"),
1759        opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"),
1760        opt(
1761            Stable,
1762            Multi,
1763            "",
1764            "cap-lints",
1765            "Set the most restrictive lint level. More restrictive lints are capped at this level",
1766            "<LEVEL>",
1767        ),
1768        opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"),
1769        opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
1770        opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
1771    ];
1772
1773    // Options in this list are hidden from `rustc --help` by default, but are
1774    // shown by `rustc --help -v`.
1775    let verbose_only = [
1776        opt(
1777            Stable,
1778            Multi,
1779            "",
1780            "extern",
1781            "Specify where an external rust library is located",
1782            "<NAME>[=<PATH>]",
1783        ),
1784        opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"),
1785        opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"),
1786        opt(
1787            Stable,
1788            Opt,
1789            "",
1790            "error-format",
1791            "How errors and other messages are produced",
1792            "<human|json|short>",
1793        ),
1794        opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"),
1795        opt(
1796            Stable,
1797            Opt,
1798            "",
1799            "color",
1800            "Configure coloring of output:
1801                * auto   = colorize, if output goes to a tty (default);
1802                * always = always colorize output;
1803                * never  = never colorize output",
1804            "<auto|always|never>",
1805        ),
1806        opt(
1807            Stable,
1808            Opt,
1809            "",
1810            "diagnostic-width",
1811            "Inform rustc of the width of the output so that diagnostics can be truncated to fit",
1812            "<WIDTH>",
1813        ),
1814        opt(
1815            Stable,
1816            Multi,
1817            "",
1818            "remap-path-prefix",
1819            "Remap source names in all output (compiler messages and output files)",
1820            "<FROM>=<TO>",
1821        ),
1822        opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"),
1823    ];
1824    options.extend(verbose_only.into_iter().map(|mut opt| {
1825        opt.is_verbose_help_only = true;
1826        opt
1827    }));
1828
1829    options
1830}
1831
1832pub fn get_cmd_lint_options(
1833    early_dcx: &EarlyDiagCtxt,
1834    matches: &getopts::Matches,
1835) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
1836    let mut lint_opts_with_position = vec![];
1837    let mut describe_lints = false;
1838
1839    for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
1840        for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
1841            if lint_name == "help" {
1842                describe_lints = true;
1843            } else {
1844                lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level));
1845            }
1846        }
1847    }
1848
1849    lint_opts_with_position.sort_by_key(|x| x.0);
1850    let lint_opts = lint_opts_with_position
1851        .iter()
1852        .cloned()
1853        .map(|(_, lint_name, level)| (lint_name, level))
1854        .collect();
1855
1856    let lint_cap = matches.opt_str("cap-lints").map(|cap| {
1857        lint::Level::from_str(&cap)
1858            .unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`")))
1859    });
1860
1861    (lint_opts, describe_lints, lint_cap)
1862}
1863
1864/// Parses the `--color` flag.
1865pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig {
1866    match matches.opt_str("color").as_deref() {
1867        Some("auto") => ColorConfig::Auto,
1868        Some("always") => ColorConfig::Always,
1869        Some("never") => ColorConfig::Never,
1870
1871        None => ColorConfig::Auto,
1872
1873        Some(arg) => early_dcx.early_fatal(format!(
1874            "argument for `--color` must be auto, \
1875                 always or never (instead was `{arg}`)"
1876        )),
1877    }
1878}
1879
1880/// Possible json config files
1881pub struct JsonConfig {
1882    pub json_rendered: HumanReadableErrorType,
1883    pub json_color: ColorConfig,
1884    json_artifact_notifications: bool,
1885    /// Output start and end timestamps of several high-level compilation sections
1886    /// (frontend, backend, linker).
1887    json_timings: bool,
1888    pub json_unused_externs: JsonUnusedExterns,
1889    json_future_incompat: bool,
1890}
1891
1892/// Report unused externs in event stream
1893#[derive(Copy, Clone)]
1894pub enum JsonUnusedExterns {
1895    /// Do not
1896    No,
1897    /// Report, but do not exit with failure status for deny/forbid
1898    Silent,
1899    /// Report, and also exit with failure status for deny/forbid
1900    Loud,
1901}
1902
1903impl JsonUnusedExterns {
1904    pub fn is_enabled(&self) -> bool {
1905        match self {
1906            JsonUnusedExterns::No => false,
1907            JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
1908        }
1909    }
1910
1911    pub fn is_loud(&self) -> bool {
1912        match self {
1913            JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
1914            JsonUnusedExterns::Loud => true,
1915        }
1916    }
1917}
1918
1919/// Parse the `--json` flag.
1920///
1921/// The first value returned is how to render JSON diagnostics, and the second
1922/// is whether or not artifact notifications are enabled.
1923pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig {
1924    let mut json_rendered = HumanReadableErrorType::Default;
1925    let mut json_color = ColorConfig::Never;
1926    let mut json_artifact_notifications = false;
1927    let mut json_unused_externs = JsonUnusedExterns::No;
1928    let mut json_future_incompat = false;
1929    let mut json_timings = false;
1930    for option in matches.opt_strs("json") {
1931        // For now conservatively forbid `--color` with `--json` since `--json`
1932        // won't actually be emitting any colors and anything colorized is
1933        // embedded in a diagnostic message anyway.
1934        if matches.opt_str("color").is_some() {
1935            early_dcx.early_fatal("cannot specify the `--color` option with `--json`");
1936        }
1937
1938        for sub_option in option.split(',') {
1939            match sub_option {
1940                "diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
1941                "diagnostic-unicode" => {
1942                    json_rendered = HumanReadableErrorType::Unicode;
1943                }
1944                "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
1945                "artifacts" => json_artifact_notifications = true,
1946                "timings" => json_timings = true,
1947                "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
1948                "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
1949                "future-incompat" => json_future_incompat = true,
1950                s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")),
1951            }
1952        }
1953    }
1954
1955    JsonConfig {
1956        json_rendered,
1957        json_color,
1958        json_artifact_notifications,
1959        json_timings,
1960        json_unused_externs,
1961        json_future_incompat,
1962    }
1963}
1964
1965/// Parses the `--error-format` flag.
1966pub fn parse_error_format(
1967    early_dcx: &mut EarlyDiagCtxt,
1968    matches: &getopts::Matches,
1969    color_config: ColorConfig,
1970    json_color: ColorConfig,
1971    json_rendered: HumanReadableErrorType,
1972) -> ErrorOutputType {
1973    // We need the `opts_present` check because the driver will send us Matches
1974    // with only stable options if no unstable options are used. Since error-format
1975    // is unstable, it will not be present. We have to use `opts_present` not
1976    // `opt_present` because the latter will panic.
1977    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
1978        match matches.opt_str("error-format").as_deref() {
1979            None | Some("human") => ErrorOutputType::HumanReadable { color_config, .. },
1980            Some("human-annotate-rs") => ErrorOutputType::HumanReadable {
1981                kind: HumanReadableErrorType::AnnotateSnippet,
1982                color_config,
1983            },
1984            Some("json") => {
1985                ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color }
1986            }
1987            Some("pretty-json") => {
1988                ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color }
1989            }
1990            Some("short") => {
1991                ErrorOutputType::HumanReadable { kind: HumanReadableErrorType::Short, color_config }
1992            }
1993            Some("human-unicode") => ErrorOutputType::HumanReadable {
1994                kind: HumanReadableErrorType::Unicode,
1995                color_config,
1996            },
1997            Some(arg) => {
1998                early_dcx.set_error_format(ErrorOutputType::HumanReadable { color_config, .. });
1999                early_dcx.early_fatal(format!(
2000                    "argument for `--error-format` must be `human`, `human-annotate-rs`, \
2001                    `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)"
2002                ))
2003            }
2004        }
2005    } else {
2006        ErrorOutputType::HumanReadable { color_config, .. }
2007    };
2008
2009    match error_format {
2010        ErrorOutputType::Json { .. } => {}
2011
2012        // Conservatively require that the `--json` argument is coupled with
2013        // `--error-format=json`. This means that `--json` is specified we
2014        // should actually be emitting JSON blobs.
2015        _ if !matches.opt_strs("json").is_empty() => {
2016            early_dcx.early_fatal("using `--json` requires also using `--error-format=json`");
2017        }
2018
2019        _ => {}
2020    }
2021
2022    error_format
2023}
2024
2025pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition {
2026    let edition = match matches.opt_str("edition") {
2027        Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
2028            early_dcx.early_fatal(format!(
2029                "argument for `--edition` must be one of: \
2030                     {EDITION_NAME_LIST}. (instead was `{arg}`)"
2031            ))
2032        }),
2033        None => DEFAULT_EDITION,
2034    };
2035
2036    if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) {
2037        let is_nightly = nightly_options::match_is_nightly_build(matches);
2038        let msg = if !is_nightly {
2039            format!(
2040                "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}"
2041            )
2042        } else {
2043            format!("edition {edition} is unstable and only available with -Z unstable-options")
2044        };
2045        early_dcx.early_fatal(msg)
2046    }
2047
2048    edition
2049}
2050
2051fn check_error_format_stability(
2052    early_dcx: &EarlyDiagCtxt,
2053    unstable_opts: &UnstableOptions,
2054    format: ErrorOutputType,
2055) {
2056    if unstable_opts.unstable_options {
2057        return;
2058    }
2059    let format = match format {
2060        ErrorOutputType::Json { pretty: true, .. } => "pretty-json",
2061        ErrorOutputType::HumanReadable { kind, .. } => match kind {
2062            HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs",
2063            HumanReadableErrorType::Unicode => "human-unicode",
2064            _ => return,
2065        },
2066        _ => return,
2067    };
2068    early_dcx.early_fatal(format!("`--error-format={format}` is unstable"))
2069}
2070
2071fn parse_output_types(
2072    early_dcx: &EarlyDiagCtxt,
2073    unstable_opts: &UnstableOptions,
2074    matches: &getopts::Matches,
2075) -> OutputTypes {
2076    let mut output_types = BTreeMap::new();
2077    if !unstable_opts.parse_crate_root_only {
2078        for list in matches.opt_strs("emit") {
2079            for output_type in list.split(',') {
2080                let (shorthand, path) = split_out_file_name(output_type);
2081                let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
2082                    early_dcx.early_fatal(format!(
2083                        "unknown emission type: `{shorthand}` - expected one of: {display}",
2084                        display = OutputType::shorthands_display(),
2085                    ))
2086                });
2087                if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options {
2088                    early_dcx.early_fatal(format!(
2089                        "{} requested but -Zunstable-options not specified",
2090                        OutputType::ThinLinkBitcode.shorthand()
2091                    ));
2092                }
2093                output_types.insert(output_type, path);
2094            }
2095        }
2096    };
2097    if output_types.is_empty() {
2098        output_types.insert(OutputType::Exe, None);
2099    }
2100    OutputTypes(output_types)
2101}
2102
2103fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) {
2104    match arg.split_once('=') {
2105        None => (arg, None),
2106        Some((kind, "-")) => (kind, Some(OutFileName::Stdout)),
2107        Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))),
2108    }
2109}
2110
2111fn should_override_cgus_and_disable_thinlto(
2112    early_dcx: &EarlyDiagCtxt,
2113    output_types: &OutputTypes,
2114    matches: &getopts::Matches,
2115    mut codegen_units: Option<usize>,
2116) -> (bool, Option<usize>) {
2117    let mut disable_local_thinlto = false;
2118    // Issue #30063: if user requests LLVM-related output to one
2119    // particular path, disable codegen-units.
2120    let incompatible: Vec<_> = output_types
2121        .0
2122        .iter()
2123        .map(|ot_path| ot_path.0)
2124        .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file())
2125        .map(|ot| ot.shorthand())
2126        .collect();
2127    if !incompatible.is_empty() {
2128        match codegen_units {
2129            Some(n) if n > 1 => {
2130                if matches.opt_present("o") {
2131                    for ot in &incompatible {
2132                        early_dcx.early_warn(format!(
2133                            "`--emit={ot}` with `-o` incompatible with \
2134                                 `-C codegen-units=N` for N > 1",
2135                        ));
2136                    }
2137                    early_dcx.early_warn("resetting to default -C codegen-units=1");
2138                    codegen_units = Some(1);
2139                    disable_local_thinlto = true;
2140                }
2141            }
2142            _ => {
2143                codegen_units = Some(1);
2144                disable_local_thinlto = true;
2145            }
2146        }
2147    }
2148
2149    if codegen_units == Some(0) {
2150        early_dcx.early_fatal("value for codegen units must be a positive non-zero integer");
2151    }
2152
2153    (disable_local_thinlto, codegen_units)
2154}
2155
2156fn collect_print_requests(
2157    early_dcx: &EarlyDiagCtxt,
2158    cg: &mut CodegenOptions,
2159    unstable_opts: &UnstableOptions,
2160    matches: &getopts::Matches,
2161) -> Vec<PrintRequest> {
2162    let mut prints = Vec::<PrintRequest>::new();
2163    if cg.target_cpu.as_deref() == Some("help") {
2164        prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
2165        cg.target_cpu = None;
2166    };
2167    if cg.target_feature == "help" {
2168        prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
2169        cg.target_feature = String::new();
2170    }
2171
2172    // We disallow reusing the same path in multiple prints, such as `--print
2173    // cfg=output.txt --print link-args=output.txt`, because outputs are printed
2174    // by disparate pieces of the compiler, and keeping track of which files
2175    // need to be overwritten vs appended to is annoying.
2176    let mut printed_paths = FxHashSet::default();
2177
2178    prints.extend(matches.opt_strs("print").into_iter().map(|req| {
2179        let (req, out) = split_out_file_name(&req);
2180
2181        let kind = if let Some((print_name, print_kind)) =
2182            PRINT_KINDS.iter().find(|&&(name, _)| name == req)
2183        {
2184            check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind));
2185            *print_kind
2186        } else {
2187            let is_nightly = nightly_options::match_is_nightly_build(matches);
2188            emit_unknown_print_request_help(early_dcx, req, is_nightly)
2189        };
2190
2191        let out = out.unwrap_or(OutFileName::Stdout);
2192        if let OutFileName::Real(path) = &out {
2193            if !printed_paths.insert(path.clone()) {
2194                early_dcx.early_fatal(format!(
2195                    "cannot print multiple outputs to the same path: {}",
2196                    path.display(),
2197                ));
2198            }
2199        }
2200
2201        PrintRequest { kind, out }
2202    }));
2203
2204    prints
2205}
2206
2207fn check_print_request_stability(
2208    early_dcx: &EarlyDiagCtxt,
2209    unstable_opts: &UnstableOptions,
2210    (print_name, print_kind): (&str, PrintKind),
2211) {
2212    if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options {
2213        early_dcx.early_fatal(format!(
2214            "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
2215                print option"
2216        ));
2217    }
2218}
2219
2220fn is_print_request_stable(print_kind: PrintKind) -> bool {
2221    match print_kind {
2222        PrintKind::AllTargetSpecsJson
2223        | PrintKind::CheckCfg
2224        | PrintKind::CrateRootLintLevels
2225        | PrintKind::SupportedCrateTypes
2226        | PrintKind::TargetSpecJson => false,
2227        _ => true,
2228    }
2229}
2230
2231fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
2232    let prints = PRINT_KINDS
2233        .iter()
2234        .filter_map(|(name, kind)| {
2235            // If we're not on nightly, we don't want to print unstable options
2236            if !is_nightly && !is_print_request_stable(*kind) {
2237                None
2238            } else {
2239                Some(format!("`{name}`"))
2240            }
2241        })
2242        .collect::<Vec<_>>();
2243    let prints = prints.join(", ");
2244
2245    let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
2246    #[allow(rustc::diagnostic_outside_of_impl)]
2247    diag.help(format!("valid print requests are: {prints}"));
2248
2249    if req == "lints" {
2250        diag.help(format!("use `-Whelp` to print a list of lints"));
2251    }
2252
2253    diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"));
2254    diag.emit()
2255}
2256
2257pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple {
2258    match matches.opt_str("target") {
2259        Some(target) if target.ends_with(".json") => {
2260            let path = Path::new(&target);
2261            TargetTuple::from_path(path).unwrap_or_else(|_| {
2262                early_dcx.early_fatal(format!("target file {path:?} does not exist"))
2263            })
2264        }
2265        Some(target) => TargetTuple::TargetTuple(target),
2266        _ => TargetTuple::from_tuple(host_tuple()),
2267    }
2268}
2269
2270fn parse_opt_level(
2271    early_dcx: &EarlyDiagCtxt,
2272    matches: &getopts::Matches,
2273    cg: &CodegenOptions,
2274) -> OptLevel {
2275    // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
2276    // to use them interchangeably. However, because they're technically different flags,
2277    // we need to work out manually which should take precedence if both are supplied (i.e.
2278    // the rightmost flag). We do this by finding the (rightmost) position of both flags and
2279    // comparing them. Note that if a flag is not found, its position will be `None`, which
2280    // always compared less than `Some(_)`.
2281    let max_o = matches.opt_positions("O").into_iter().max();
2282    let max_c = matches
2283        .opt_strs_pos("C")
2284        .into_iter()
2285        .flat_map(|(i, s)| {
2286            // NB: This can match a string without `=`.
2287            if let Some("opt-level") = s.split('=').next() { Some(i) } else { None }
2288        })
2289        .max();
2290    if max_o > max_c {
2291        OptLevel::Aggressive
2292    } else {
2293        match cg.opt_level.as_ref() {
2294            "0" => OptLevel::No,
2295            "1" => OptLevel::Less,
2296            "2" => OptLevel::More,
2297            "3" => OptLevel::Aggressive,
2298            "s" => OptLevel::Size,
2299            "z" => OptLevel::SizeMin,
2300            arg => {
2301                early_dcx.early_fatal(format!(
2302                    "optimization level needs to be \
2303                            between 0-3, s or z (instead was `{arg}`)"
2304                ));
2305            }
2306        }
2307    }
2308}
2309
2310fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInfo {
2311    let max_g = matches.opt_positions("g").into_iter().max();
2312    let max_c = matches
2313        .opt_strs_pos("C")
2314        .into_iter()
2315        .flat_map(|(i, s)| {
2316            // NB: This can match a string without `=`.
2317            if let Some("debuginfo") = s.split('=').next() { Some(i) } else { None }
2318        })
2319        .max();
2320    if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
2321}
2322
2323fn parse_assert_incr_state(
2324    early_dcx: &EarlyDiagCtxt,
2325    opt_assertion: &Option<String>,
2326) -> Option<IncrementalStateAssertion> {
2327    match opt_assertion {
2328        Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
2329        Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
2330        Some(s) => {
2331            early_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}"))
2332        }
2333        None => None,
2334    }
2335}
2336
2337pub fn parse_externs(
2338    early_dcx: &EarlyDiagCtxt,
2339    matches: &getopts::Matches,
2340    unstable_opts: &UnstableOptions,
2341) -> Externs {
2342    let is_unstable_enabled = unstable_opts.unstable_options;
2343    let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
2344    for arg in matches.opt_strs("extern") {
2345        let ExternOpt { crate_name: name, path, options } =
2346            split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit());
2347
2348        let entry = externs.entry(name.to_owned());
2349
2350        use std::collections::btree_map::Entry;
2351
2352        let entry = if let Some(path) = path {
2353            // --extern prelude_name=some_file.rlib
2354            let path = CanonicalizedPath::new(path);
2355            match entry {
2356                Entry::Vacant(vacant) => {
2357                    let files = BTreeSet::from_iter(iter::once(path));
2358                    vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files)))
2359                }
2360                Entry::Occupied(occupied) => {
2361                    let ext_ent = occupied.into_mut();
2362                    match ext_ent {
2363                        ExternEntry { location: ExternLocation::ExactPaths(files), .. } => {
2364                            files.insert(path);
2365                        }
2366                        ExternEntry {
2367                            location: location @ ExternLocation::FoundInLibrarySearchDirectories,
2368                            ..
2369                        } => {
2370                            // Exact paths take precedence over search directories.
2371                            let files = BTreeSet::from_iter(iter::once(path));
2372                            *location = ExternLocation::ExactPaths(files);
2373                        }
2374                    }
2375                    ext_ent
2376                }
2377            }
2378        } else {
2379            // --extern prelude_name
2380            match entry {
2381                Entry::Vacant(vacant) => {
2382                    vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
2383                }
2384                Entry::Occupied(occupied) => {
2385                    // Ignore if already specified.
2386                    occupied.into_mut()
2387                }
2388            }
2389        };
2390
2391        let mut is_private_dep = false;
2392        let mut add_prelude = true;
2393        let mut nounused_dep = false;
2394        let mut force = false;
2395        if let Some(opts) = options {
2396            if !is_unstable_enabled {
2397                early_dcx.early_fatal(
2398                    "the `-Z unstable-options` flag must also be passed to \
2399                     enable `--extern` options",
2400                );
2401            }
2402            for opt in opts.split(',') {
2403                match opt {
2404                    "priv" => is_private_dep = true,
2405                    "noprelude" => {
2406                        if let ExternLocation::ExactPaths(_) = &entry.location {
2407                            add_prelude = false;
2408                        } else {
2409                            early_dcx.early_fatal(
2410                                "the `noprelude` --extern option requires a file path",
2411                            );
2412                        }
2413                    }
2414                    "nounused" => nounused_dep = true,
2415                    "force" => force = true,
2416                    _ => early_dcx.early_fatal(format!("unknown --extern option `{opt}`")),
2417                }
2418            }
2419        }
2420
2421        // Crates start out being not private, and go to being private `priv`
2422        // is specified.
2423        entry.is_private_dep |= is_private_dep;
2424        // likewise `nounused`
2425        entry.nounused_dep |= nounused_dep;
2426        // and `force`
2427        entry.force |= force;
2428        // If any flag is missing `noprelude`, then add to the prelude.
2429        entry.add_prelude |= add_prelude;
2430    }
2431    Externs(externs)
2432}
2433
2434fn parse_remap_path_prefix(
2435    early_dcx: &EarlyDiagCtxt,
2436    matches: &getopts::Matches,
2437    unstable_opts: &UnstableOptions,
2438) -> Vec<(PathBuf, PathBuf)> {
2439    let mut mapping: Vec<(PathBuf, PathBuf)> = matches
2440        .opt_strs("remap-path-prefix")
2441        .into_iter()
2442        .map(|remap| match remap.rsplit_once('=') {
2443            None => {
2444                early_dcx.early_fatal("--remap-path-prefix must contain '=' between FROM and TO")
2445            }
2446            Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
2447        })
2448        .collect();
2449    match &unstable_opts.remap_cwd_prefix {
2450        Some(to) => match std::env::current_dir() {
2451            Ok(cwd) => mapping.push((cwd, to.clone())),
2452            Err(_) => (),
2453        },
2454        None => (),
2455    };
2456    mapping
2457}
2458
2459fn parse_logical_env(
2460    early_dcx: &EarlyDiagCtxt,
2461    matches: &getopts::Matches,
2462) -> FxIndexMap<String, String> {
2463    let mut vars = FxIndexMap::default();
2464
2465    for arg in matches.opt_strs("env-set") {
2466        if let Some((name, val)) = arg.split_once('=') {
2467            vars.insert(name.to_string(), val.to_string());
2468        } else {
2469            early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`"));
2470        }
2471    }
2472
2473    vars
2474}
2475
2476// JUSTIFICATION: before wrapper fn is available
2477#[allow(rustc::bad_opt_access)]
2478pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options {
2479    let color = parse_color(early_dcx, matches);
2480
2481    let edition = parse_crate_edition(early_dcx, matches);
2482
2483    let JsonConfig {
2484        json_rendered,
2485        json_color,
2486        json_artifact_notifications,
2487        json_timings,
2488        json_unused_externs,
2489        json_future_incompat,
2490    } = parse_json(early_dcx, matches);
2491
2492    let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered);
2493
2494    early_dcx.set_error_format(error_format);
2495
2496    let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
2497        early_dcx.early_fatal("`--diagnostic-width` must be an positive integer");
2498    });
2499
2500    let unparsed_crate_types = matches.opt_strs("crate-type");
2501    let crate_types = parse_crate_types_from_list(unparsed_crate_types)
2502        .unwrap_or_else(|e| early_dcx.early_fatal(e));
2503
2504    let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new();
2505
2506    let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
2507    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
2508
2509    if !unstable_opts.unstable_options && json_timings {
2510        early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`");
2511    }
2512
2513    check_error_format_stability(early_dcx, &unstable_opts, error_format);
2514
2515    let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
2516
2517    let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
2518    let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
2519        early_dcx,
2520        &output_types,
2521        matches,
2522        cg.codegen_units,
2523    );
2524
2525    if unstable_opts.threads == 0 {
2526        early_dcx.early_fatal("value for threads must be a positive non-zero integer");
2527    }
2528
2529    if unstable_opts.threads == parse::MAX_THREADS_CAP {
2530        early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP));
2531    }
2532
2533    let incremental = cg.incremental.as_ref().map(PathBuf::from);
2534
2535    let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
2536
2537    if cg.profile_generate.enabled() && cg.profile_use.is_some() {
2538        early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive");
2539    }
2540
2541    if unstable_opts.profile_sample_use.is_some()
2542        && (cg.profile_generate.enabled() || cg.profile_use.is_some())
2543    {
2544        early_dcx.early_fatal(
2545            "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
2546        );
2547    }
2548
2549    // Check for unstable values of `-C symbol-mangling-version`.
2550    // This is what prevents them from being used on stable compilers.
2551    match cg.symbol_mangling_version {
2552        // Stable values:
2553        None | Some(SymbolManglingVersion::V0) => {}
2554
2555        // Unstable values:
2556        Some(SymbolManglingVersion::Legacy) => {
2557            if !unstable_opts.unstable_options {
2558                early_dcx.early_fatal(
2559                    "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
2560                );
2561            }
2562        }
2563        Some(SymbolManglingVersion::Hashed) => {
2564            if !unstable_opts.unstable_options {
2565                early_dcx.early_fatal(
2566                    "`-C symbol-mangling-version=hashed` requires `-Z unstable-options`",
2567                );
2568            }
2569        }
2570    }
2571
2572    if cg.instrument_coverage != InstrumentCoverage::No {
2573        if cg.profile_generate.enabled() || cg.profile_use.is_some() {
2574            early_dcx.early_fatal(
2575                "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
2576                or `-C profile-generate`",
2577            );
2578        }
2579
2580        // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
2581        // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
2582        // multiple runs, including some changes to source code; so mangled names must be consistent
2583        // across compilations.
2584        match cg.symbol_mangling_version {
2585            None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
2586            Some(SymbolManglingVersion::Legacy) => {
2587                early_dcx.early_warn(
2588                    "-C instrument-coverage requires symbol mangling version `v0`, \
2589                    but `-C symbol-mangling-version=legacy` was specified",
2590                );
2591            }
2592            Some(SymbolManglingVersion::V0) => {}
2593            Some(SymbolManglingVersion::Hashed) => {
2594                early_dcx.early_warn(
2595                    "-C instrument-coverage requires symbol mangling version `v0`, \
2596                    but `-C symbol-mangling-version=hashed` was specified",
2597                );
2598            }
2599        }
2600    }
2601
2602    if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
2603        // FIXME: this is only mutation of UnstableOptions here, move into
2604        // UnstableOptions::build?
2605        unstable_opts.graphviz_font = graphviz_font;
2606    }
2607
2608    if !cg.embed_bitcode {
2609        match cg.lto {
2610            LtoCli::No | LtoCli::Unspecified => {}
2611            LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
2612                early_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible")
2613            }
2614        }
2615    }
2616
2617    if !nightly_options::is_unstable_enabled(matches)
2618        && cg.force_frame_pointers == FramePointer::NonLeaf
2619    {
2620        early_dcx.early_fatal(
2621            "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \
2622                and a nightly compiler",
2623        )
2624    }
2625
2626    // For testing purposes, until we have more feedback about these options: ensure `-Z
2627    // unstable-options` is required when using the unstable `-C link-self-contained` and `-C
2628    // linker-flavor` options.
2629    if !nightly_options::is_unstable_enabled(matches) {
2630        let uses_unstable_self_contained_option =
2631            cg.link_self_contained.are_unstable_variants_set();
2632        if uses_unstable_self_contained_option {
2633            early_dcx.early_fatal(
2634                "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
2635                the `-Z unstable-options` flag must also be passed to use the unstable values",
2636            );
2637        }
2638
2639        if let Some(flavor) = cg.linker_flavor {
2640            if flavor.is_unstable() {
2641                early_dcx.early_fatal(format!(
2642                    "the linker flavor `{}` is unstable, the `-Z unstable-options` \
2643                        flag must also be passed to use the unstable values",
2644                    flavor.desc()
2645                ));
2646            }
2647        }
2648    }
2649
2650    // Check `-C link-self-contained` for consistency: individual components cannot be both enabled
2651    // and disabled at the same time.
2652    if let Some(erroneous_components) = cg.link_self_contained.check_consistency() {
2653        let names: String = erroneous_components
2654            .into_iter()
2655            .map(|c| c.as_str().unwrap())
2656            .intersperse(", ")
2657            .collect();
2658        early_dcx.early_fatal(format!(
2659            "some `-C link-self-contained` components were both enabled and disabled: {names}"
2660        ));
2661    }
2662
2663    let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches);
2664
2665    // -Zretpoline-external-thunk also requires -Zretpoline
2666    if unstable_opts.retpoline_external_thunk {
2667        unstable_opts.retpoline = true;
2668        target_modifiers.insert(
2669            OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline),
2670            "true".to_string(),
2671        );
2672    }
2673
2674    let cg = cg;
2675
2676    let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
2677    let target_triple = parse_target_triple(early_dcx, matches);
2678    let opt_level = parse_opt_level(early_dcx, matches, &cg);
2679    // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
2680    // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
2681    // for more details.
2682    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
2683    let debuginfo = select_debuginfo(matches, &cg);
2684    let debuginfo_compression = unstable_opts.debuginfo_compression;
2685
2686    let crate_name = matches.opt_str("crate-name");
2687    let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref());
2688    // Parse any `-l` flags, which link to native libraries.
2689    let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches);
2690
2691    let test = matches.opt_present("test");
2692
2693    if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
2694        early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
2695    }
2696
2697    if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() {
2698        early_dcx
2699            .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
2700    }
2701
2702    let externs = parse_externs(early_dcx, matches, &unstable_opts);
2703
2704    let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
2705
2706    let pretty = parse_pretty(early_dcx, &unstable_opts);
2707
2708    // query-dep-graph is required if dump-dep-graph is given #106736
2709    if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
2710        early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`");
2711    }
2712
2713    let logical_env = parse_logical_env(early_dcx, matches);
2714
2715    let sysroot = filesearch::materialize_sysroot(sysroot_opt);
2716
2717    let real_source_base_dir = |suffix: &str, confirm: &str| {
2718        let mut candidate = sysroot.join(suffix);
2719        if let Ok(metadata) = candidate.symlink_metadata() {
2720            // Replace the symlink bootstrap creates, with its destination.
2721            // We could try to use `fs::canonicalize` instead, but that might
2722            // produce unnecessarily verbose path.
2723            if metadata.file_type().is_symlink() {
2724                if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
2725                    candidate = symlink_dest;
2726                }
2727            }
2728        }
2729
2730        // Only use this directory if it has a file we can expect to always find.
2731        candidate.join(confirm).is_file().then_some(candidate)
2732    };
2733
2734    let real_rust_source_base_dir =
2735        // This is the location used by the `rust-src` `rustup` component.
2736        real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs");
2737
2738    let real_rustc_dev_source_base_dir =
2739        // This is the location used by the `rustc-dev` `rustup` component.
2740        real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs");
2741
2742    let mut search_paths = vec![];
2743    for s in &matches.opt_strs("L") {
2744        search_paths.push(SearchPath::from_cli_opt(
2745            &sysroot,
2746            &target_triple,
2747            early_dcx,
2748            s,
2749            unstable_opts.unstable_options,
2750        ));
2751    }
2752
2753    let working_dir = std::env::current_dir().unwrap_or_else(|e| {
2754        early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
2755    });
2756
2757    let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
2758    let working_dir = file_mapping.to_real_filename(&working_dir);
2759
2760    let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals;
2761
2762    Options {
2763        assert_incr_state,
2764        crate_types,
2765        optimize: opt_level,
2766        debuginfo,
2767        debuginfo_compression,
2768        lint_opts,
2769        lint_cap,
2770        describe_lints,
2771        output_types,
2772        search_paths,
2773        sysroot,
2774        target_triple,
2775        test,
2776        incremental,
2777        untracked_state_hash: Default::default(),
2778        unstable_opts,
2779        prints,
2780        cg,
2781        error_format,
2782        diagnostic_width,
2783        externs,
2784        unstable_features,
2785        crate_name,
2786        libs,
2787        debug_assertions,
2788        actually_rustdoc: false,
2789        resolve_doc_links: ResolveDocLinks::ExportedMetadata,
2790        trimmed_def_paths: false,
2791        cli_forced_codegen_units: codegen_units,
2792        cli_forced_local_thinlto_off: disable_local_thinlto,
2793        remap_path_prefix,
2794        real_rust_source_base_dir,
2795        real_rustc_dev_source_base_dir,
2796        edition,
2797        json_artifact_notifications,
2798        json_timings,
2799        json_unused_externs,
2800        json_future_incompat,
2801        pretty,
2802        working_dir,
2803        color,
2804        logical_env,
2805        verbose,
2806        target_modifiers,
2807    }
2808}
2809
2810fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> Option<PpMode> {
2811    use PpMode::*;
2812
2813    let first = match unstable_opts.unpretty.as_deref()? {
2814        "normal" => Source(PpSourceMode::Normal),
2815        "identified" => Source(PpSourceMode::Identified),
2816        "expanded" => Source(PpSourceMode::Expanded),
2817        "expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
2818        "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
2819        "ast-tree" => AstTree,
2820        "ast-tree,expanded" => AstTreeExpanded,
2821        "hir" => Hir(PpHirMode::Normal),
2822        "hir,identified" => Hir(PpHirMode::Identified),
2823        "hir,typed" => Hir(PpHirMode::Typed),
2824        "hir-tree" => HirTree,
2825        "thir-tree" => ThirTree,
2826        "thir-flat" => ThirFlat,
2827        "mir" => Mir,
2828        "stable-mir" => StableMir,
2829        "mir-cfg" => MirCFG,
2830        name => early_dcx.early_fatal(format!(
2831            "argument to `unpretty` must be one of `normal`, `identified`, \
2832                            `expanded`, `expanded,identified`, `expanded,hygiene`, \
2833                            `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
2834                            `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \
2835                            `mir-cfg`; got {name}"
2836        )),
2837    };
2838    debug!("got unpretty option: {first:?}");
2839    Some(first)
2840}
2841
2842pub fn make_crate_type_option() -> RustcOptGroup {
2843    make_opt(
2844        OptionStability::Stable,
2845        OptionKind::Multi,
2846        "",
2847        "crate-type",
2848        "Comma separated list of types of crates
2849                                for the compiler to emit",
2850        "<bin|lib|rlib|dylib|cdylib|staticlib|proc-macro>",
2851    )
2852}
2853
2854pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
2855    let mut crate_types: Vec<CrateType> = Vec::new();
2856    for unparsed_crate_type in &list_list {
2857        for part in unparsed_crate_type.split(',') {
2858            let new_part = match part {
2859                "lib" => default_lib_output(),
2860                "rlib" => CrateType::Rlib,
2861                "staticlib" => CrateType::Staticlib,
2862                "dylib" => CrateType::Dylib,
2863                "cdylib" => CrateType::Cdylib,
2864                "bin" => CrateType::Executable,
2865                "proc-macro" => CrateType::ProcMacro,
2866                "sdylib" => CrateType::Sdylib,
2867                _ => {
2868                    return Err(format!(
2869                        "unknown crate type: `{part}`, expected one of: \
2870                        `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`",
2871                    ));
2872                }
2873            };
2874            if !crate_types.contains(&new_part) {
2875                crate_types.push(new_part)
2876            }
2877        }
2878    }
2879
2880    Ok(crate_types)
2881}
2882
2883pub mod nightly_options {
2884    use rustc_feature::UnstableFeatures;
2885
2886    use super::{OptionStability, RustcOptGroup};
2887    use crate::EarlyDiagCtxt;
2888
2889    pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
2890        match_is_nightly_build(matches)
2891            && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
2892    }
2893
2894    pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool {
2895        is_nightly_build(matches.opt_str("crate-name").as_deref())
2896    }
2897
2898    fn is_nightly_build(krate: Option<&str>) -> bool {
2899        UnstableFeatures::from_environment(krate).is_nightly_build()
2900    }
2901
2902    pub fn check_nightly_options(
2903        early_dcx: &EarlyDiagCtxt,
2904        matches: &getopts::Matches,
2905        flags: &[RustcOptGroup],
2906    ) {
2907        let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
2908        let really_allows_unstable_options = match_is_nightly_build(matches);
2909        let mut nightly_options_on_stable = 0;
2910
2911        for opt in flags.iter() {
2912            if opt.stability == OptionStability::Stable {
2913                continue;
2914            }
2915            if !matches.opt_present(opt.name) {
2916                continue;
2917            }
2918            if opt.name != "Z" && !has_z_unstable_option {
2919                early_dcx.early_fatal(format!(
2920                    "the `-Z unstable-options` flag must also be passed to enable \
2921                         the flag `{}`",
2922                    opt.name
2923                ));
2924            }
2925            if really_allows_unstable_options {
2926                continue;
2927            }
2928            match opt.stability {
2929                OptionStability::Unstable => {
2930                    nightly_options_on_stable += 1;
2931                    let msg = format!(
2932                        "the option `{}` is only accepted on the nightly compiler",
2933                        opt.name
2934                    );
2935                    // The non-zero nightly_options_on_stable will force an early_fatal eventually.
2936                    let _ = early_dcx.early_err(msg);
2937                }
2938                OptionStability::Stable => {}
2939            }
2940        }
2941        if nightly_options_on_stable > 0 {
2942            early_dcx
2943                .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
2944            early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
2945            early_dcx.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
2946            early_dcx.early_fatal(format!(
2947                "{} nightly option{} were parsed",
2948                nightly_options_on_stable,
2949                if nightly_options_on_stable > 1 { "s" } else { "" }
2950            ));
2951        }
2952    }
2953}
2954
2955impl fmt::Display for CrateType {
2956    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2957        match *self {
2958            CrateType::Executable => "bin".fmt(f),
2959            CrateType::Dylib => "dylib".fmt(f),
2960            CrateType::Rlib => "rlib".fmt(f),
2961            CrateType::Staticlib => "staticlib".fmt(f),
2962            CrateType::Cdylib => "cdylib".fmt(f),
2963            CrateType::ProcMacro => "proc-macro".fmt(f),
2964            CrateType::Sdylib => "sdylib".fmt(f),
2965        }
2966    }
2967}
2968
2969impl IntoDiagArg for CrateType {
2970    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
2971        self.to_string().into_diag_arg(&mut None)
2972    }
2973}
2974
2975#[derive(Copy, Clone, PartialEq, Debug)]
2976pub enum PpSourceMode {
2977    /// `-Zunpretty=normal`
2978    Normal,
2979    /// `-Zunpretty=expanded`
2980    Expanded,
2981    /// `-Zunpretty=identified`
2982    Identified,
2983    /// `-Zunpretty=expanded,identified`
2984    ExpandedIdentified,
2985    /// `-Zunpretty=expanded,hygiene`
2986    ExpandedHygiene,
2987}
2988
2989#[derive(Copy, Clone, PartialEq, Debug)]
2990pub enum PpHirMode {
2991    /// `-Zunpretty=hir`
2992    Normal,
2993    /// `-Zunpretty=hir,identified`
2994    Identified,
2995    /// `-Zunpretty=hir,typed`
2996    Typed,
2997}
2998
2999#[derive(Copy, Clone, PartialEq, Debug)]
3000/// Pretty print mode
3001pub enum PpMode {
3002    /// Options that print the source code, i.e.
3003    /// `-Zunpretty=normal` and `-Zunpretty=expanded`
3004    Source(PpSourceMode),
3005    /// `-Zunpretty=ast-tree`
3006    AstTree,
3007    /// `-Zunpretty=ast-tree,expanded`
3008    AstTreeExpanded,
3009    /// Options that print the HIR, i.e. `-Zunpretty=hir`
3010    Hir(PpHirMode),
3011    /// `-Zunpretty=hir-tree`
3012    HirTree,
3013    /// `-Zunpretty=thir-tree`
3014    ThirTree,
3015    /// `-Zunpretty=thir-flat`
3016    ThirFlat,
3017    /// `-Zunpretty=mir`
3018    Mir,
3019    /// `-Zunpretty=mir-cfg`
3020    MirCFG,
3021    /// `-Zunpretty=stable-mir`
3022    StableMir,
3023}
3024
3025impl PpMode {
3026    pub fn needs_ast_map(&self) -> bool {
3027        use PpMode::*;
3028        use PpSourceMode::*;
3029        match *self {
3030            Source(Normal | Identified) | AstTree => false,
3031
3032            Source(Expanded | ExpandedIdentified | ExpandedHygiene)
3033            | AstTreeExpanded
3034            | Hir(_)
3035            | HirTree
3036            | ThirTree
3037            | ThirFlat
3038            | Mir
3039            | MirCFG
3040            | StableMir => true,
3041        }
3042    }
3043
3044    pub fn needs_analysis(&self) -> bool {
3045        use PpMode::*;
3046        matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat)
3047    }
3048}
3049
3050#[derive(Clone, Hash, PartialEq, Eq, Debug)]
3051pub enum WasiExecModel {
3052    Command,
3053    Reactor,
3054}
3055
3056/// Command-line arguments passed to the compiler have to be incorporated with
3057/// the dependency tracking system for incremental compilation. This module
3058/// provides some utilities to make this more convenient.
3059///
3060/// The values of all command-line arguments that are relevant for dependency
3061/// tracking are hashed into a single value that determines whether the
3062/// incremental compilation cache can be re-used or not. This hashing is done
3063/// via the `DepTrackingHash` trait defined below, since the standard `Hash`
3064/// implementation might not be suitable (e.g., arguments are stored in a `Vec`,
3065/// the hash of which is order dependent, but we might not want the order of
3066/// arguments to make a difference for the hash).
3067///
3068/// However, since the value provided by `Hash::hash` often *is* suitable,
3069/// especially for primitive types, there is the
3070/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the
3071/// `Hash` implementation for `DepTrackingHash`. It's important though that
3072/// we have an opt-in scheme here, so one is hopefully forced to think about
3073/// how the hash should be calculated when adding a new command-line argument.
3074pub(crate) mod dep_tracking {
3075    use std::collections::BTreeMap;
3076    use std::hash::Hash;
3077    use std::num::NonZero;
3078    use std::path::PathBuf;
3079
3080    use rustc_abi::Align;
3081    use rustc_data_structures::fx::FxIndexMap;
3082    use rustc_data_structures::stable_hasher::StableHasher;
3083    use rustc_errors::LanguageIdentifier;
3084    use rustc_feature::UnstableFeatures;
3085    use rustc_hashes::Hash64;
3086    use rustc_span::RealFileName;
3087    use rustc_span::edition::Edition;
3088    use rustc_target::spec::{
3089        CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
3090        RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple,
3091        TlsModel,
3092    };
3093
3094    use super::{
3095        AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions,
3096        CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn,
3097        InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
3098        LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, OutFileName,
3099        OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents,
3100        ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
3101        SymbolManglingVersion, WasiExecModel,
3102    };
3103    use crate::lint;
3104    use crate::utils::NativeLib;
3105
3106    pub(crate) trait DepTrackingHash {
3107        fn hash(
3108            &self,
3109            hasher: &mut StableHasher,
3110            error_format: ErrorOutputType,
3111            for_crate_hash: bool,
3112        );
3113    }
3114
3115    macro_rules! impl_dep_tracking_hash_via_hash {
3116        ($($t:ty),+ $(,)?) => {$(
3117            impl DepTrackingHash for $t {
3118                fn hash(&self, hasher: &mut StableHasher, _: ErrorOutputType, _for_crate_hash: bool) {
3119                    Hash::hash(self, hasher);
3120                }
3121            }
3122        )+};
3123    }
3124
3125    impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
3126        fn hash(
3127            &self,
3128            hasher: &mut StableHasher,
3129            error_format: ErrorOutputType,
3130            for_crate_hash: bool,
3131        ) {
3132            match self {
3133                Some(x) => {
3134                    Hash::hash(&1, hasher);
3135                    DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
3136                }
3137                None => Hash::hash(&0, hasher),
3138            }
3139        }
3140    }
3141
3142    impl_dep_tracking_hash_via_hash!(
3143        (),
3144        AutoDiff,
3145        bool,
3146        usize,
3147        NonZero<usize>,
3148        u64,
3149        Hash64,
3150        String,
3151        PathBuf,
3152        lint::Level,
3153        WasiExecModel,
3154        u32,
3155        FramePointer,
3156        RelocModel,
3157        CodeModel,
3158        TlsModel,
3159        InstrumentCoverage,
3160        CoverageOptions,
3161        InstrumentXRay,
3162        CrateType,
3163        MergeFunctions,
3164        OnBrokenPipe,
3165        PanicStrategy,
3166        RelroLevel,
3167        OptLevel,
3168        LtoCli,
3169        DebugInfo,
3170        DebugInfoCompression,
3171        MirStripDebugInfo,
3172        CollapseMacroDebuginfo,
3173        UnstableFeatures,
3174        NativeLib,
3175        SanitizerSet,
3176        CFGuard,
3177        CFProtection,
3178        TargetTuple,
3179        Edition,
3180        LinkerPluginLto,
3181        ResolveDocLinks,
3182        SplitDebuginfo,
3183        SplitDwarfKind,
3184        StackProtector,
3185        SwitchWithOptPath,
3186        SymbolManglingVersion,
3187        SymbolVisibility,
3188        RemapPathScopeComponents,
3189        SourceFileHashAlgorithm,
3190        OutFileName,
3191        OutputType,
3192        RealFileName,
3193        LocationDetail,
3194        FmtDebug,
3195        BranchProtection,
3196        OomStrategy,
3197        LanguageIdentifier,
3198        NextSolverConfig,
3199        PatchableFunctionEntry,
3200        Polonius,
3201        InliningThreshold,
3202        FunctionReturn,
3203        Align,
3204    );
3205
3206    impl<T1, T2> DepTrackingHash for (T1, T2)
3207    where
3208        T1: DepTrackingHash,
3209        T2: DepTrackingHash,
3210    {
3211        fn hash(
3212            &self,
3213            hasher: &mut StableHasher,
3214            error_format: ErrorOutputType,
3215            for_crate_hash: bool,
3216        ) {
3217            Hash::hash(&0, hasher);
3218            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3219            Hash::hash(&1, hasher);
3220            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3221        }
3222    }
3223
3224    impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
3225    where
3226        T1: DepTrackingHash,
3227        T2: DepTrackingHash,
3228        T3: DepTrackingHash,
3229    {
3230        fn hash(
3231            &self,
3232            hasher: &mut StableHasher,
3233            error_format: ErrorOutputType,
3234            for_crate_hash: bool,
3235        ) {
3236            Hash::hash(&0, hasher);
3237            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3238            Hash::hash(&1, hasher);
3239            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3240            Hash::hash(&2, hasher);
3241            DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
3242        }
3243    }
3244
3245    impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
3246        fn hash(
3247            &self,
3248            hasher: &mut StableHasher,
3249            error_format: ErrorOutputType,
3250            for_crate_hash: bool,
3251        ) {
3252            Hash::hash(&self.len(), hasher);
3253            for (index, elem) in self.iter().enumerate() {
3254                Hash::hash(&index, hasher);
3255                DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
3256            }
3257        }
3258    }
3259
3260    impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
3261        fn hash(
3262            &self,
3263            hasher: &mut StableHasher,
3264            error_format: ErrorOutputType,
3265            for_crate_hash: bool,
3266        ) {
3267            Hash::hash(&self.len(), hasher);
3268            for (key, value) in self.iter() {
3269                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3270                DepTrackingHash::hash(value, hasher, error_format, for_crate_hash);
3271            }
3272        }
3273    }
3274
3275    impl DepTrackingHash for OutputTypes {
3276        fn hash(
3277            &self,
3278            hasher: &mut StableHasher,
3279            error_format: ErrorOutputType,
3280            for_crate_hash: bool,
3281        ) {
3282            Hash::hash(&self.0.len(), hasher);
3283            for (key, val) in &self.0 {
3284                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3285                if !for_crate_hash {
3286                    DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
3287                }
3288            }
3289        }
3290    }
3291
3292    // This is a stable hash because BTreeMap is a sorted container
3293    pub(crate) fn stable_hash(
3294        sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
3295        hasher: &mut StableHasher,
3296        error_format: ErrorOutputType,
3297        for_crate_hash: bool,
3298    ) {
3299        for (key, sub_hash) in sub_hashes {
3300            // Using Hash::hash() instead of DepTrackingHash::hash() is fine for
3301            // the keys, as they are just plain strings
3302            Hash::hash(&key.len(), hasher);
3303            Hash::hash(key, hasher);
3304            sub_hash.hash(hasher, error_format, for_crate_hash);
3305        }
3306    }
3307}
3308
3309/// Default behavior to use in out-of-memory situations.
3310#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
3311pub enum OomStrategy {
3312    /// Generate a panic that can be caught by `catch_unwind`.
3313    Panic,
3314
3315    /// Abort the process immediately.
3316    Abort,
3317}
3318
3319impl OomStrategy {
3320    pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic";
3321
3322    pub fn should_panic(self) -> u8 {
3323        match self {
3324            OomStrategy::Panic => 1,
3325            OomStrategy::Abort => 0,
3326        }
3327    }
3328}
3329
3330/// How to run proc-macro code when building this crate
3331#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3332pub enum ProcMacroExecutionStrategy {
3333    /// Run the proc-macro code on the same thread as the server.
3334    SameThread,
3335
3336    /// Run the proc-macro code on a different thread.
3337    CrossThread,
3338}
3339
3340/// How to perform collapse macros debug info
3341/// if-ext - if macro from different crate (related to callsite code)
3342/// | cmd \ attr    | no  | (unspecified) | external | yes |
3343/// | no            | no  | no            | no       | no  |
3344/// | (unspecified) | no  | no            | if-ext   | yes |
3345/// | external      | no  | if-ext        | if-ext   | yes |
3346/// | yes           | yes | yes           | yes      | yes |
3347#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3348pub enum CollapseMacroDebuginfo {
3349    /// Don't collapse debuginfo for the macro
3350    No = 0,
3351    /// Unspecified value
3352    Unspecified = 1,
3353    /// Collapse debuginfo if the macro comes from a different crate
3354    External = 2,
3355    /// Collapse debuginfo for the macro
3356    Yes = 3,
3357}
3358
3359/// Which format to use for `-Z dump-mono-stats`
3360#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3361pub enum DumpMonoStatsFormat {
3362    /// Pretty-print a markdown table
3363    Markdown,
3364    /// Emit structured JSON
3365    Json,
3366}
3367
3368impl DumpMonoStatsFormat {
3369    pub fn extension(self) -> &'static str {
3370        match self {
3371            Self::Markdown => "md",
3372            Self::Json => "json",
3373        }
3374    }
3375}
3376
3377/// `-Z patchable-function-entry` representation - how many nops to put before and after function
3378/// entry.
3379#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3380pub struct PatchableFunctionEntry {
3381    /// Nops before the entry
3382    prefix: u8,
3383    /// Nops after the entry
3384    entry: u8,
3385}
3386
3387impl PatchableFunctionEntry {
3388    pub fn from_total_and_prefix_nops(
3389        total_nops: u8,
3390        prefix_nops: u8,
3391    ) -> Option<PatchableFunctionEntry> {
3392        if total_nops < prefix_nops {
3393            None
3394        } else {
3395            Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3396        }
3397    }
3398    pub fn prefix(&self) -> u8 {
3399        self.prefix
3400    }
3401    pub fn entry(&self) -> u8 {
3402        self.entry
3403    }
3404}
3405
3406/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
3407/// or future prototype.
3408#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3409pub enum Polonius {
3410    /// The default value: disabled.
3411    #[default]
3412    Off,
3413
3414    /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
3415    Legacy,
3416
3417    /// In-tree prototype, extending the NLL infrastructure.
3418    Next,
3419}
3420
3421impl Polonius {
3422    /// Returns whether the legacy version of polonius is enabled
3423    pub fn is_legacy_enabled(&self) -> bool {
3424        matches!(self, Polonius::Legacy)
3425    }
3426
3427    /// Returns whether the "next" version of polonius is enabled
3428    pub fn is_next_enabled(&self) -> bool {
3429        matches!(self, Polonius::Next)
3430    }
3431}
3432
3433#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3434pub enum InliningThreshold {
3435    Always,
3436    Sometimes(usize),
3437    Never,
3438}
3439
3440impl Default for InliningThreshold {
3441    fn default() -> Self {
3442        Self::Sometimes(100)
3443    }
3444}
3445
3446/// The different settings that the `-Zfunction-return` flag can have.
3447#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3448pub enum FunctionReturn {
3449    /// Keep the function return unmodified.
3450    #[default]
3451    Keep,
3452
3453    /// Replace returns with jumps to thunk, without emitting the thunk.
3454    ThunkExtern,
3455}
3456
3457/// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag.
3458/// By default, only enabled in the NLL MIR dumps, and disabled in all other passes.
3459#[derive(Clone, Copy, Default, PartialEq, Debug)]
3460pub enum MirIncludeSpans {
3461    Off,
3462    On,
3463    /// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as
3464    /// `Off` in all other cases.
3465    #[default]
3466    Nll,
3467}
3468
3469impl MirIncludeSpans {
3470    /// Unless opting into extra comments for all passes, they can be considered disabled.
3471    /// The cases where a distinction between on/off and a per-pass value can exist will be handled
3472    /// in the passes themselves: i.e. the `Nll` value is considered off for all intents and
3473    /// purposes, except for the NLL MIR dump pass.
3474    pub fn is_enabled(self) -> bool {
3475        self == MirIncludeSpans::On
3476    }
3477}