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