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