rustc_session/
config.rs

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