rustc_session/
config.rs

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