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