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::Default { short: 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(
1969    early_dcx: &EarlyDiagCtxt,
1970    matches: &getopts::Matches,
1971    is_nightly_build: bool,
1972) -> JsonConfig {
1973    let mut json_rendered = if is_nightly_build {
1974        HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false }
1975    } else {
1976        HumanReadableErrorType::Default { short: false }
1977    };
1978    let mut json_color = ColorConfig::Never;
1979    let mut json_artifact_notifications = false;
1980    let mut json_unused_externs = JsonUnusedExterns::No;
1981    let mut json_future_incompat = false;
1982    let mut json_timings = false;
1983    for option in matches.opt_strs("json") {
1984        // For now conservatively forbid `--color` with `--json` since `--json`
1985        // won't actually be emitting any colors and anything colorized is
1986        // embedded in a diagnostic message anyway.
1987        if matches.opt_str("color").is_some() {
1988            early_dcx.early_fatal("cannot specify the `--color` option with `--json`");
1989        }
1990
1991        for sub_option in option.split(',') {
1992            match sub_option {
1993                "diagnostic-short" => {
1994                    json_rendered = if is_nightly_build {
1995                        HumanReadableErrorType::AnnotateSnippet { short: true, unicode: false }
1996                    } else {
1997                        HumanReadableErrorType::Default { short: true }
1998                    };
1999                }
2000                "diagnostic-unicode" => {
2001                    json_rendered =
2002                        HumanReadableErrorType::AnnotateSnippet { short: false, unicode: true };
2003                }
2004                "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
2005                "artifacts" => json_artifact_notifications = true,
2006                "timings" => json_timings = true,
2007                "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
2008                "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
2009                "future-incompat" => json_future_incompat = true,
2010                s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")),
2011            }
2012        }
2013    }
2014
2015    JsonConfig {
2016        json_rendered,
2017        json_color,
2018        json_artifact_notifications,
2019        json_timings,
2020        json_unused_externs,
2021        json_future_incompat,
2022    }
2023}
2024
2025/// Parses the `--error-format` flag.
2026pub fn parse_error_format(
2027    early_dcx: &mut EarlyDiagCtxt,
2028    matches: &getopts::Matches,
2029    color_config: ColorConfig,
2030    json_color: ColorConfig,
2031    json_rendered: HumanReadableErrorType,
2032    is_nightly_build: bool,
2033) -> ErrorOutputType {
2034    let default_kind = if is_nightly_build {
2035        HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false }
2036    } else {
2037        HumanReadableErrorType::Default { short: false }
2038    };
2039    // We need the `opts_present` check because the driver will send us Matches
2040    // with only stable options if no unstable options are used. Since error-format
2041    // is unstable, it will not be present. We have to use `opts_present` not
2042    // `opt_present` because the latter will panic.
2043    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
2044        match matches.opt_str("error-format").as_deref() {
2045            None | Some("human") => {
2046                ErrorOutputType::HumanReadable { color_config, kind: default_kind }
2047            }
2048            Some("human-annotate-rs") => ErrorOutputType::HumanReadable {
2049                kind: HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false },
2050                color_config,
2051            },
2052            Some("json") => {
2053                ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color }
2054            }
2055            Some("pretty-json") => {
2056                ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color }
2057            }
2058            Some("short") => ErrorOutputType::HumanReadable {
2059                kind: if is_nightly_build {
2060                    HumanReadableErrorType::AnnotateSnippet { short: true, unicode: false }
2061                } else {
2062                    HumanReadableErrorType::Default { short: true }
2063                },
2064                color_config,
2065            },
2066            Some("human-unicode") => ErrorOutputType::HumanReadable {
2067                kind: HumanReadableErrorType::AnnotateSnippet { short: false, unicode: true },
2068                color_config,
2069            },
2070            Some(arg) => {
2071                early_dcx.set_error_format(ErrorOutputType::HumanReadable {
2072                    color_config,
2073                    kind: default_kind,
2074                });
2075                early_dcx.early_fatal(format!(
2076                    "argument for `--error-format` must be `human`, `human-annotate-rs`, \
2077                    `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)"
2078                ))
2079            }
2080        }
2081    } else {
2082        ErrorOutputType::HumanReadable { color_config, kind: default_kind }
2083    };
2084
2085    match error_format {
2086        ErrorOutputType::Json { .. } => {}
2087
2088        // Conservatively require that the `--json` argument is coupled with
2089        // `--error-format=json`. This means that `--json` is specified we
2090        // should actually be emitting JSON blobs.
2091        _ if !matches.opt_strs("json").is_empty() => {
2092            early_dcx.early_fatal("using `--json` requires also using `--error-format=json`");
2093        }
2094
2095        _ => {}
2096    }
2097
2098    error_format
2099}
2100
2101pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition {
2102    let edition = match matches.opt_str("edition") {
2103        Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
2104            early_dcx.early_fatal(format!(
2105                "argument for `--edition` must be one of: \
2106                     {EDITION_NAME_LIST}. (instead was `{arg}`)"
2107            ))
2108        }),
2109        None => DEFAULT_EDITION,
2110    };
2111
2112    if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) {
2113        let is_nightly = nightly_options::match_is_nightly_build(matches);
2114        let msg = if !is_nightly {
2115            format!(
2116                "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}"
2117            )
2118        } else {
2119            format!("edition {edition} is unstable and only available with -Z unstable-options")
2120        };
2121        early_dcx.early_fatal(msg)
2122    }
2123
2124    edition
2125}
2126
2127fn check_error_format_stability(
2128    early_dcx: &EarlyDiagCtxt,
2129    unstable_opts: &UnstableOptions,
2130    is_nightly_build: bool,
2131    format: ErrorOutputType,
2132) {
2133    if unstable_opts.unstable_options || is_nightly_build {
2134        return;
2135    }
2136    let format = match format {
2137        ErrorOutputType::Json { pretty: true, .. } => "pretty-json",
2138        ErrorOutputType::HumanReadable { kind, .. } => match kind {
2139            HumanReadableErrorType::AnnotateSnippet { unicode: false, .. } => "human-annotate-rs",
2140            HumanReadableErrorType::AnnotateSnippet { unicode: true, .. } => "human-unicode",
2141            _ => return,
2142        },
2143        _ => return,
2144    };
2145    early_dcx.early_fatal(format!("`--error-format={format}` is unstable"))
2146}
2147
2148fn parse_output_types(
2149    early_dcx: &EarlyDiagCtxt,
2150    unstable_opts: &UnstableOptions,
2151    matches: &getopts::Matches,
2152) -> OutputTypes {
2153    let mut output_types = BTreeMap::new();
2154    if !unstable_opts.parse_crate_root_only {
2155        for list in matches.opt_strs("emit") {
2156            for output_type in list.split(',') {
2157                let (shorthand, path) = split_out_file_name(output_type);
2158                let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
2159                    early_dcx.early_fatal(format!(
2160                        "unknown emission type: `{shorthand}` - expected one of: {display}",
2161                        display = OutputType::shorthands_display(),
2162                    ))
2163                });
2164                if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options {
2165                    early_dcx.early_fatal(format!(
2166                        "{} requested but -Zunstable-options not specified",
2167                        OutputType::ThinLinkBitcode.shorthand()
2168                    ));
2169                }
2170                output_types.insert(output_type, path);
2171            }
2172        }
2173    };
2174    if output_types.is_empty() {
2175        output_types.insert(OutputType::Exe, None);
2176    }
2177    OutputTypes(output_types)
2178}
2179
2180fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) {
2181    match arg.split_once('=') {
2182        None => (arg, None),
2183        Some((kind, "-")) => (kind, Some(OutFileName::Stdout)),
2184        Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))),
2185    }
2186}
2187
2188fn should_override_cgus_and_disable_thinlto(
2189    early_dcx: &EarlyDiagCtxt,
2190    output_types: &OutputTypes,
2191    matches: &getopts::Matches,
2192    mut codegen_units: Option<usize>,
2193) -> (bool, Option<usize>) {
2194    let mut disable_local_thinlto = false;
2195    // Issue #30063: if user requests LLVM-related output to one
2196    // particular path, disable codegen-units.
2197    let incompatible: Vec<_> = output_types
2198        .0
2199        .iter()
2200        .map(|ot_path| ot_path.0)
2201        .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file())
2202        .map(|ot| ot.shorthand())
2203        .collect();
2204    if !incompatible.is_empty() {
2205        match codegen_units {
2206            Some(n) if n > 1 => {
2207                if matches.opt_present("o") {
2208                    for ot in &incompatible {
2209                        early_dcx.early_warn(format!(
2210                            "`--emit={ot}` with `-o` incompatible with \
2211                                 `-C codegen-units=N` for N > 1",
2212                        ));
2213                    }
2214                    early_dcx.early_warn("resetting to default -C codegen-units=1");
2215                    codegen_units = Some(1);
2216                    disable_local_thinlto = true;
2217                }
2218            }
2219            _ => {
2220                codegen_units = Some(1);
2221                disable_local_thinlto = true;
2222            }
2223        }
2224    }
2225
2226    if codegen_units == Some(0) {
2227        early_dcx.early_fatal("value for codegen units must be a positive non-zero integer");
2228    }
2229
2230    (disable_local_thinlto, codegen_units)
2231}
2232
2233pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple {
2234    match matches.opt_str("target") {
2235        Some(target) if target.ends_with(".json") => {
2236            let path = Path::new(&target);
2237            TargetTuple::from_path(path).unwrap_or_else(|_| {
2238                early_dcx.early_fatal(format!("target file {path:?} does not exist"))
2239            })
2240        }
2241        Some(target) => TargetTuple::TargetTuple(target),
2242        _ => TargetTuple::from_tuple(host_tuple()),
2243    }
2244}
2245
2246fn parse_opt_level(
2247    early_dcx: &EarlyDiagCtxt,
2248    matches: &getopts::Matches,
2249    cg: &CodegenOptions,
2250) -> OptLevel {
2251    // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
2252    // to use them interchangeably. However, because they're technically different flags,
2253    // we need to work out manually which should take precedence if both are supplied (i.e.
2254    // the rightmost flag). We do this by finding the (rightmost) position of both flags and
2255    // comparing them. Note that if a flag is not found, its position will be `None`, which
2256    // always compared less than `Some(_)`.
2257    let max_o = matches.opt_positions("O").into_iter().max();
2258    let max_c = matches
2259        .opt_strs_pos("C")
2260        .into_iter()
2261        .flat_map(|(i, s)| {
2262            // NB: This can match a string without `=`.
2263            if let Some("opt-level") = s.split('=').next() { Some(i) } else { None }
2264        })
2265        .max();
2266    if max_o > max_c {
2267        OptLevel::Aggressive
2268    } else {
2269        match cg.opt_level.as_ref() {
2270            "0" => OptLevel::No,
2271            "1" => OptLevel::Less,
2272            "2" => OptLevel::More,
2273            "3" => OptLevel::Aggressive,
2274            "s" => OptLevel::Size,
2275            "z" => OptLevel::SizeMin,
2276            arg => {
2277                early_dcx.early_fatal(format!(
2278                    "optimization level needs to be \
2279                            between 0-3, s or z (instead was `{arg}`)"
2280                ));
2281            }
2282        }
2283    }
2284}
2285
2286fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInfo {
2287    let max_g = matches.opt_positions("g").into_iter().max();
2288    let max_c = matches
2289        .opt_strs_pos("C")
2290        .into_iter()
2291        .flat_map(|(i, s)| {
2292            // NB: This can match a string without `=`.
2293            if let Some("debuginfo") = s.split('=').next() { Some(i) } else { None }
2294        })
2295        .max();
2296    if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
2297}
2298
2299fn parse_assert_incr_state(
2300    early_dcx: &EarlyDiagCtxt,
2301    opt_assertion: &Option<String>,
2302) -> Option<IncrementalStateAssertion> {
2303    match opt_assertion {
2304        Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
2305        Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
2306        Some(s) => {
2307            early_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}"))
2308        }
2309        None => None,
2310    }
2311}
2312
2313pub fn parse_externs(
2314    early_dcx: &EarlyDiagCtxt,
2315    matches: &getopts::Matches,
2316    unstable_opts: &UnstableOptions,
2317) -> Externs {
2318    let is_unstable_enabled = unstable_opts.unstable_options;
2319    let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
2320    for arg in matches.opt_strs("extern") {
2321        let ExternOpt { crate_name: name, path, options } =
2322            split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit());
2323
2324        let entry = externs.entry(name.to_owned());
2325
2326        use std::collections::btree_map::Entry;
2327
2328        let entry = if let Some(path) = path {
2329            // --extern prelude_name=some_file.rlib
2330            let path = CanonicalizedPath::new(path);
2331            match entry {
2332                Entry::Vacant(vacant) => {
2333                    let files = BTreeSet::from_iter(iter::once(path));
2334                    vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files)))
2335                }
2336                Entry::Occupied(occupied) => {
2337                    let ext_ent = occupied.into_mut();
2338                    match ext_ent {
2339                        ExternEntry { location: ExternLocation::ExactPaths(files), .. } => {
2340                            files.insert(path);
2341                        }
2342                        ExternEntry {
2343                            location: location @ ExternLocation::FoundInLibrarySearchDirectories,
2344                            ..
2345                        } => {
2346                            // Exact paths take precedence over search directories.
2347                            let files = BTreeSet::from_iter(iter::once(path));
2348                            *location = ExternLocation::ExactPaths(files);
2349                        }
2350                    }
2351                    ext_ent
2352                }
2353            }
2354        } else {
2355            // --extern prelude_name
2356            match entry {
2357                Entry::Vacant(vacant) => {
2358                    vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
2359                }
2360                Entry::Occupied(occupied) => {
2361                    // Ignore if already specified.
2362                    occupied.into_mut()
2363                }
2364            }
2365        };
2366
2367        let mut is_private_dep = false;
2368        let mut add_prelude = true;
2369        let mut nounused_dep = false;
2370        let mut force = false;
2371        if let Some(opts) = options {
2372            if !is_unstable_enabled {
2373                early_dcx.early_fatal(
2374                    "the `-Z unstable-options` flag must also be passed to \
2375                     enable `--extern` options",
2376                );
2377            }
2378            for opt in opts.split(',') {
2379                match opt {
2380                    "priv" => is_private_dep = true,
2381                    "noprelude" => {
2382                        if let ExternLocation::ExactPaths(_) = &entry.location {
2383                            add_prelude = false;
2384                        } else {
2385                            early_dcx.early_fatal(
2386                                "the `noprelude` --extern option requires a file path",
2387                            );
2388                        }
2389                    }
2390                    "nounused" => nounused_dep = true,
2391                    "force" => force = true,
2392                    _ => early_dcx.early_fatal(format!("unknown --extern option `{opt}`")),
2393                }
2394            }
2395        }
2396
2397        // Crates start out being not private, and go to being private `priv`
2398        // is specified.
2399        entry.is_private_dep |= is_private_dep;
2400        // likewise `nounused`
2401        entry.nounused_dep |= nounused_dep;
2402        // and `force`
2403        entry.force |= force;
2404        // If any flag is missing `noprelude`, then add to the prelude.
2405        entry.add_prelude |= add_prelude;
2406    }
2407    Externs(externs)
2408}
2409
2410fn parse_remap_path_prefix(
2411    early_dcx: &EarlyDiagCtxt,
2412    matches: &getopts::Matches,
2413    unstable_opts: &UnstableOptions,
2414) -> Vec<(PathBuf, PathBuf)> {
2415    let mut mapping: Vec<(PathBuf, PathBuf)> = matches
2416        .opt_strs("remap-path-prefix")
2417        .into_iter()
2418        .map(|remap| match remap.rsplit_once('=') {
2419            None => {
2420                early_dcx.early_fatal("--remap-path-prefix must contain '=' between FROM and TO")
2421            }
2422            Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
2423        })
2424        .collect();
2425    match &unstable_opts.remap_cwd_prefix {
2426        Some(to) => match std::env::current_dir() {
2427            Ok(cwd) => mapping.push((cwd, to.clone())),
2428            Err(_) => (),
2429        },
2430        None => (),
2431    };
2432    mapping
2433}
2434
2435fn parse_logical_env(
2436    early_dcx: &EarlyDiagCtxt,
2437    matches: &getopts::Matches,
2438) -> FxIndexMap<String, String> {
2439    let mut vars = FxIndexMap::default();
2440
2441    for arg in matches.opt_strs("env-set") {
2442        if let Some((name, val)) = arg.split_once('=') {
2443            vars.insert(name.to_string(), val.to_string());
2444        } else {
2445            early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`"));
2446        }
2447    }
2448
2449    vars
2450}
2451
2452// JUSTIFICATION: before wrapper fn is available
2453#[allow(rustc::bad_opt_access)]
2454pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options {
2455    let color = parse_color(early_dcx, matches);
2456
2457    let edition = parse_crate_edition(early_dcx, matches);
2458
2459    let crate_name = matches.opt_str("crate-name");
2460    let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref());
2461    let JsonConfig {
2462        json_rendered,
2463        json_color,
2464        json_artifact_notifications,
2465        json_timings,
2466        json_unused_externs,
2467        json_future_incompat,
2468    } = parse_json(early_dcx, matches, unstable_features.is_nightly_build());
2469
2470    let error_format = parse_error_format(
2471        early_dcx,
2472        matches,
2473        color,
2474        json_color,
2475        json_rendered,
2476        unstable_features.is_nightly_build(),
2477    );
2478
2479    early_dcx.set_error_format(error_format);
2480
2481    let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
2482        early_dcx.early_fatal("`--diagnostic-width` must be an positive integer");
2483    });
2484
2485    let unparsed_crate_types = matches.opt_strs("crate-type");
2486    let crate_types = parse_crate_types_from_list(unparsed_crate_types)
2487        .unwrap_or_else(|e| early_dcx.early_fatal(e));
2488
2489    let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new();
2490
2491    let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
2492    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
2493
2494    if !unstable_opts.unstable_options && json_timings {
2495        early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`");
2496    }
2497
2498    check_error_format_stability(
2499        early_dcx,
2500        &unstable_opts,
2501        unstable_features.is_nightly_build(),
2502        error_format,
2503    );
2504
2505    let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
2506
2507    let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
2508    let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
2509        early_dcx,
2510        &output_types,
2511        matches,
2512        cg.codegen_units,
2513    );
2514
2515    if unstable_opts.threads == 0 {
2516        early_dcx.early_fatal("value for threads must be a positive non-zero integer");
2517    }
2518
2519    if unstable_opts.threads == parse::MAX_THREADS_CAP {
2520        early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP));
2521    }
2522
2523    let incremental = cg.incremental.as_ref().map(PathBuf::from);
2524
2525    let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
2526
2527    if cg.profile_generate.enabled() && cg.profile_use.is_some() {
2528        early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive");
2529    }
2530
2531    if unstable_opts.profile_sample_use.is_some()
2532        && (cg.profile_generate.enabled() || cg.profile_use.is_some())
2533    {
2534        early_dcx.early_fatal(
2535            "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
2536        );
2537    }
2538
2539    // Check for unstable values of `-C symbol-mangling-version`.
2540    // This is what prevents them from being used on stable compilers.
2541    match cg.symbol_mangling_version {
2542        // Stable values:
2543        None | Some(SymbolManglingVersion::V0) => {}
2544
2545        // Unstable values:
2546        Some(SymbolManglingVersion::Legacy) => {
2547            if !unstable_opts.unstable_options {
2548                early_dcx.early_fatal(
2549                    "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
2550                );
2551            }
2552        }
2553        Some(SymbolManglingVersion::Hashed) => {
2554            if !unstable_opts.unstable_options {
2555                early_dcx.early_fatal(
2556                    "`-C symbol-mangling-version=hashed` requires `-Z unstable-options`",
2557                );
2558            }
2559        }
2560    }
2561
2562    if cg.instrument_coverage != InstrumentCoverage::No {
2563        if cg.profile_generate.enabled() || cg.profile_use.is_some() {
2564            early_dcx.early_fatal(
2565                "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
2566                or `-C profile-generate`",
2567            );
2568        }
2569
2570        // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
2571        // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
2572        // multiple runs, including some changes to source code; so mangled names must be consistent
2573        // across compilations.
2574        match cg.symbol_mangling_version {
2575            None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
2576            Some(SymbolManglingVersion::Legacy) => {
2577                early_dcx.early_warn(
2578                    "-C instrument-coverage requires symbol mangling version `v0`, \
2579                    but `-C symbol-mangling-version=legacy` was specified",
2580                );
2581            }
2582            Some(SymbolManglingVersion::V0) => {}
2583            Some(SymbolManglingVersion::Hashed) => {
2584                early_dcx.early_warn(
2585                    "-C instrument-coverage requires symbol mangling version `v0`, \
2586                    but `-C symbol-mangling-version=hashed` was specified",
2587                );
2588            }
2589        }
2590    }
2591
2592    if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
2593        // FIXME: this is only mutation of UnstableOptions here, move into
2594        // UnstableOptions::build?
2595        unstable_opts.graphviz_font = graphviz_font;
2596    }
2597
2598    if !cg.embed_bitcode {
2599        match cg.lto {
2600            LtoCli::No | LtoCli::Unspecified => {}
2601            LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
2602                early_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible")
2603            }
2604        }
2605    }
2606
2607    let unstable_options_enabled = nightly_options::is_unstable_enabled(matches);
2608    if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf {
2609        early_dcx.early_fatal(
2610            "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \
2611                and a nightly compiler",
2612        )
2613    }
2614
2615    if !nightly_options::is_unstable_enabled(matches)
2616        && unstable_opts.offload.contains(&Offload::Enable)
2617    {
2618        early_dcx.early_fatal(
2619            "`-Zoffload=Enable` also requires `-Zunstable-options` \
2620                and a nightly compiler",
2621        )
2622    }
2623
2624    let target_triple = parse_target_triple(early_dcx, matches);
2625
2626    // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and
2627    // `-C linker-flavor` options.
2628    if !unstable_options_enabled {
2629        if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) {
2630            early_dcx.early_fatal(error);
2631        }
2632
2633        if let Some(flavor) = cg.linker_flavor {
2634            if flavor.is_unstable() {
2635                early_dcx.early_fatal(format!(
2636                    "the linker flavor `{}` is unstable, the `-Z unstable-options` \
2637                        flag must also be passed to use the unstable values",
2638                    flavor.desc()
2639                ));
2640            }
2641        }
2642    }
2643
2644    // Check `-C link-self-contained` for consistency: individual components cannot be both enabled
2645    // and disabled at the same time.
2646    if let Some(erroneous_components) = cg.link_self_contained.check_consistency() {
2647        let names: String = erroneous_components
2648            .into_iter()
2649            .map(|c| c.as_str().unwrap())
2650            .intersperse(", ")
2651            .collect();
2652        early_dcx.early_fatal(format!(
2653            "some `-C link-self-contained` components were both enabled and disabled: {names}"
2654        ));
2655    }
2656
2657    let prints = print_request::collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches);
2658
2659    // -Zretpoline-external-thunk also requires -Zretpoline
2660    if unstable_opts.retpoline_external_thunk {
2661        unstable_opts.retpoline = true;
2662        target_modifiers.insert(
2663            OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline),
2664            "true".to_string(),
2665        );
2666    }
2667
2668    let cg = cg;
2669
2670    let opt_level = parse_opt_level(early_dcx, matches, &cg);
2671    // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
2672    // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
2673    // for more details.
2674    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
2675    let debuginfo = select_debuginfo(matches, &cg);
2676    let debuginfo_compression = unstable_opts.debuginfo_compression;
2677
2678    if !unstable_options_enabled {
2679        if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) {
2680            early_dcx.early_fatal(error);
2681        }
2682    }
2683
2684    if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) {
2685        early_dcx.early_fatal(
2686            "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler",
2687        )
2688    }
2689
2690    // Parse any `-l` flags, which link to native libraries.
2691    let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches);
2692
2693    let test = matches.opt_present("test");
2694
2695    if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
2696        early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
2697    }
2698
2699    if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() {
2700        early_dcx
2701            .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
2702    }
2703
2704    let externs = parse_externs(early_dcx, matches, &unstable_opts);
2705
2706    let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
2707
2708    let pretty = parse_pretty(early_dcx, &unstable_opts);
2709
2710    // query-dep-graph is required if dump-dep-graph is given #106736
2711    if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
2712        early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`");
2713    }
2714
2715    let logical_env = parse_logical_env(early_dcx, matches);
2716
2717    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
2718
2719    let real_source_base_dir = |suffix: &str, confirm: &str| {
2720        let mut candidate = sysroot.path().join(suffix);
2721        if let Ok(metadata) = candidate.symlink_metadata() {
2722            // Replace the symlink bootstrap creates, with its destination.
2723            // We could try to use `fs::canonicalize` instead, but that might
2724            // produce unnecessarily verbose path.
2725            if metadata.file_type().is_symlink() {
2726                if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
2727                    candidate = symlink_dest;
2728                }
2729            }
2730        }
2731
2732        // Only use this directory if it has a file we can expect to always find.
2733        candidate.join(confirm).is_file().then_some(candidate)
2734    };
2735
2736    let real_rust_source_base_dir =
2737        // This is the location used by the `rust-src` `rustup` component.
2738        real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs");
2739
2740    let real_rustc_dev_source_base_dir =
2741        // This is the location used by the `rustc-dev` `rustup` component.
2742        real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs");
2743
2744    // We eagerly scan all files in each passed -L path. If the same directory is passed multiple
2745    // times, and the directory contains a lot of files, this can take a lot of time.
2746    // So we remove -L paths that were passed multiple times, and keep only the first occurrence.
2747    // We still have to keep the original order of the -L arguments.
2748    let search_paths: Vec<SearchPath> = {
2749        let mut seen_search_paths = FxHashSet::default();
2750        let search_path_matches: Vec<String> = matches.opt_strs("L");
2751        search_path_matches
2752            .iter()
2753            .filter(|p| seen_search_paths.insert(*p))
2754            .map(|path| {
2755                SearchPath::from_cli_opt(
2756                    sysroot.path(),
2757                    &target_triple,
2758                    early_dcx,
2759                    &path,
2760                    unstable_opts.unstable_options,
2761                )
2762            })
2763            .collect()
2764    };
2765
2766    // Ideally we would use `SourceMap::working_dir` instead, but we don't have access to it
2767    // so we manually create the potentially-remapped working directory
2768    let working_dir = {
2769        let working_dir = std::env::current_dir().unwrap_or_else(|e| {
2770            early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
2771        });
2772
2773        let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
2774        file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
2775    };
2776
2777    let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals;
2778
2779    Options {
2780        assert_incr_state,
2781        crate_types,
2782        optimize: opt_level,
2783        debuginfo,
2784        debuginfo_compression,
2785        lint_opts,
2786        lint_cap,
2787        describe_lints,
2788        output_types,
2789        search_paths,
2790        sysroot,
2791        target_triple,
2792        test,
2793        incremental,
2794        untracked_state_hash: Default::default(),
2795        unstable_opts,
2796        prints,
2797        cg,
2798        error_format,
2799        diagnostic_width,
2800        externs,
2801        unstable_features,
2802        crate_name,
2803        libs,
2804        debug_assertions,
2805        actually_rustdoc: false,
2806        resolve_doc_links: ResolveDocLinks::ExportedMetadata,
2807        trimmed_def_paths: false,
2808        cli_forced_codegen_units: codegen_units,
2809        cli_forced_local_thinlto_off: disable_local_thinlto,
2810        remap_path_prefix,
2811        real_rust_source_base_dir,
2812        real_rustc_dev_source_base_dir,
2813        edition,
2814        json_artifact_notifications,
2815        json_timings,
2816        json_unused_externs,
2817        json_future_incompat,
2818        pretty,
2819        working_dir,
2820        color,
2821        logical_env,
2822        verbose,
2823        target_modifiers,
2824    }
2825}
2826
2827fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> Option<PpMode> {
2828    use PpMode::*;
2829
2830    let first = match unstable_opts.unpretty.as_deref()? {
2831        "normal" => Source(PpSourceMode::Normal),
2832        "identified" => Source(PpSourceMode::Identified),
2833        "expanded" => Source(PpSourceMode::Expanded),
2834        "expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
2835        "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
2836        "ast-tree" => AstTree,
2837        "ast-tree,expanded" => AstTreeExpanded,
2838        "hir" => Hir(PpHirMode::Normal),
2839        "hir,identified" => Hir(PpHirMode::Identified),
2840        "hir,typed" => Hir(PpHirMode::Typed),
2841        "hir-tree" => HirTree,
2842        "thir-tree" => ThirTree,
2843        "thir-flat" => ThirFlat,
2844        "mir" => Mir,
2845        "stable-mir" => StableMir,
2846        "mir-cfg" => MirCFG,
2847        name => early_dcx.early_fatal(format!(
2848            "argument to `unpretty` must be one of `normal`, `identified`, \
2849                            `expanded`, `expanded,identified`, `expanded,hygiene`, \
2850                            `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
2851                            `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \
2852                            `mir-cfg`; got {name}"
2853        )),
2854    };
2855    debug!("got unpretty option: {first:?}");
2856    Some(first)
2857}
2858
2859pub fn make_crate_type_option() -> RustcOptGroup {
2860    make_opt(
2861        OptionStability::Stable,
2862        OptionKind::Multi,
2863        "",
2864        "crate-type",
2865        "Comma separated list of types of crates
2866                                for the compiler to emit",
2867        "<bin|lib|rlib|dylib|cdylib|staticlib|proc-macro>",
2868    )
2869}
2870
2871pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
2872    let mut crate_types: Vec<CrateType> = Vec::new();
2873    for unparsed_crate_type in &list_list {
2874        for part in unparsed_crate_type.split(',') {
2875            let new_part = match part {
2876                "lib" => default_lib_output(),
2877                "rlib" => CrateType::Rlib,
2878                "staticlib" => CrateType::Staticlib,
2879                "dylib" => CrateType::Dylib,
2880                "cdylib" => CrateType::Cdylib,
2881                "bin" => CrateType::Executable,
2882                "proc-macro" => CrateType::ProcMacro,
2883                "sdylib" => CrateType::Sdylib,
2884                _ => {
2885                    return Err(format!(
2886                        "unknown crate type: `{part}`, expected one of: \
2887                        `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`",
2888                    ));
2889                }
2890            };
2891            if !crate_types.contains(&new_part) {
2892                crate_types.push(new_part)
2893            }
2894        }
2895    }
2896
2897    Ok(crate_types)
2898}
2899
2900pub mod nightly_options {
2901    use rustc_feature::UnstableFeatures;
2902
2903    use super::{OptionStability, RustcOptGroup};
2904    use crate::EarlyDiagCtxt;
2905
2906    pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
2907        match_is_nightly_build(matches)
2908            && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
2909    }
2910
2911    pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool {
2912        is_nightly_build(matches.opt_str("crate-name").as_deref())
2913    }
2914
2915    fn is_nightly_build(krate: Option<&str>) -> bool {
2916        UnstableFeatures::from_environment(krate).is_nightly_build()
2917    }
2918
2919    pub fn check_nightly_options(
2920        early_dcx: &EarlyDiagCtxt,
2921        matches: &getopts::Matches,
2922        flags: &[RustcOptGroup],
2923    ) {
2924        let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
2925        let really_allows_unstable_options = match_is_nightly_build(matches);
2926        let mut nightly_options_on_stable = 0;
2927
2928        for opt in flags.iter() {
2929            if opt.stability == OptionStability::Stable {
2930                continue;
2931            }
2932            if !matches.opt_present(opt.name) {
2933                continue;
2934            }
2935            if opt.name != "Z" && !has_z_unstable_option {
2936                early_dcx.early_fatal(format!(
2937                    "the `-Z unstable-options` flag must also be passed to enable \
2938                         the flag `{}`",
2939                    opt.name
2940                ));
2941            }
2942            if really_allows_unstable_options {
2943                continue;
2944            }
2945            match opt.stability {
2946                OptionStability::Unstable => {
2947                    nightly_options_on_stable += 1;
2948                    let msg = format!(
2949                        "the option `{}` is only accepted on the nightly compiler",
2950                        opt.name
2951                    );
2952                    // The non-zero nightly_options_on_stable will force an early_fatal eventually.
2953                    let _ = early_dcx.early_err(msg);
2954                }
2955                OptionStability::Stable => {}
2956            }
2957        }
2958        if nightly_options_on_stable > 0 {
2959            early_dcx
2960                .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
2961            early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
2962            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>");
2963            early_dcx.early_fatal(format!(
2964                "{} nightly option{} were parsed",
2965                nightly_options_on_stable,
2966                if nightly_options_on_stable > 1 { "s" } else { "" }
2967            ));
2968        }
2969    }
2970}
2971
2972impl fmt::Display for CrateType {
2973    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2974        match *self {
2975            CrateType::Executable => "bin".fmt(f),
2976            CrateType::Dylib => "dylib".fmt(f),
2977            CrateType::Rlib => "rlib".fmt(f),
2978            CrateType::Staticlib => "staticlib".fmt(f),
2979            CrateType::Cdylib => "cdylib".fmt(f),
2980            CrateType::ProcMacro => "proc-macro".fmt(f),
2981            CrateType::Sdylib => "sdylib".fmt(f),
2982        }
2983    }
2984}
2985
2986impl IntoDiagArg for CrateType {
2987    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
2988        self.to_string().into_diag_arg(&mut None)
2989    }
2990}
2991
2992#[derive(Copy, Clone, PartialEq, Debug)]
2993pub enum PpSourceMode {
2994    /// `-Zunpretty=normal`
2995    Normal,
2996    /// `-Zunpretty=expanded`
2997    Expanded,
2998    /// `-Zunpretty=identified`
2999    Identified,
3000    /// `-Zunpretty=expanded,identified`
3001    ExpandedIdentified,
3002    /// `-Zunpretty=expanded,hygiene`
3003    ExpandedHygiene,
3004}
3005
3006#[derive(Copy, Clone, PartialEq, Debug)]
3007pub enum PpHirMode {
3008    /// `-Zunpretty=hir`
3009    Normal,
3010    /// `-Zunpretty=hir,identified`
3011    Identified,
3012    /// `-Zunpretty=hir,typed`
3013    Typed,
3014}
3015
3016#[derive(Copy, Clone, PartialEq, Debug)]
3017/// Pretty print mode
3018pub enum PpMode {
3019    /// Options that print the source code, i.e.
3020    /// `-Zunpretty=normal` and `-Zunpretty=expanded`
3021    Source(PpSourceMode),
3022    /// `-Zunpretty=ast-tree`
3023    AstTree,
3024    /// `-Zunpretty=ast-tree,expanded`
3025    AstTreeExpanded,
3026    /// Options that print the HIR, i.e. `-Zunpretty=hir`
3027    Hir(PpHirMode),
3028    /// `-Zunpretty=hir-tree`
3029    HirTree,
3030    /// `-Zunpretty=thir-tree`
3031    ThirTree,
3032    /// `-Zunpretty=thir-flat`
3033    ThirFlat,
3034    /// `-Zunpretty=mir`
3035    Mir,
3036    /// `-Zunpretty=mir-cfg`
3037    MirCFG,
3038    /// `-Zunpretty=stable-mir`
3039    StableMir,
3040}
3041
3042impl PpMode {
3043    pub fn needs_ast_map(&self) -> bool {
3044        use PpMode::*;
3045        use PpSourceMode::*;
3046        match *self {
3047            Source(Normal | Identified) | AstTree => false,
3048
3049            Source(Expanded | ExpandedIdentified | ExpandedHygiene)
3050            | AstTreeExpanded
3051            | Hir(_)
3052            | HirTree
3053            | ThirTree
3054            | ThirFlat
3055            | Mir
3056            | MirCFG
3057            | StableMir => true,
3058        }
3059    }
3060
3061    pub fn needs_analysis(&self) -> bool {
3062        use PpMode::*;
3063        matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat)
3064    }
3065}
3066
3067#[derive(Clone, Hash, PartialEq, Eq, Debug)]
3068pub enum WasiExecModel {
3069    Command,
3070    Reactor,
3071}
3072
3073/// Command-line arguments passed to the compiler have to be incorporated with
3074/// the dependency tracking system for incremental compilation. This module
3075/// provides some utilities to make this more convenient.
3076///
3077/// The values of all command-line arguments that are relevant for dependency
3078/// tracking are hashed into a single value that determines whether the
3079/// incremental compilation cache can be re-used or not. This hashing is done
3080/// via the `DepTrackingHash` trait defined below, since the standard `Hash`
3081/// implementation might not be suitable (e.g., arguments are stored in a `Vec`,
3082/// the hash of which is order dependent, but we might not want the order of
3083/// arguments to make a difference for the hash).
3084///
3085/// However, since the value provided by `Hash::hash` often *is* suitable,
3086/// especially for primitive types, there is the
3087/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the
3088/// `Hash` implementation for `DepTrackingHash`. It's important though that
3089/// we have an opt-in scheme here, so one is hopefully forced to think about
3090/// how the hash should be calculated when adding a new command-line argument.
3091pub(crate) mod dep_tracking {
3092    use std::collections::BTreeMap;
3093    use std::hash::Hash;
3094    use std::num::NonZero;
3095    use std::path::PathBuf;
3096
3097    use rustc_abi::Align;
3098    use rustc_data_structures::fx::FxIndexMap;
3099    use rustc_data_structures::stable_hasher::StableHasher;
3100    use rustc_errors::LanguageIdentifier;
3101    use rustc_feature::UnstableFeatures;
3102    use rustc_hashes::Hash64;
3103    use rustc_span::edition::Edition;
3104    use rustc_span::{RealFileName, RemapPathScopeComponents};
3105    use rustc_target::spec::{
3106        CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
3107        RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple,
3108        TlsModel,
3109    };
3110
3111    use super::{
3112        AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo,
3113        CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug,
3114        FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto,
3115        LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel,
3116        OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks,
3117        SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
3118        WasiExecModel,
3119    };
3120    use crate::lint;
3121    use crate::utils::NativeLib;
3122
3123    pub(crate) trait DepTrackingHash {
3124        fn hash(
3125            &self,
3126            hasher: &mut StableHasher,
3127            error_format: ErrorOutputType,
3128            for_crate_hash: bool,
3129        );
3130    }
3131
3132    macro_rules! impl_dep_tracking_hash_via_hash {
3133        ($($t:ty),+ $(,)?) => {$(
3134            impl DepTrackingHash for $t {
3135                fn hash(&self, hasher: &mut StableHasher, _: ErrorOutputType, _for_crate_hash: bool) {
3136                    Hash::hash(self, hasher);
3137                }
3138            }
3139        )+};
3140    }
3141
3142    impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
3143        fn hash(
3144            &self,
3145            hasher: &mut StableHasher,
3146            error_format: ErrorOutputType,
3147            for_crate_hash: bool,
3148        ) {
3149            match self {
3150                Some(x) => {
3151                    Hash::hash(&1, hasher);
3152                    DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
3153                }
3154                None => Hash::hash(&0, hasher),
3155            }
3156        }
3157    }
3158
3159    impl_dep_tracking_hash_via_hash!(
3160        (),
3161        AnnotateMoves,
3162        AutoDiff,
3163        Offload,
3164        bool,
3165        usize,
3166        NonZero<usize>,
3167        u64,
3168        Hash64,
3169        String,
3170        PathBuf,
3171        lint::Level,
3172        WasiExecModel,
3173        u32,
3174        FramePointer,
3175        RelocModel,
3176        CodeModel,
3177        TlsModel,
3178        InstrumentCoverage,
3179        CoverageOptions,
3180        InstrumentXRay,
3181        CrateType,
3182        MergeFunctions,
3183        OnBrokenPipe,
3184        PanicStrategy,
3185        RelroLevel,
3186        OptLevel,
3187        LtoCli,
3188        DebugInfo,
3189        DebugInfoCompression,
3190        MirStripDebugInfo,
3191        CollapseMacroDebuginfo,
3192        UnstableFeatures,
3193        NativeLib,
3194        SanitizerSet,
3195        CFGuard,
3196        CFProtection,
3197        TargetTuple,
3198        Edition,
3199        LinkerPluginLto,
3200        ResolveDocLinks,
3201        SplitDebuginfo,
3202        SplitDwarfKind,
3203        StackProtector,
3204        SwitchWithOptPath,
3205        SymbolManglingVersion,
3206        SymbolVisibility,
3207        RemapPathScopeComponents,
3208        SourceFileHashAlgorithm,
3209        OutFileName,
3210        OutputType,
3211        RealFileName,
3212        LocationDetail,
3213        FmtDebug,
3214        BranchProtection,
3215        LanguageIdentifier,
3216        NextSolverConfig,
3217        PatchableFunctionEntry,
3218        Polonius,
3219        InliningThreshold,
3220        FunctionReturn,
3221        Align,
3222    );
3223
3224    impl<T1, T2> DepTrackingHash for (T1, T2)
3225    where
3226        T1: DepTrackingHash,
3227        T2: DepTrackingHash,
3228    {
3229        fn hash(
3230            &self,
3231            hasher: &mut StableHasher,
3232            error_format: ErrorOutputType,
3233            for_crate_hash: bool,
3234        ) {
3235            Hash::hash(&0, hasher);
3236            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3237            Hash::hash(&1, hasher);
3238            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3239        }
3240    }
3241
3242    impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
3243    where
3244        T1: DepTrackingHash,
3245        T2: DepTrackingHash,
3246        T3: DepTrackingHash,
3247    {
3248        fn hash(
3249            &self,
3250            hasher: &mut StableHasher,
3251            error_format: ErrorOutputType,
3252            for_crate_hash: bool,
3253        ) {
3254            Hash::hash(&0, hasher);
3255            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3256            Hash::hash(&1, hasher);
3257            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3258            Hash::hash(&2, hasher);
3259            DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
3260        }
3261    }
3262
3263    impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
3264        fn hash(
3265            &self,
3266            hasher: &mut StableHasher,
3267            error_format: ErrorOutputType,
3268            for_crate_hash: bool,
3269        ) {
3270            Hash::hash(&self.len(), hasher);
3271            for (index, elem) in self.iter().enumerate() {
3272                Hash::hash(&index, hasher);
3273                DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
3274            }
3275        }
3276    }
3277
3278    impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
3279        fn hash(
3280            &self,
3281            hasher: &mut StableHasher,
3282            error_format: ErrorOutputType,
3283            for_crate_hash: bool,
3284        ) {
3285            Hash::hash(&self.len(), hasher);
3286            for (key, value) in self.iter() {
3287                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3288                DepTrackingHash::hash(value, hasher, error_format, for_crate_hash);
3289            }
3290        }
3291    }
3292
3293    impl DepTrackingHash for OutputTypes {
3294        fn hash(
3295            &self,
3296            hasher: &mut StableHasher,
3297            error_format: ErrorOutputType,
3298            for_crate_hash: bool,
3299        ) {
3300            Hash::hash(&self.0.len(), hasher);
3301            for (key, val) in &self.0 {
3302                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3303                if !for_crate_hash {
3304                    DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
3305                }
3306            }
3307        }
3308    }
3309
3310    // This is a stable hash because BTreeMap is a sorted container
3311    pub(crate) fn stable_hash(
3312        sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
3313        hasher: &mut StableHasher,
3314        error_format: ErrorOutputType,
3315        for_crate_hash: bool,
3316    ) {
3317        for (key, sub_hash) in sub_hashes {
3318            // Using Hash::hash() instead of DepTrackingHash::hash() is fine for
3319            // the keys, as they are just plain strings
3320            Hash::hash(&key.len(), hasher);
3321            Hash::hash(key, hasher);
3322            sub_hash.hash(hasher, error_format, for_crate_hash);
3323        }
3324    }
3325}
3326
3327/// How to run proc-macro code when building this crate
3328#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3329pub enum ProcMacroExecutionStrategy {
3330    /// Run the proc-macro code on the same thread as the server.
3331    SameThread,
3332
3333    /// Run the proc-macro code on a different thread.
3334    CrossThread,
3335}
3336
3337/// How to perform collapse macros debug info
3338/// if-ext - if macro from different crate (related to callsite code)
3339/// | cmd \ attr    | no  | (unspecified) | external | yes |
3340/// | no            | no  | no            | no       | no  |
3341/// | (unspecified) | no  | no            | if-ext   | yes |
3342/// | external      | no  | if-ext        | if-ext   | yes |
3343/// | yes           | yes | yes           | yes      | yes |
3344#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3345pub enum CollapseMacroDebuginfo {
3346    /// Don't collapse debuginfo for the macro
3347    No = 0,
3348    /// Unspecified value
3349    Unspecified = 1,
3350    /// Collapse debuginfo if the macro comes from a different crate
3351    External = 2,
3352    /// Collapse debuginfo for the macro
3353    Yes = 3,
3354}
3355
3356/// Which format to use for `-Z dump-mono-stats`
3357#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3358pub enum DumpMonoStatsFormat {
3359    /// Pretty-print a markdown table
3360    Markdown,
3361    /// Emit structured JSON
3362    Json,
3363}
3364
3365impl DumpMonoStatsFormat {
3366    pub fn extension(self) -> &'static str {
3367        match self {
3368            Self::Markdown => "md",
3369            Self::Json => "json",
3370        }
3371    }
3372}
3373
3374/// `-Z patchable-function-entry` representation - how many nops to put before and after function
3375/// entry.
3376#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3377pub struct PatchableFunctionEntry {
3378    /// Nops before the entry
3379    prefix: u8,
3380    /// Nops after the entry
3381    entry: u8,
3382}
3383
3384impl PatchableFunctionEntry {
3385    pub fn from_total_and_prefix_nops(
3386        total_nops: u8,
3387        prefix_nops: u8,
3388    ) -> Option<PatchableFunctionEntry> {
3389        if total_nops < prefix_nops {
3390            None
3391        } else {
3392            Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3393        }
3394    }
3395    pub fn prefix(&self) -> u8 {
3396        self.prefix
3397    }
3398    pub fn entry(&self) -> u8 {
3399        self.entry
3400    }
3401}
3402
3403/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
3404/// or future prototype.
3405#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3406pub enum Polonius {
3407    /// The default value: disabled.
3408    #[default]
3409    Off,
3410
3411    /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
3412    Legacy,
3413
3414    /// In-tree prototype, extending the NLL infrastructure.
3415    Next,
3416}
3417
3418impl Polonius {
3419    /// Returns whether the legacy version of polonius is enabled
3420    pub fn is_legacy_enabled(&self) -> bool {
3421        matches!(self, Polonius::Legacy)
3422    }
3423
3424    /// Returns whether the "next" version of polonius is enabled
3425    pub fn is_next_enabled(&self) -> bool {
3426        matches!(self, Polonius::Next)
3427    }
3428}
3429
3430#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3431pub enum InliningThreshold {
3432    Always,
3433    Sometimes(usize),
3434    Never,
3435}
3436
3437impl Default for InliningThreshold {
3438    fn default() -> Self {
3439        Self::Sometimes(100)
3440    }
3441}
3442
3443/// The different settings that the `-Zfunction-return` flag can have.
3444#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3445pub enum FunctionReturn {
3446    /// Keep the function return unmodified.
3447    #[default]
3448    Keep,
3449
3450    /// Replace returns with jumps to thunk, without emitting the thunk.
3451    ThunkExtern,
3452}
3453
3454/// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag.
3455/// By default, only enabled in the NLL MIR dumps, and disabled in all other passes.
3456#[derive(Clone, Copy, Default, PartialEq, Debug)]
3457pub enum MirIncludeSpans {
3458    Off,
3459    On,
3460    /// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as
3461    /// `Off` in all other cases.
3462    #[default]
3463    Nll,
3464}
3465
3466impl MirIncludeSpans {
3467    /// Unless opting into extra comments for all passes, they can be considered disabled.
3468    /// The cases where a distinction between on/off and a per-pass value can exist will be handled
3469    /// in the passes themselves: i.e. the `Nll` value is considered off for all intents and
3470    /// purposes, except for the NLL MIR dump pass.
3471    pub fn is_enabled(self) -> bool {
3472        self == MirIncludeSpans::On
3473    }
3474}