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