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, 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 stripped_len = filename.len() - MAX_FILENAME_LENGTH + hash_len;
1215
1216        let mut hasher = StableHasher::new();
1217        filename[..stripped_len].hash(&mut hasher);
1218        let hash = hasher.finish::<Hash64>();
1219
1220        path.set_file_name(format!("{:x}-{}", hash, &filename[stripped_len..]));
1221    }
1222    path
1223}
1224impl OutputFilenames {
1225    pub fn new(
1226        out_directory: PathBuf,
1227        out_crate_name: String,
1228        out_filestem: String,
1229        single_output_file: Option<OutFileName>,
1230        temps_directory: Option<PathBuf>,
1231        explicit_dwo_out_directory: Option<PathBuf>,
1232        extra: String,
1233        outputs: OutputTypes,
1234    ) -> Self {
1235        OutputFilenames {
1236            out_directory,
1237            single_output_file,
1238            temps_directory,
1239            explicit_dwo_out_directory,
1240            outputs,
1241            crate_stem: format!("{out_crate_name}{extra}"),
1242            filestem: format!("{out_filestem}{extra}"),
1243        }
1244    }
1245
1246    pub fn path(&self, flavor: OutputType) -> OutFileName {
1247        self.outputs
1248            .get(&flavor)
1249            .and_then(|p| p.to_owned())
1250            .or_else(|| self.single_output_file.clone())
1251            .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor)))
1252    }
1253
1254    pub fn interface_path(&self) -> PathBuf {
1255        self.out_directory.join(format!("lib{}.rs", self.crate_stem))
1256    }
1257
1258    /// Gets the output path where a compilation artifact of the given type
1259    /// should be placed on disk.
1260    fn output_path(&self, flavor: OutputType) -> PathBuf {
1261        let extension = flavor.extension();
1262        match flavor {
1263            OutputType::Metadata => {
1264                self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension))
1265            }
1266            _ => self.with_directory_and_extension(&self.out_directory, extension),
1267        }
1268    }
1269
1270    /// Gets the path where a compilation artifact of the given type for the
1271    /// given codegen unit should be placed on disk. If codegen_unit_name is
1272    /// None, a path distinct from those of any codegen unit will be generated.
1273    pub fn temp_path_for_cgu(
1274        &self,
1275        flavor: OutputType,
1276        codegen_unit_name: &str,
1277        invocation_temp: Option<&str>,
1278    ) -> PathBuf {
1279        let extension = flavor.extension();
1280        self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp)
1281    }
1282
1283    /// Like `temp_path`, but specifically for dwarf objects.
1284    pub fn temp_path_dwo_for_cgu(
1285        &self,
1286        codegen_unit_name: &str,
1287        invocation_temp: Option<&str>,
1288    ) -> PathBuf {
1289        let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp);
1290        if let Some(dwo_out) = &self.explicit_dwo_out_directory {
1291            let mut o = dwo_out.clone();
1292            o.push(p.file_name().unwrap());
1293            o
1294        } else {
1295            p
1296        }
1297    }
1298
1299    /// Like `temp_path`, but also supports things where there is no corresponding
1300    /// OutputType, like noopt-bitcode or lto-bitcode.
1301    pub fn temp_path_ext_for_cgu(
1302        &self,
1303        ext: &str,
1304        codegen_unit_name: &str,
1305        invocation_temp: Option<&str>,
1306    ) -> PathBuf {
1307        let mut extension = codegen_unit_name.to_string();
1308
1309        // Append `.{invocation_temp}` to ensure temporary files are unique.
1310        if let Some(rng) = invocation_temp {
1311            extension.push('.');
1312            extension.push_str(rng);
1313        }
1314
1315        // FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty.
1316        // Append `.rcgu.{ext}`.
1317        if !ext.is_empty() {
1318            extension.push('.');
1319            extension.push_str(RUST_CGU_EXT);
1320            extension.push('.');
1321            extension.push_str(ext);
1322        }
1323
1324        let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
1325        maybe_strip_file_name(self.with_directory_and_extension(temps_directory, &extension))
1326    }
1327
1328    pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf {
1329        let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
1330        self.with_directory_and_extension(temps_directory, &ext)
1331    }
1332
1333    pub fn with_extension(&self, extension: &str) -> PathBuf {
1334        self.with_directory_and_extension(&self.out_directory, extension)
1335    }
1336
1337    pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf {
1338        let mut path = directory.join(&self.filestem);
1339        path.set_extension(extension);
1340        path
1341    }
1342
1343    /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF
1344    /// mode is being used, which is the logic that this function is intended to encapsulate.
1345    pub fn split_dwarf_path(
1346        &self,
1347        split_debuginfo_kind: SplitDebuginfo,
1348        split_dwarf_kind: SplitDwarfKind,
1349        cgu_name: &str,
1350        invocation_temp: Option<&str>,
1351    ) -> Option<PathBuf> {
1352        let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp);
1353        let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp);
1354        match (split_debuginfo_kind, split_dwarf_kind) {
1355            (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
1356            // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
1357            // (pointing at the path which is being determined here). Use the path to the current
1358            // object file.
1359            (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
1360                Some(obj_out)
1361            }
1362            // Split mode emits the DWARF into a different file, use that path.
1363            (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
1364                Some(dwo_out)
1365            }
1366        }
1367    }
1368}
1369
1370bitflags::bitflags! {
1371    /// Scopes used to determined if it need to apply to --remap-path-prefix
1372    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
1373    pub struct RemapPathScopeComponents: u8 {
1374        /// Apply remappings to the expansion of std::file!() macro
1375        const MACRO = 1 << 0;
1376        /// Apply remappings to printed compiler diagnostics
1377        const DIAGNOSTICS = 1 << 1;
1378        /// Apply remappings to debug information
1379        const DEBUGINFO = 1 << 3;
1380
1381        /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled
1382        /// executables or libraries are remapped but not elsewhere.
1383        const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits();
1384    }
1385}
1386
1387#[derive(Clone, Debug)]
1388pub struct Sysroot {
1389    pub explicit: Option<PathBuf>,
1390    pub default: PathBuf,
1391}
1392
1393impl Sysroot {
1394    pub fn new(explicit: Option<PathBuf>) -> Sysroot {
1395        Sysroot { explicit, default: filesearch::default_sysroot() }
1396    }
1397
1398    /// Return explicit sysroot if it was passed with `--sysroot`, or default sysroot otherwise.
1399    pub fn path(&self) -> &Path {
1400        self.explicit.as_deref().unwrap_or(&self.default)
1401    }
1402
1403    /// Returns both explicit sysroot if it was passed with `--sysroot` and the default sysroot.
1404    pub fn all_paths(&self) -> impl Iterator<Item = &Path> {
1405        self.explicit.as_deref().into_iter().chain(iter::once(&*self.default))
1406    }
1407}
1408
1409pub fn host_tuple() -> &'static str {
1410    // Get the host triple out of the build environment. This ensures that our
1411    // idea of the host triple is the same as for the set of libraries we've
1412    // actually built. We can't just take LLVM's host triple because they
1413    // normalize all ix86 architectures to i386.
1414    //
1415    // Instead of grabbing the host triple (for the current host), we grab (at
1416    // compile time) the target triple that this rustc is built with and
1417    // calling that (at runtime) the host triple.
1418    (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
1419}
1420
1421fn file_path_mapping(
1422    remap_path_prefix: Vec<(PathBuf, PathBuf)>,
1423    unstable_opts: &UnstableOptions,
1424) -> FilePathMapping {
1425    FilePathMapping::new(
1426        remap_path_prefix.clone(),
1427        if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS)
1428            && !remap_path_prefix.is_empty()
1429        {
1430            FileNameDisplayPreference::Remapped
1431        } else {
1432            FileNameDisplayPreference::Local
1433        },
1434        if unstable_opts.remap_path_scope.is_all() {
1435            FileNameEmbeddablePreference::RemappedOnly
1436        } else {
1437            FileNameEmbeddablePreference::LocalAndRemapped
1438        },
1439    )
1440}
1441
1442impl Default for Options {
1443    fn default() -> Options {
1444        Options {
1445            assert_incr_state: None,
1446            crate_types: Vec::new(),
1447            optimize: OptLevel::No,
1448            debuginfo: DebugInfo::None,
1449            debuginfo_compression: DebugInfoCompression::None,
1450            lint_opts: Vec::new(),
1451            lint_cap: None,
1452            describe_lints: false,
1453            output_types: OutputTypes(BTreeMap::new()),
1454            search_paths: vec![],
1455            sysroot: Sysroot::new(None),
1456            target_triple: TargetTuple::from_tuple(host_tuple()),
1457            test: false,
1458            incremental: None,
1459            untracked_state_hash: Default::default(),
1460            unstable_opts: Default::default(),
1461            prints: Vec::new(),
1462            cg: Default::default(),
1463            error_format: ErrorOutputType::default(),
1464            diagnostic_width: None,
1465            externs: Externs(BTreeMap::new()),
1466            crate_name: None,
1467            libs: Vec::new(),
1468            unstable_features: UnstableFeatures::Disallow,
1469            debug_assertions: true,
1470            actually_rustdoc: false,
1471            resolve_doc_links: ResolveDocLinks::None,
1472            trimmed_def_paths: false,
1473            cli_forced_codegen_units: None,
1474            cli_forced_local_thinlto_off: false,
1475            remap_path_prefix: Vec::new(),
1476            real_rust_source_base_dir: None,
1477            real_rustc_dev_source_base_dir: None,
1478            edition: DEFAULT_EDITION,
1479            json_artifact_notifications: false,
1480            json_timings: false,
1481            json_unused_externs: JsonUnusedExterns::No,
1482            json_future_incompat: false,
1483            pretty: None,
1484            working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
1485            color: ColorConfig::Auto,
1486            logical_env: FxIndexMap::default(),
1487            verbose: false,
1488            target_modifiers: BTreeMap::default(),
1489        }
1490    }
1491}
1492
1493impl Options {
1494    /// Returns `true` if there is a reason to build the dep graph.
1495    pub fn build_dep_graph(&self) -> bool {
1496        self.incremental.is_some()
1497            || self.unstable_opts.dump_dep_graph
1498            || self.unstable_opts.query_dep_graph
1499    }
1500
1501    pub fn file_path_mapping(&self) -> FilePathMapping {
1502        file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts)
1503    }
1504
1505    /// Returns `true` if there will be an output file generated.
1506    pub fn will_create_output_file(&self) -> bool {
1507        !self.unstable_opts.parse_crate_root_only && // The file is just being parsed
1508            self.unstable_opts.ls.is_empty() // The file is just being queried
1509    }
1510
1511    #[inline]
1512    pub fn share_generics(&self) -> bool {
1513        match self.unstable_opts.share_generics {
1514            Some(setting) => setting,
1515            None => match self.optimize {
1516                OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
1517                OptLevel::More | OptLevel::Aggressive => false,
1518            },
1519        }
1520    }
1521
1522    pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
1523        self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
1524    }
1525
1526    #[inline]
1527    pub fn autodiff_enabled(&self) -> bool {
1528        self.unstable_opts.autodiff.contains(&AutoDiff::Enable)
1529    }
1530}
1531
1532impl UnstableOptions {
1533    pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags {
1534        DiagCtxtFlags {
1535            can_emit_warnings,
1536            treat_err_as_bug: self.treat_err_as_bug,
1537            eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs,
1538            macro_backtrace: self.macro_backtrace,
1539            deduplicate_diagnostics: self.deduplicate_diagnostics,
1540            track_diagnostics: self.track_diagnostics,
1541        }
1542    }
1543
1544    pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm {
1545        self.src_hash_algorithm.unwrap_or_else(|| {
1546            if target.is_like_msvc {
1547                SourceFileHashAlgorithm::Sha256
1548            } else {
1549                SourceFileHashAlgorithm::Md5
1550            }
1551        })
1552    }
1553
1554    pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
1555        self.checksum_hash_algorithm
1556    }
1557}
1558
1559// The type of entry function, so users can have their own entry functions
1560#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
1561pub enum EntryFnType {
1562    Main {
1563        /// Specifies what to do with `SIGPIPE` before calling `fn main()`.
1564        ///
1565        /// What values that are valid and what they mean must be in sync
1566        /// across rustc and libstd, but we don't want it public in libstd,
1567        /// so we take a bit of an unusual approach with simple constants
1568        /// and an `include!()`.
1569        sigpipe: u8,
1570    },
1571}
1572
1573#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
1574#[derive(HashStable_Generic)]
1575pub enum CrateType {
1576    Executable,
1577    Dylib,
1578    Rlib,
1579    Staticlib,
1580    Cdylib,
1581    ProcMacro,
1582    Sdylib,
1583}
1584
1585impl CrateType {
1586    pub fn has_metadata(self) -> bool {
1587        match self {
1588            CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
1589            CrateType::Executable
1590            | CrateType::Cdylib
1591            | CrateType::Staticlib
1592            | CrateType::Sdylib => false,
1593        }
1594    }
1595}
1596
1597#[derive(Clone, Hash, Debug, PartialEq, Eq)]
1598pub enum Passes {
1599    Some(Vec<String>),
1600    All,
1601}
1602
1603impl Passes {
1604    fn is_empty(&self) -> bool {
1605        match *self {
1606            Passes::Some(ref v) => v.is_empty(),
1607            Passes::All => false,
1608        }
1609    }
1610
1611    pub(crate) fn extend(&mut self, passes: impl IntoIterator<Item = String>) {
1612        match *self {
1613            Passes::Some(ref mut v) => v.extend(passes),
1614            Passes::All => {}
1615        }
1616    }
1617}
1618
1619#[derive(Clone, Copy, Hash, Debug, PartialEq)]
1620pub enum PAuthKey {
1621    A,
1622    B,
1623}
1624
1625#[derive(Clone, Copy, Hash, Debug, PartialEq)]
1626pub struct PacRet {
1627    pub leaf: bool,
1628    pub pc: bool,
1629    pub key: PAuthKey,
1630}
1631
1632#[derive(Clone, Copy, Hash, Debug, PartialEq, Default)]
1633pub struct BranchProtection {
1634    pub bti: bool,
1635    pub pac_ret: Option<PacRet>,
1636    pub gcs: bool,
1637}
1638
1639pub(crate) const fn default_lib_output() -> CrateType {
1640    CrateType::Rlib
1641}
1642
1643pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
1644    // First disallow some configuration given on the command line
1645    cfg::disallow_cfgs(sess, &user_cfg);
1646
1647    // Then combine the configuration requested by the session (command line) with
1648    // some default and generated configuration items.
1649    user_cfg.extend(cfg::default_configuration(sess));
1650    user_cfg
1651}
1652
1653pub fn build_target_config(
1654    early_dcx: &EarlyDiagCtxt,
1655    target: &TargetTuple,
1656    sysroot: &Path,
1657) -> Target {
1658    match Target::search(target, sysroot) {
1659        Ok((target, warnings)) => {
1660            for warning in warnings.warning_messages() {
1661                early_dcx.early_warn(warning)
1662            }
1663
1664            if !matches!(target.pointer_width, 16 | 32 | 64) {
1665                early_dcx.early_fatal(format!(
1666                    "target specification was invalid: unrecognized target-pointer-width {}",
1667                    target.pointer_width
1668                ))
1669            }
1670            target
1671        }
1672        Err(e) => {
1673            let mut err =
1674                early_dcx.early_struct_fatal(format!("error loading target specification: {e}"));
1675            err.help("run `rustc --print target-list` for a list of built-in targets");
1676            err.emit();
1677        }
1678    }
1679}
1680
1681#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1682pub enum OptionStability {
1683    Stable,
1684    Unstable,
1685}
1686
1687#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1688pub enum OptionKind {
1689    /// An option that takes a value, and cannot appear more than once (e.g. `--out-dir`).
1690    ///
1691    /// Corresponds to [`getopts::Options::optopt`].
1692    Opt,
1693
1694    /// An option that takes a value, and can appear multiple times (e.g. `--emit`).
1695    ///
1696    /// Corresponds to [`getopts::Options::optmulti`].
1697    Multi,
1698
1699    /// An option that does not take a value, and cannot appear more than once (e.g. `--help`).
1700    ///
1701    /// Corresponds to [`getopts::Options::optflag`].
1702    /// The `hint` string must be empty.
1703    Flag,
1704
1705    /// An option that does not take a value, and can appear multiple times (e.g. `-O`).
1706    ///
1707    /// Corresponds to [`getopts::Options::optflagmulti`].
1708    /// The `hint` string must be empty.
1709    FlagMulti,
1710}
1711
1712pub struct RustcOptGroup {
1713    /// The "primary" name for this option. Normally equal to `long_name`,
1714    /// except for options that don't have a long name, in which case
1715    /// `short_name` is used.
1716    ///
1717    /// This is needed when interacting with `getopts` in some situations,
1718    /// because if an option has both forms, that library treats the long name
1719    /// as primary and the short name as an alias.
1720    pub name: &'static str,
1721    stability: OptionStability,
1722    kind: OptionKind,
1723
1724    short_name: &'static str,
1725    long_name: &'static str,
1726    desc: &'static str,
1727    value_hint: &'static str,
1728
1729    /// If true, this option should not be printed by `rustc --help`, but
1730    /// should still be printed by `rustc --help -v`.
1731    pub is_verbose_help_only: bool,
1732}
1733
1734impl RustcOptGroup {
1735    pub fn is_stable(&self) -> bool {
1736        self.stability == OptionStability::Stable
1737    }
1738
1739    pub fn apply(&self, options: &mut getopts::Options) {
1740        let &Self { short_name, long_name, desc, value_hint, .. } = self;
1741        match self.kind {
1742            OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint),
1743            OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint),
1744            OptionKind::Flag => options.optflag(short_name, long_name, desc),
1745            OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
1746        };
1747    }
1748
1749    /// This is for diagnostics-only.
1750    pub fn long_name(&self) -> &str {
1751        self.long_name
1752    }
1753}
1754
1755pub fn make_opt(
1756    stability: OptionStability,
1757    kind: OptionKind,
1758    short_name: &'static str,
1759    long_name: &'static str,
1760    desc: &'static str,
1761    value_hint: &'static str,
1762) -> RustcOptGroup {
1763    // "Flag" options don't have a value, and therefore don't have a value hint.
1764    match kind {
1765        OptionKind::Opt | OptionKind::Multi => {}
1766        OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""),
1767    }
1768    RustcOptGroup {
1769        name: cmp::max_by_key(short_name, long_name, |s| s.len()),
1770        stability,
1771        kind,
1772        short_name,
1773        long_name,
1774        desc,
1775        value_hint,
1776        is_verbose_help_only: false,
1777    }
1778}
1779
1780static EDITION_STRING: LazyLock<String> = LazyLock::new(|| {
1781    format!(
1782        "Specify which edition of the compiler to use when compiling code. \
1783The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}."
1784    )
1785});
1786
1787static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
1788    format!(
1789        "Compiler information to print on stdout (or to a file)\n\
1790        INFO may be one of <{}>.",
1791        PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|")
1792    )
1793});
1794
1795static EMIT_HELP: LazyLock<String> = LazyLock::new(|| {
1796    let mut result =
1797        String::from("Comma separated list of types of output for the compiler to emit.\n");
1798    result.push_str("Each TYPE has the default FILE name:\n");
1799
1800    for output in OutputType::iter_all() {
1801        result.push_str(&format!("*  {} - {}\n", output.shorthand(), output.default_filename()));
1802    }
1803
1804    result
1805});
1806
1807/// Returns all rustc command line options, including metadata for
1808/// each option, such as whether the option is stable.
1809///
1810/// # Option style guidelines
1811///
1812/// - `<param>`: Indicates a required parameter
1813/// - `[param]`: Indicates an optional parameter
1814/// - `|`: Indicates a mutually exclusive option
1815/// - `*`: a list element with description
1816pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
1817    use OptionKind::{Flag, FlagMulti, Multi, Opt};
1818    use OptionStability::{Stable, Unstable};
1819
1820    use self::make_opt as opt;
1821
1822    let mut options = vec![
1823        opt(Stable, Flag, "h", "help", "Display this message", ""),
1824        opt(
1825            Stable,
1826            Multi,
1827            "",
1828            "cfg",
1829            "Configure the compilation environment.\n\
1830                SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.",
1831            "<SPEC>",
1832        ),
1833        opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"),
1834        opt(
1835            Stable,
1836            Multi,
1837            "L",
1838            "",
1839            "Add a directory to the library search path. \
1840                The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).",
1841            "[<KIND>=]<PATH>",
1842        ),
1843        opt(
1844            Stable,
1845            Multi,
1846            "l",
1847            "",
1848            "Link the generated crate(s) to the specified native\n\
1849                library NAME. The optional KIND can be one of\n\
1850                <static|framework|dylib> (default: dylib).\n\
1851                Optional comma separated MODIFIERS\n\
1852                <bundle|verbatim|whole-archive|as-needed>\n\
1853                may be specified each with a prefix of either '+' to\n\
1854                enable or '-' to disable.",
1855            "[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]",
1856        ),
1857        make_crate_type_option(),
1858        opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"),
1859        opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST),
1860        opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"),
1861        opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"),
1862        opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""),
1863        opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""),
1864        opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"),
1865        opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"),
1866        opt(
1867            Stable,
1868            Opt,
1869            "",
1870            "explain",
1871            "Provide a detailed explanation of an error message",
1872            "<OPT>",
1873        ),
1874        opt(Stable, Flag, "", "test", "Build a test harness", ""),
1875        opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"),
1876        opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"),
1877        opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"),
1878        opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"),
1879        opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"),
1880        opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"),
1881        opt(
1882            Stable,
1883            Multi,
1884            "",
1885            "cap-lints",
1886            "Set the most restrictive lint level. More restrictive lints are capped at this level",
1887            "<LEVEL>",
1888        ),
1889        opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"),
1890        opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
1891        opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
1892    ];
1893
1894    // Options in this list are hidden from `rustc --help` by default, but are
1895    // shown by `rustc --help -v`.
1896    let verbose_only = [
1897        opt(
1898            Stable,
1899            Multi,
1900            "",
1901            "extern",
1902            "Specify where an external rust library is located",
1903            "<NAME>[=<PATH>]",
1904        ),
1905        opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"),
1906        opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"),
1907        opt(
1908            Stable,
1909            Opt,
1910            "",
1911            "error-format",
1912            "How errors and other messages are produced",
1913            "<human|json|short>",
1914        ),
1915        opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"),
1916        opt(
1917            Stable,
1918            Opt,
1919            "",
1920            "color",
1921            "Configure coloring of output:
1922                * auto   = colorize, if output goes to a tty (default);
1923                * always = always colorize output;
1924                * never  = never colorize output",
1925            "<auto|always|never>",
1926        ),
1927        opt(
1928            Stable,
1929            Opt,
1930            "",
1931            "diagnostic-width",
1932            "Inform rustc of the width of the output so that diagnostics can be truncated to fit",
1933            "<WIDTH>",
1934        ),
1935        opt(
1936            Stable,
1937            Multi,
1938            "",
1939            "remap-path-prefix",
1940            "Remap source names in all output (compiler messages and output files)",
1941            "<FROM>=<TO>",
1942        ),
1943        opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"),
1944    ];
1945    options.extend(verbose_only.into_iter().map(|mut opt| {
1946        opt.is_verbose_help_only = true;
1947        opt
1948    }));
1949
1950    options
1951}
1952
1953pub fn get_cmd_lint_options(
1954    early_dcx: &EarlyDiagCtxt,
1955    matches: &getopts::Matches,
1956) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
1957    let mut lint_opts_with_position = vec![];
1958    let mut describe_lints = false;
1959
1960    for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
1961        for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
1962            if lint_name == "help" {
1963                describe_lints = true;
1964            } else {
1965                lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level));
1966            }
1967        }
1968    }
1969
1970    lint_opts_with_position.sort_by_key(|x| x.0);
1971    let lint_opts = lint_opts_with_position
1972        .iter()
1973        .cloned()
1974        .map(|(_, lint_name, level)| (lint_name, level))
1975        .collect();
1976
1977    let lint_cap = matches.opt_str("cap-lints").map(|cap| {
1978        lint::Level::from_str(&cap)
1979            .unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`")))
1980    });
1981
1982    (lint_opts, describe_lints, lint_cap)
1983}
1984
1985/// Parses the `--color` flag.
1986pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig {
1987    match matches.opt_str("color").as_deref() {
1988        Some("auto") => ColorConfig::Auto,
1989        Some("always") => ColorConfig::Always,
1990        Some("never") => ColorConfig::Never,
1991
1992        None => ColorConfig::Auto,
1993
1994        Some(arg) => early_dcx.early_fatal(format!(
1995            "argument for `--color` must be auto, \
1996                 always or never (instead was `{arg}`)"
1997        )),
1998    }
1999}
2000
2001/// Possible json config files
2002pub struct JsonConfig {
2003    pub json_rendered: HumanReadableErrorType,
2004    pub json_color: ColorConfig,
2005    json_artifact_notifications: bool,
2006    /// Output start and end timestamps of several high-level compilation sections
2007    /// (frontend, backend, linker).
2008    json_timings: bool,
2009    pub json_unused_externs: JsonUnusedExterns,
2010    json_future_incompat: bool,
2011}
2012
2013/// Report unused externs in event stream
2014#[derive(Copy, Clone)]
2015pub enum JsonUnusedExterns {
2016    /// Do not
2017    No,
2018    /// Report, but do not exit with failure status for deny/forbid
2019    Silent,
2020    /// Report, and also exit with failure status for deny/forbid
2021    Loud,
2022}
2023
2024impl JsonUnusedExterns {
2025    pub fn is_enabled(&self) -> bool {
2026        match self {
2027            JsonUnusedExterns::No => false,
2028            JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
2029        }
2030    }
2031
2032    pub fn is_loud(&self) -> bool {
2033        match self {
2034            JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
2035            JsonUnusedExterns::Loud => true,
2036        }
2037    }
2038}
2039
2040/// Parse the `--json` flag.
2041///
2042/// The first value returned is how to render JSON diagnostics, and the second
2043/// is whether or not artifact notifications are enabled.
2044pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig {
2045    let mut json_rendered = HumanReadableErrorType::Default;
2046    let mut json_color = ColorConfig::Never;
2047    let mut json_artifact_notifications = false;
2048    let mut json_unused_externs = JsonUnusedExterns::No;
2049    let mut json_future_incompat = false;
2050    let mut json_timings = false;
2051    for option in matches.opt_strs("json") {
2052        // For now conservatively forbid `--color` with `--json` since `--json`
2053        // won't actually be emitting any colors and anything colorized is
2054        // embedded in a diagnostic message anyway.
2055        if matches.opt_str("color").is_some() {
2056            early_dcx.early_fatal("cannot specify the `--color` option with `--json`");
2057        }
2058
2059        for sub_option in option.split(',') {
2060            match sub_option {
2061                "diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
2062                "diagnostic-unicode" => {
2063                    json_rendered = HumanReadableErrorType::Unicode;
2064                }
2065                "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
2066                "artifacts" => json_artifact_notifications = true,
2067                "timings" => json_timings = true,
2068                "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
2069                "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
2070                "future-incompat" => json_future_incompat = true,
2071                s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")),
2072            }
2073        }
2074    }
2075
2076    JsonConfig {
2077        json_rendered,
2078        json_color,
2079        json_artifact_notifications,
2080        json_timings,
2081        json_unused_externs,
2082        json_future_incompat,
2083    }
2084}
2085
2086/// Parses the `--error-format` flag.
2087pub fn parse_error_format(
2088    early_dcx: &mut EarlyDiagCtxt,
2089    matches: &getopts::Matches,
2090    color_config: ColorConfig,
2091    json_color: ColorConfig,
2092    json_rendered: HumanReadableErrorType,
2093) -> ErrorOutputType {
2094    // We need the `opts_present` check because the driver will send us Matches
2095    // with only stable options if no unstable options are used. Since error-format
2096    // is unstable, it will not be present. We have to use `opts_present` not
2097    // `opt_present` because the latter will panic.
2098    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
2099        match matches.opt_str("error-format").as_deref() {
2100            None | Some("human") => ErrorOutputType::HumanReadable { color_config, .. },
2101            Some("human-annotate-rs") => ErrorOutputType::HumanReadable {
2102                kind: HumanReadableErrorType::AnnotateSnippet,
2103                color_config,
2104            },
2105            Some("json") => {
2106                ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color }
2107            }
2108            Some("pretty-json") => {
2109                ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color }
2110            }
2111            Some("short") => {
2112                ErrorOutputType::HumanReadable { kind: HumanReadableErrorType::Short, color_config }
2113            }
2114            Some("human-unicode") => ErrorOutputType::HumanReadable {
2115                kind: HumanReadableErrorType::Unicode,
2116                color_config,
2117            },
2118            Some(arg) => {
2119                early_dcx.set_error_format(ErrorOutputType::HumanReadable { color_config, .. });
2120                early_dcx.early_fatal(format!(
2121                    "argument for `--error-format` must be `human`, `human-annotate-rs`, \
2122                    `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)"
2123                ))
2124            }
2125        }
2126    } else {
2127        ErrorOutputType::HumanReadable { color_config, .. }
2128    };
2129
2130    match error_format {
2131        ErrorOutputType::Json { .. } => {}
2132
2133        // Conservatively require that the `--json` argument is coupled with
2134        // `--error-format=json`. This means that `--json` is specified we
2135        // should actually be emitting JSON blobs.
2136        _ if !matches.opt_strs("json").is_empty() => {
2137            early_dcx.early_fatal("using `--json` requires also using `--error-format=json`");
2138        }
2139
2140        _ => {}
2141    }
2142
2143    error_format
2144}
2145
2146pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition {
2147    let edition = match matches.opt_str("edition") {
2148        Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
2149            early_dcx.early_fatal(format!(
2150                "argument for `--edition` must be one of: \
2151                     {EDITION_NAME_LIST}. (instead was `{arg}`)"
2152            ))
2153        }),
2154        None => DEFAULT_EDITION,
2155    };
2156
2157    if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) {
2158        let is_nightly = nightly_options::match_is_nightly_build(matches);
2159        let msg = if !is_nightly {
2160            format!(
2161                "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}"
2162            )
2163        } else {
2164            format!("edition {edition} is unstable and only available with -Z unstable-options")
2165        };
2166        early_dcx.early_fatal(msg)
2167    }
2168
2169    edition
2170}
2171
2172fn check_error_format_stability(
2173    early_dcx: &EarlyDiagCtxt,
2174    unstable_opts: &UnstableOptions,
2175    format: ErrorOutputType,
2176) {
2177    if unstable_opts.unstable_options {
2178        return;
2179    }
2180    let format = match format {
2181        ErrorOutputType::Json { pretty: true, .. } => "pretty-json",
2182        ErrorOutputType::HumanReadable { kind, .. } => match kind {
2183            HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs",
2184            HumanReadableErrorType::Unicode => "human-unicode",
2185            _ => return,
2186        },
2187        _ => return,
2188    };
2189    early_dcx.early_fatal(format!("`--error-format={format}` is unstable"))
2190}
2191
2192fn parse_output_types(
2193    early_dcx: &EarlyDiagCtxt,
2194    unstable_opts: &UnstableOptions,
2195    matches: &getopts::Matches,
2196) -> OutputTypes {
2197    let mut output_types = BTreeMap::new();
2198    if !unstable_opts.parse_crate_root_only {
2199        for list in matches.opt_strs("emit") {
2200            for output_type in list.split(',') {
2201                let (shorthand, path) = split_out_file_name(output_type);
2202                let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
2203                    early_dcx.early_fatal(format!(
2204                        "unknown emission type: `{shorthand}` - expected one of: {display}",
2205                        display = OutputType::shorthands_display(),
2206                    ))
2207                });
2208                if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options {
2209                    early_dcx.early_fatal(format!(
2210                        "{} requested but -Zunstable-options not specified",
2211                        OutputType::ThinLinkBitcode.shorthand()
2212                    ));
2213                }
2214                output_types.insert(output_type, path);
2215            }
2216        }
2217    };
2218    if output_types.is_empty() {
2219        output_types.insert(OutputType::Exe, None);
2220    }
2221    OutputTypes(output_types)
2222}
2223
2224fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) {
2225    match arg.split_once('=') {
2226        None => (arg, None),
2227        Some((kind, "-")) => (kind, Some(OutFileName::Stdout)),
2228        Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))),
2229    }
2230}
2231
2232fn should_override_cgus_and_disable_thinlto(
2233    early_dcx: &EarlyDiagCtxt,
2234    output_types: &OutputTypes,
2235    matches: &getopts::Matches,
2236    mut codegen_units: Option<usize>,
2237) -> (bool, Option<usize>) {
2238    let mut disable_local_thinlto = false;
2239    // Issue #30063: if user requests LLVM-related output to one
2240    // particular path, disable codegen-units.
2241    let incompatible: Vec<_> = output_types
2242        .0
2243        .iter()
2244        .map(|ot_path| ot_path.0)
2245        .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file())
2246        .map(|ot| ot.shorthand())
2247        .collect();
2248    if !incompatible.is_empty() {
2249        match codegen_units {
2250            Some(n) if n > 1 => {
2251                if matches.opt_present("o") {
2252                    for ot in &incompatible {
2253                        early_dcx.early_warn(format!(
2254                            "`--emit={ot}` with `-o` incompatible with \
2255                                 `-C codegen-units=N` for N > 1",
2256                        ));
2257                    }
2258                    early_dcx.early_warn("resetting to default -C codegen-units=1");
2259                    codegen_units = Some(1);
2260                    disable_local_thinlto = true;
2261                }
2262            }
2263            _ => {
2264                codegen_units = Some(1);
2265                disable_local_thinlto = true;
2266            }
2267        }
2268    }
2269
2270    if codegen_units == Some(0) {
2271        early_dcx.early_fatal("value for codegen units must be a positive non-zero integer");
2272    }
2273
2274    (disable_local_thinlto, codegen_units)
2275}
2276
2277fn collect_print_requests(
2278    early_dcx: &EarlyDiagCtxt,
2279    cg: &mut CodegenOptions,
2280    unstable_opts: &UnstableOptions,
2281    matches: &getopts::Matches,
2282) -> Vec<PrintRequest> {
2283    let mut prints = Vec::<PrintRequest>::new();
2284    if cg.target_cpu.as_deref() == Some("help") {
2285        prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
2286        cg.target_cpu = None;
2287    };
2288    if cg.target_feature == "help" {
2289        prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
2290        cg.target_feature = String::new();
2291    }
2292
2293    // We disallow reusing the same path in multiple prints, such as `--print
2294    // cfg=output.txt --print link-args=output.txt`, because outputs are printed
2295    // by disparate pieces of the compiler, and keeping track of which files
2296    // need to be overwritten vs appended to is annoying.
2297    let mut printed_paths = FxHashSet::default();
2298
2299    prints.extend(matches.opt_strs("print").into_iter().map(|req| {
2300        let (req, out) = split_out_file_name(&req);
2301
2302        let kind = if let Some((print_name, print_kind)) =
2303            PRINT_KINDS.iter().find(|&&(name, _)| name == req)
2304        {
2305            check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind));
2306            *print_kind
2307        } else {
2308            let is_nightly = nightly_options::match_is_nightly_build(matches);
2309            emit_unknown_print_request_help(early_dcx, req, is_nightly)
2310        };
2311
2312        let out = out.unwrap_or(OutFileName::Stdout);
2313        if let OutFileName::Real(path) = &out {
2314            if !printed_paths.insert(path.clone()) {
2315                early_dcx.early_fatal(format!(
2316                    "cannot print multiple outputs to the same path: {}",
2317                    path.display(),
2318                ));
2319            }
2320        }
2321
2322        PrintRequest { kind, out }
2323    }));
2324
2325    prints
2326}
2327
2328fn check_print_request_stability(
2329    early_dcx: &EarlyDiagCtxt,
2330    unstable_opts: &UnstableOptions,
2331    (print_name, print_kind): (&str, PrintKind),
2332) {
2333    if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options {
2334        early_dcx.early_fatal(format!(
2335            "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
2336                print option"
2337        ));
2338    }
2339}
2340
2341fn is_print_request_stable(print_kind: PrintKind) -> bool {
2342    match print_kind {
2343        PrintKind::AllTargetSpecsJson
2344        | PrintKind::CheckCfg
2345        | PrintKind::CrateRootLintLevels
2346        | PrintKind::SupportedCrateTypes
2347        | PrintKind::TargetSpecJson
2348        | PrintKind::TargetSpecJsonSchema => false,
2349        _ => true,
2350    }
2351}
2352
2353fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
2354    let prints = PRINT_KINDS
2355        .iter()
2356        .filter_map(|(name, kind)| {
2357            // If we're not on nightly, we don't want to print unstable options
2358            if !is_nightly && !is_print_request_stable(*kind) {
2359                None
2360            } else {
2361                Some(format!("`{name}`"))
2362            }
2363        })
2364        .collect::<Vec<_>>();
2365    let prints = prints.join(", ");
2366
2367    let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
2368    #[allow(rustc::diagnostic_outside_of_impl)]
2369    diag.help(format!("valid print requests are: {prints}"));
2370
2371    if req == "lints" {
2372        diag.help(format!("use `-Whelp` to print a list of lints"));
2373    }
2374
2375    diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"));
2376    diag.emit()
2377}
2378
2379pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple {
2380    match matches.opt_str("target") {
2381        Some(target) if target.ends_with(".json") => {
2382            let path = Path::new(&target);
2383            TargetTuple::from_path(path).unwrap_or_else(|_| {
2384                early_dcx.early_fatal(format!("target file {path:?} does not exist"))
2385            })
2386        }
2387        Some(target) => TargetTuple::TargetTuple(target),
2388        _ => TargetTuple::from_tuple(host_tuple()),
2389    }
2390}
2391
2392fn parse_opt_level(
2393    early_dcx: &EarlyDiagCtxt,
2394    matches: &getopts::Matches,
2395    cg: &CodegenOptions,
2396) -> OptLevel {
2397    // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
2398    // to use them interchangeably. However, because they're technically different flags,
2399    // we need to work out manually which should take precedence if both are supplied (i.e.
2400    // the rightmost flag). We do this by finding the (rightmost) position of both flags and
2401    // comparing them. Note that if a flag is not found, its position will be `None`, which
2402    // always compared less than `Some(_)`.
2403    let max_o = matches.opt_positions("O").into_iter().max();
2404    let max_c = matches
2405        .opt_strs_pos("C")
2406        .into_iter()
2407        .flat_map(|(i, s)| {
2408            // NB: This can match a string without `=`.
2409            if let Some("opt-level") = s.split('=').next() { Some(i) } else { None }
2410        })
2411        .max();
2412    if max_o > max_c {
2413        OptLevel::Aggressive
2414    } else {
2415        match cg.opt_level.as_ref() {
2416            "0" => OptLevel::No,
2417            "1" => OptLevel::Less,
2418            "2" => OptLevel::More,
2419            "3" => OptLevel::Aggressive,
2420            "s" => OptLevel::Size,
2421            "z" => OptLevel::SizeMin,
2422            arg => {
2423                early_dcx.early_fatal(format!(
2424                    "optimization level needs to be \
2425                            between 0-3, s or z (instead was `{arg}`)"
2426                ));
2427            }
2428        }
2429    }
2430}
2431
2432fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInfo {
2433    let max_g = matches.opt_positions("g").into_iter().max();
2434    let max_c = matches
2435        .opt_strs_pos("C")
2436        .into_iter()
2437        .flat_map(|(i, s)| {
2438            // NB: This can match a string without `=`.
2439            if let Some("debuginfo") = s.split('=').next() { Some(i) } else { None }
2440        })
2441        .max();
2442    if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
2443}
2444
2445fn parse_assert_incr_state(
2446    early_dcx: &EarlyDiagCtxt,
2447    opt_assertion: &Option<String>,
2448) -> Option<IncrementalStateAssertion> {
2449    match opt_assertion {
2450        Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
2451        Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
2452        Some(s) => {
2453            early_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}"))
2454        }
2455        None => None,
2456    }
2457}
2458
2459pub fn parse_externs(
2460    early_dcx: &EarlyDiagCtxt,
2461    matches: &getopts::Matches,
2462    unstable_opts: &UnstableOptions,
2463) -> Externs {
2464    let is_unstable_enabled = unstable_opts.unstable_options;
2465    let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
2466    for arg in matches.opt_strs("extern") {
2467        let ExternOpt { crate_name: name, path, options } =
2468            split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit());
2469
2470        let entry = externs.entry(name.to_owned());
2471
2472        use std::collections::btree_map::Entry;
2473
2474        let entry = if let Some(path) = path {
2475            // --extern prelude_name=some_file.rlib
2476            let path = CanonicalizedPath::new(path);
2477            match entry {
2478                Entry::Vacant(vacant) => {
2479                    let files = BTreeSet::from_iter(iter::once(path));
2480                    vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files)))
2481                }
2482                Entry::Occupied(occupied) => {
2483                    let ext_ent = occupied.into_mut();
2484                    match ext_ent {
2485                        ExternEntry { location: ExternLocation::ExactPaths(files), .. } => {
2486                            files.insert(path);
2487                        }
2488                        ExternEntry {
2489                            location: location @ ExternLocation::FoundInLibrarySearchDirectories,
2490                            ..
2491                        } => {
2492                            // Exact paths take precedence over search directories.
2493                            let files = BTreeSet::from_iter(iter::once(path));
2494                            *location = ExternLocation::ExactPaths(files);
2495                        }
2496                    }
2497                    ext_ent
2498                }
2499            }
2500        } else {
2501            // --extern prelude_name
2502            match entry {
2503                Entry::Vacant(vacant) => {
2504                    vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
2505                }
2506                Entry::Occupied(occupied) => {
2507                    // Ignore if already specified.
2508                    occupied.into_mut()
2509                }
2510            }
2511        };
2512
2513        let mut is_private_dep = false;
2514        let mut add_prelude = true;
2515        let mut nounused_dep = false;
2516        let mut force = false;
2517        if let Some(opts) = options {
2518            if !is_unstable_enabled {
2519                early_dcx.early_fatal(
2520                    "the `-Z unstable-options` flag must also be passed to \
2521                     enable `--extern` options",
2522                );
2523            }
2524            for opt in opts.split(',') {
2525                match opt {
2526                    "priv" => is_private_dep = true,
2527                    "noprelude" => {
2528                        if let ExternLocation::ExactPaths(_) = &entry.location {
2529                            add_prelude = false;
2530                        } else {
2531                            early_dcx.early_fatal(
2532                                "the `noprelude` --extern option requires a file path",
2533                            );
2534                        }
2535                    }
2536                    "nounused" => nounused_dep = true,
2537                    "force" => force = true,
2538                    _ => early_dcx.early_fatal(format!("unknown --extern option `{opt}`")),
2539                }
2540            }
2541        }
2542
2543        // Crates start out being not private, and go to being private `priv`
2544        // is specified.
2545        entry.is_private_dep |= is_private_dep;
2546        // likewise `nounused`
2547        entry.nounused_dep |= nounused_dep;
2548        // and `force`
2549        entry.force |= force;
2550        // If any flag is missing `noprelude`, then add to the prelude.
2551        entry.add_prelude |= add_prelude;
2552    }
2553    Externs(externs)
2554}
2555
2556fn parse_remap_path_prefix(
2557    early_dcx: &EarlyDiagCtxt,
2558    matches: &getopts::Matches,
2559    unstable_opts: &UnstableOptions,
2560) -> Vec<(PathBuf, PathBuf)> {
2561    let mut mapping: Vec<(PathBuf, PathBuf)> = matches
2562        .opt_strs("remap-path-prefix")
2563        .into_iter()
2564        .map(|remap| match remap.rsplit_once('=') {
2565            None => {
2566                early_dcx.early_fatal("--remap-path-prefix must contain '=' between FROM and TO")
2567            }
2568            Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
2569        })
2570        .collect();
2571    match &unstable_opts.remap_cwd_prefix {
2572        Some(to) => match std::env::current_dir() {
2573            Ok(cwd) => mapping.push((cwd, to.clone())),
2574            Err(_) => (),
2575        },
2576        None => (),
2577    };
2578    mapping
2579}
2580
2581fn parse_logical_env(
2582    early_dcx: &EarlyDiagCtxt,
2583    matches: &getopts::Matches,
2584) -> FxIndexMap<String, String> {
2585    let mut vars = FxIndexMap::default();
2586
2587    for arg in matches.opt_strs("env-set") {
2588        if let Some((name, val)) = arg.split_once('=') {
2589            vars.insert(name.to_string(), val.to_string());
2590        } else {
2591            early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`"));
2592        }
2593    }
2594
2595    vars
2596}
2597
2598// JUSTIFICATION: before wrapper fn is available
2599#[allow(rustc::bad_opt_access)]
2600pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options {
2601    let color = parse_color(early_dcx, matches);
2602
2603    let edition = parse_crate_edition(early_dcx, matches);
2604
2605    let JsonConfig {
2606        json_rendered,
2607        json_color,
2608        json_artifact_notifications,
2609        json_timings,
2610        json_unused_externs,
2611        json_future_incompat,
2612    } = parse_json(early_dcx, matches);
2613
2614    let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered);
2615
2616    early_dcx.set_error_format(error_format);
2617
2618    let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
2619        early_dcx.early_fatal("`--diagnostic-width` must be an positive integer");
2620    });
2621
2622    let unparsed_crate_types = matches.opt_strs("crate-type");
2623    let crate_types = parse_crate_types_from_list(unparsed_crate_types)
2624        .unwrap_or_else(|e| early_dcx.early_fatal(e));
2625
2626    let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new();
2627
2628    let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
2629    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
2630
2631    if !unstable_opts.unstable_options && json_timings {
2632        early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`");
2633    }
2634
2635    check_error_format_stability(early_dcx, &unstable_opts, error_format);
2636
2637    let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
2638
2639    let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
2640    let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
2641        early_dcx,
2642        &output_types,
2643        matches,
2644        cg.codegen_units,
2645    );
2646
2647    if unstable_opts.threads == 0 {
2648        early_dcx.early_fatal("value for threads must be a positive non-zero integer");
2649    }
2650
2651    if unstable_opts.threads == parse::MAX_THREADS_CAP {
2652        early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP));
2653    }
2654
2655    let incremental = cg.incremental.as_ref().map(PathBuf::from);
2656
2657    let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
2658
2659    if cg.profile_generate.enabled() && cg.profile_use.is_some() {
2660        early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive");
2661    }
2662
2663    if unstable_opts.profile_sample_use.is_some()
2664        && (cg.profile_generate.enabled() || cg.profile_use.is_some())
2665    {
2666        early_dcx.early_fatal(
2667            "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
2668        );
2669    }
2670
2671    // Check for unstable values of `-C symbol-mangling-version`.
2672    // This is what prevents them from being used on stable compilers.
2673    match cg.symbol_mangling_version {
2674        // Stable values:
2675        None | Some(SymbolManglingVersion::V0) => {}
2676
2677        // Unstable values:
2678        Some(SymbolManglingVersion::Legacy) => {
2679            if !unstable_opts.unstable_options {
2680                early_dcx.early_fatal(
2681                    "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
2682                );
2683            }
2684        }
2685        Some(SymbolManglingVersion::Hashed) => {
2686            if !unstable_opts.unstable_options {
2687                early_dcx.early_fatal(
2688                    "`-C symbol-mangling-version=hashed` requires `-Z unstable-options`",
2689                );
2690            }
2691        }
2692    }
2693
2694    if cg.instrument_coverage != InstrumentCoverage::No {
2695        if cg.profile_generate.enabled() || cg.profile_use.is_some() {
2696            early_dcx.early_fatal(
2697                "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
2698                or `-C profile-generate`",
2699            );
2700        }
2701
2702        // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
2703        // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
2704        // multiple runs, including some changes to source code; so mangled names must be consistent
2705        // across compilations.
2706        match cg.symbol_mangling_version {
2707            None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
2708            Some(SymbolManglingVersion::Legacy) => {
2709                early_dcx.early_warn(
2710                    "-C instrument-coverage requires symbol mangling version `v0`, \
2711                    but `-C symbol-mangling-version=legacy` was specified",
2712                );
2713            }
2714            Some(SymbolManglingVersion::V0) => {}
2715            Some(SymbolManglingVersion::Hashed) => {
2716                early_dcx.early_warn(
2717                    "-C instrument-coverage requires symbol mangling version `v0`, \
2718                    but `-C symbol-mangling-version=hashed` was specified",
2719                );
2720            }
2721        }
2722    }
2723
2724    if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
2725        // FIXME: this is only mutation of UnstableOptions here, move into
2726        // UnstableOptions::build?
2727        unstable_opts.graphviz_font = graphviz_font;
2728    }
2729
2730    if !cg.embed_bitcode {
2731        match cg.lto {
2732            LtoCli::No | LtoCli::Unspecified => {}
2733            LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
2734                early_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible")
2735            }
2736        }
2737    }
2738
2739    let unstable_options_enabled = nightly_options::is_unstable_enabled(matches);
2740    if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf {
2741        early_dcx.early_fatal(
2742            "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \
2743                and a nightly compiler",
2744        )
2745    }
2746
2747    if !nightly_options::is_unstable_enabled(matches)
2748        && unstable_opts.offload.contains(&Offload::Enable)
2749    {
2750        early_dcx.early_fatal(
2751            "`-Zoffload=Enable` also requires `-Zunstable-options` \
2752                and a nightly compiler",
2753        )
2754    }
2755
2756    let target_triple = parse_target_triple(early_dcx, matches);
2757
2758    // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and
2759    // `-C linker-flavor` options.
2760    if !unstable_options_enabled {
2761        if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) {
2762            early_dcx.early_fatal(error);
2763        }
2764
2765        if let Some(flavor) = cg.linker_flavor {
2766            if flavor.is_unstable() {
2767                early_dcx.early_fatal(format!(
2768                    "the linker flavor `{}` is unstable, the `-Z unstable-options` \
2769                        flag must also be passed to use the unstable values",
2770                    flavor.desc()
2771                ));
2772            }
2773        }
2774    }
2775
2776    // Check `-C link-self-contained` for consistency: individual components cannot be both enabled
2777    // and disabled at the same time.
2778    if let Some(erroneous_components) = cg.link_self_contained.check_consistency() {
2779        let names: String = erroneous_components
2780            .into_iter()
2781            .map(|c| c.as_str().unwrap())
2782            .intersperse(", ")
2783            .collect();
2784        early_dcx.early_fatal(format!(
2785            "some `-C link-self-contained` components were both enabled and disabled: {names}"
2786        ));
2787    }
2788
2789    let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches);
2790
2791    // -Zretpoline-external-thunk also requires -Zretpoline
2792    if unstable_opts.retpoline_external_thunk {
2793        unstable_opts.retpoline = true;
2794        target_modifiers.insert(
2795            OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline),
2796            "true".to_string(),
2797        );
2798    }
2799
2800    let cg = cg;
2801
2802    let opt_level = parse_opt_level(early_dcx, matches, &cg);
2803    // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
2804    // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
2805    // for more details.
2806    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
2807    let debuginfo = select_debuginfo(matches, &cg);
2808    let debuginfo_compression = unstable_opts.debuginfo_compression;
2809
2810    if !unstable_options_enabled {
2811        if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) {
2812            early_dcx.early_fatal(error);
2813        }
2814    }
2815
2816    if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) {
2817        early_dcx.early_fatal(
2818            "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler",
2819        )
2820    }
2821
2822    let crate_name = matches.opt_str("crate-name");
2823    let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref());
2824    // Parse any `-l` flags, which link to native libraries.
2825    let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches);
2826
2827    let test = matches.opt_present("test");
2828
2829    if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
2830        early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
2831    }
2832
2833    if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() {
2834        early_dcx
2835            .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
2836    }
2837
2838    let externs = parse_externs(early_dcx, matches, &unstable_opts);
2839
2840    let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
2841
2842    let pretty = parse_pretty(early_dcx, &unstable_opts);
2843
2844    // query-dep-graph is required if dump-dep-graph is given #106736
2845    if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
2846        early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`");
2847    }
2848
2849    let logical_env = parse_logical_env(early_dcx, matches);
2850
2851    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
2852
2853    let real_source_base_dir = |suffix: &str, confirm: &str| {
2854        let mut candidate = sysroot.path().join(suffix);
2855        if let Ok(metadata) = candidate.symlink_metadata() {
2856            // Replace the symlink bootstrap creates, with its destination.
2857            // We could try to use `fs::canonicalize` instead, but that might
2858            // produce unnecessarily verbose path.
2859            if metadata.file_type().is_symlink() {
2860                if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
2861                    candidate = symlink_dest;
2862                }
2863            }
2864        }
2865
2866        // Only use this directory if it has a file we can expect to always find.
2867        candidate.join(confirm).is_file().then_some(candidate)
2868    };
2869
2870    let real_rust_source_base_dir =
2871        // This is the location used by the `rust-src` `rustup` component.
2872        real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs");
2873
2874    let real_rustc_dev_source_base_dir =
2875        // This is the location used by the `rustc-dev` `rustup` component.
2876        real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs");
2877
2878    // We eagerly scan all files in each passed -L path. If the same directory is passed multiple
2879    // times, and the directory contains a lot of files, this can take a lot of time.
2880    // So we remove -L paths that were passed multiple times, and keep only the first occurrence.
2881    // We still have to keep the original order of the -L arguments.
2882    let search_paths: Vec<SearchPath> = {
2883        let mut seen_search_paths = FxHashSet::default();
2884        let search_path_matches: Vec<String> = matches.opt_strs("L");
2885        search_path_matches
2886            .iter()
2887            .filter(|p| seen_search_paths.insert(*p))
2888            .map(|path| {
2889                SearchPath::from_cli_opt(
2890                    sysroot.path(),
2891                    &target_triple,
2892                    early_dcx,
2893                    &path,
2894                    unstable_opts.unstable_options,
2895                )
2896            })
2897            .collect()
2898    };
2899
2900    let working_dir = std::env::current_dir().unwrap_or_else(|e| {
2901        early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
2902    });
2903
2904    let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
2905    let working_dir = file_mapping.to_real_filename(&working_dir);
2906
2907    let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals;
2908
2909    Options {
2910        assert_incr_state,
2911        crate_types,
2912        optimize: opt_level,
2913        debuginfo,
2914        debuginfo_compression,
2915        lint_opts,
2916        lint_cap,
2917        describe_lints,
2918        output_types,
2919        search_paths,
2920        sysroot,
2921        target_triple,
2922        test,
2923        incremental,
2924        untracked_state_hash: Default::default(),
2925        unstable_opts,
2926        prints,
2927        cg,
2928        error_format,
2929        diagnostic_width,
2930        externs,
2931        unstable_features,
2932        crate_name,
2933        libs,
2934        debug_assertions,
2935        actually_rustdoc: false,
2936        resolve_doc_links: ResolveDocLinks::ExportedMetadata,
2937        trimmed_def_paths: false,
2938        cli_forced_codegen_units: codegen_units,
2939        cli_forced_local_thinlto_off: disable_local_thinlto,
2940        remap_path_prefix,
2941        real_rust_source_base_dir,
2942        real_rustc_dev_source_base_dir,
2943        edition,
2944        json_artifact_notifications,
2945        json_timings,
2946        json_unused_externs,
2947        json_future_incompat,
2948        pretty,
2949        working_dir,
2950        color,
2951        logical_env,
2952        verbose,
2953        target_modifiers,
2954    }
2955}
2956
2957fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> Option<PpMode> {
2958    use PpMode::*;
2959
2960    let first = match unstable_opts.unpretty.as_deref()? {
2961        "normal" => Source(PpSourceMode::Normal),
2962        "identified" => Source(PpSourceMode::Identified),
2963        "expanded" => Source(PpSourceMode::Expanded),
2964        "expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
2965        "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
2966        "ast-tree" => AstTree,
2967        "ast-tree,expanded" => AstTreeExpanded,
2968        "hir" => Hir(PpHirMode::Normal),
2969        "hir,identified" => Hir(PpHirMode::Identified),
2970        "hir,typed" => Hir(PpHirMode::Typed),
2971        "hir-tree" => HirTree,
2972        "thir-tree" => ThirTree,
2973        "thir-flat" => ThirFlat,
2974        "mir" => Mir,
2975        "stable-mir" => StableMir,
2976        "mir-cfg" => MirCFG,
2977        name => early_dcx.early_fatal(format!(
2978            "argument to `unpretty` must be one of `normal`, `identified`, \
2979                            `expanded`, `expanded,identified`, `expanded,hygiene`, \
2980                            `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
2981                            `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \
2982                            `mir-cfg`; got {name}"
2983        )),
2984    };
2985    debug!("got unpretty option: {first:?}");
2986    Some(first)
2987}
2988
2989pub fn make_crate_type_option() -> RustcOptGroup {
2990    make_opt(
2991        OptionStability::Stable,
2992        OptionKind::Multi,
2993        "",
2994        "crate-type",
2995        "Comma separated list of types of crates
2996                                for the compiler to emit",
2997        "<bin|lib|rlib|dylib|cdylib|staticlib|proc-macro>",
2998    )
2999}
3000
3001pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
3002    let mut crate_types: Vec<CrateType> = Vec::new();
3003    for unparsed_crate_type in &list_list {
3004        for part in unparsed_crate_type.split(',') {
3005            let new_part = match part {
3006                "lib" => default_lib_output(),
3007                "rlib" => CrateType::Rlib,
3008                "staticlib" => CrateType::Staticlib,
3009                "dylib" => CrateType::Dylib,
3010                "cdylib" => CrateType::Cdylib,
3011                "bin" => CrateType::Executable,
3012                "proc-macro" => CrateType::ProcMacro,
3013                "sdylib" => CrateType::Sdylib,
3014                _ => {
3015                    return Err(format!(
3016                        "unknown crate type: `{part}`, expected one of: \
3017                        `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`",
3018                    ));
3019                }
3020            };
3021            if !crate_types.contains(&new_part) {
3022                crate_types.push(new_part)
3023            }
3024        }
3025    }
3026
3027    Ok(crate_types)
3028}
3029
3030pub mod nightly_options {
3031    use rustc_feature::UnstableFeatures;
3032
3033    use super::{OptionStability, RustcOptGroup};
3034    use crate::EarlyDiagCtxt;
3035
3036    pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
3037        match_is_nightly_build(matches)
3038            && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
3039    }
3040
3041    pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool {
3042        is_nightly_build(matches.opt_str("crate-name").as_deref())
3043    }
3044
3045    fn is_nightly_build(krate: Option<&str>) -> bool {
3046        UnstableFeatures::from_environment(krate).is_nightly_build()
3047    }
3048
3049    pub fn check_nightly_options(
3050        early_dcx: &EarlyDiagCtxt,
3051        matches: &getopts::Matches,
3052        flags: &[RustcOptGroup],
3053    ) {
3054        let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
3055        let really_allows_unstable_options = match_is_nightly_build(matches);
3056        let mut nightly_options_on_stable = 0;
3057
3058        for opt in flags.iter() {
3059            if opt.stability == OptionStability::Stable {
3060                continue;
3061            }
3062            if !matches.opt_present(opt.name) {
3063                continue;
3064            }
3065            if opt.name != "Z" && !has_z_unstable_option {
3066                early_dcx.early_fatal(format!(
3067                    "the `-Z unstable-options` flag must also be passed to enable \
3068                         the flag `{}`",
3069                    opt.name
3070                ));
3071            }
3072            if really_allows_unstable_options {
3073                continue;
3074            }
3075            match opt.stability {
3076                OptionStability::Unstable => {
3077                    nightly_options_on_stable += 1;
3078                    let msg = format!(
3079                        "the option `{}` is only accepted on the nightly compiler",
3080                        opt.name
3081                    );
3082                    // The non-zero nightly_options_on_stable will force an early_fatal eventually.
3083                    let _ = early_dcx.early_err(msg);
3084                }
3085                OptionStability::Stable => {}
3086            }
3087        }
3088        if nightly_options_on_stable > 0 {
3089            early_dcx
3090                .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
3091            early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
3092            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>");
3093            early_dcx.early_fatal(format!(
3094                "{} nightly option{} were parsed",
3095                nightly_options_on_stable,
3096                if nightly_options_on_stable > 1 { "s" } else { "" }
3097            ));
3098        }
3099    }
3100}
3101
3102impl fmt::Display for CrateType {
3103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3104        match *self {
3105            CrateType::Executable => "bin".fmt(f),
3106            CrateType::Dylib => "dylib".fmt(f),
3107            CrateType::Rlib => "rlib".fmt(f),
3108            CrateType::Staticlib => "staticlib".fmt(f),
3109            CrateType::Cdylib => "cdylib".fmt(f),
3110            CrateType::ProcMacro => "proc-macro".fmt(f),
3111            CrateType::Sdylib => "sdylib".fmt(f),
3112        }
3113    }
3114}
3115
3116impl IntoDiagArg for CrateType {
3117    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
3118        self.to_string().into_diag_arg(&mut None)
3119    }
3120}
3121
3122#[derive(Copy, Clone, PartialEq, Debug)]
3123pub enum PpSourceMode {
3124    /// `-Zunpretty=normal`
3125    Normal,
3126    /// `-Zunpretty=expanded`
3127    Expanded,
3128    /// `-Zunpretty=identified`
3129    Identified,
3130    /// `-Zunpretty=expanded,identified`
3131    ExpandedIdentified,
3132    /// `-Zunpretty=expanded,hygiene`
3133    ExpandedHygiene,
3134}
3135
3136#[derive(Copy, Clone, PartialEq, Debug)]
3137pub enum PpHirMode {
3138    /// `-Zunpretty=hir`
3139    Normal,
3140    /// `-Zunpretty=hir,identified`
3141    Identified,
3142    /// `-Zunpretty=hir,typed`
3143    Typed,
3144}
3145
3146#[derive(Copy, Clone, PartialEq, Debug)]
3147/// Pretty print mode
3148pub enum PpMode {
3149    /// Options that print the source code, i.e.
3150    /// `-Zunpretty=normal` and `-Zunpretty=expanded`
3151    Source(PpSourceMode),
3152    /// `-Zunpretty=ast-tree`
3153    AstTree,
3154    /// `-Zunpretty=ast-tree,expanded`
3155    AstTreeExpanded,
3156    /// Options that print the HIR, i.e. `-Zunpretty=hir`
3157    Hir(PpHirMode),
3158    /// `-Zunpretty=hir-tree`
3159    HirTree,
3160    /// `-Zunpretty=thir-tree`
3161    ThirTree,
3162    /// `-Zunpretty=thir-flat`
3163    ThirFlat,
3164    /// `-Zunpretty=mir`
3165    Mir,
3166    /// `-Zunpretty=mir-cfg`
3167    MirCFG,
3168    /// `-Zunpretty=stable-mir`
3169    StableMir,
3170}
3171
3172impl PpMode {
3173    pub fn needs_ast_map(&self) -> bool {
3174        use PpMode::*;
3175        use PpSourceMode::*;
3176        match *self {
3177            Source(Normal | Identified) | AstTree => false,
3178
3179            Source(Expanded | ExpandedIdentified | ExpandedHygiene)
3180            | AstTreeExpanded
3181            | Hir(_)
3182            | HirTree
3183            | ThirTree
3184            | ThirFlat
3185            | Mir
3186            | MirCFG
3187            | StableMir => true,
3188        }
3189    }
3190
3191    pub fn needs_analysis(&self) -> bool {
3192        use PpMode::*;
3193        matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat)
3194    }
3195}
3196
3197#[derive(Clone, Hash, PartialEq, Eq, Debug)]
3198pub enum WasiExecModel {
3199    Command,
3200    Reactor,
3201}
3202
3203/// Command-line arguments passed to the compiler have to be incorporated with
3204/// the dependency tracking system for incremental compilation. This module
3205/// provides some utilities to make this more convenient.
3206///
3207/// The values of all command-line arguments that are relevant for dependency
3208/// tracking are hashed into a single value that determines whether the
3209/// incremental compilation cache can be re-used or not. This hashing is done
3210/// via the `DepTrackingHash` trait defined below, since the standard `Hash`
3211/// implementation might not be suitable (e.g., arguments are stored in a `Vec`,
3212/// the hash of which is order dependent, but we might not want the order of
3213/// arguments to make a difference for the hash).
3214///
3215/// However, since the value provided by `Hash::hash` often *is* suitable,
3216/// especially for primitive types, there is the
3217/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the
3218/// `Hash` implementation for `DepTrackingHash`. It's important though that
3219/// we have an opt-in scheme here, so one is hopefully forced to think about
3220/// how the hash should be calculated when adding a new command-line argument.
3221pub(crate) mod dep_tracking {
3222    use std::collections::BTreeMap;
3223    use std::hash::Hash;
3224    use std::num::NonZero;
3225    use std::path::PathBuf;
3226
3227    use rustc_abi::Align;
3228    use rustc_data_structures::fx::FxIndexMap;
3229    use rustc_data_structures::stable_hasher::StableHasher;
3230    use rustc_errors::LanguageIdentifier;
3231    use rustc_feature::UnstableFeatures;
3232    use rustc_hashes::Hash64;
3233    use rustc_span::RealFileName;
3234    use rustc_span::edition::Edition;
3235    use rustc_target::spec::{
3236        CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
3237        RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple,
3238        TlsModel,
3239    };
3240
3241    use super::{
3242        AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions,
3243        CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn,
3244        InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
3245        LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OomStrategy, OptLevel, OutFileName,
3246        OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents,
3247        ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
3248        SymbolManglingVersion, WasiExecModel,
3249    };
3250    use crate::lint;
3251    use crate::utils::NativeLib;
3252
3253    pub(crate) trait DepTrackingHash {
3254        fn hash(
3255            &self,
3256            hasher: &mut StableHasher,
3257            error_format: ErrorOutputType,
3258            for_crate_hash: bool,
3259        );
3260    }
3261
3262    macro_rules! impl_dep_tracking_hash_via_hash {
3263        ($($t:ty),+ $(,)?) => {$(
3264            impl DepTrackingHash for $t {
3265                fn hash(&self, hasher: &mut StableHasher, _: ErrorOutputType, _for_crate_hash: bool) {
3266                    Hash::hash(self, hasher);
3267                }
3268            }
3269        )+};
3270    }
3271
3272    impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
3273        fn hash(
3274            &self,
3275            hasher: &mut StableHasher,
3276            error_format: ErrorOutputType,
3277            for_crate_hash: bool,
3278        ) {
3279            match self {
3280                Some(x) => {
3281                    Hash::hash(&1, hasher);
3282                    DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
3283                }
3284                None => Hash::hash(&0, hasher),
3285            }
3286        }
3287    }
3288
3289    impl_dep_tracking_hash_via_hash!(
3290        (),
3291        AutoDiff,
3292        Offload,
3293        bool,
3294        usize,
3295        NonZero<usize>,
3296        u64,
3297        Hash64,
3298        String,
3299        PathBuf,
3300        lint::Level,
3301        WasiExecModel,
3302        u32,
3303        FramePointer,
3304        RelocModel,
3305        CodeModel,
3306        TlsModel,
3307        InstrumentCoverage,
3308        CoverageOptions,
3309        InstrumentXRay,
3310        CrateType,
3311        MergeFunctions,
3312        OnBrokenPipe,
3313        PanicStrategy,
3314        RelroLevel,
3315        OptLevel,
3316        LtoCli,
3317        DebugInfo,
3318        DebugInfoCompression,
3319        MirStripDebugInfo,
3320        CollapseMacroDebuginfo,
3321        UnstableFeatures,
3322        NativeLib,
3323        SanitizerSet,
3324        CFGuard,
3325        CFProtection,
3326        TargetTuple,
3327        Edition,
3328        LinkerPluginLto,
3329        ResolveDocLinks,
3330        SplitDebuginfo,
3331        SplitDwarfKind,
3332        StackProtector,
3333        SwitchWithOptPath,
3334        SymbolManglingVersion,
3335        SymbolVisibility,
3336        RemapPathScopeComponents,
3337        SourceFileHashAlgorithm,
3338        OutFileName,
3339        OutputType,
3340        RealFileName,
3341        LocationDetail,
3342        FmtDebug,
3343        BranchProtection,
3344        OomStrategy,
3345        LanguageIdentifier,
3346        NextSolverConfig,
3347        PatchableFunctionEntry,
3348        Polonius,
3349        InliningThreshold,
3350        FunctionReturn,
3351        Align,
3352    );
3353
3354    impl<T1, T2> DepTrackingHash for (T1, T2)
3355    where
3356        T1: DepTrackingHash,
3357        T2: DepTrackingHash,
3358    {
3359        fn hash(
3360            &self,
3361            hasher: &mut StableHasher,
3362            error_format: ErrorOutputType,
3363            for_crate_hash: bool,
3364        ) {
3365            Hash::hash(&0, hasher);
3366            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3367            Hash::hash(&1, hasher);
3368            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3369        }
3370    }
3371
3372    impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
3373    where
3374        T1: DepTrackingHash,
3375        T2: DepTrackingHash,
3376        T3: DepTrackingHash,
3377    {
3378        fn hash(
3379            &self,
3380            hasher: &mut StableHasher,
3381            error_format: ErrorOutputType,
3382            for_crate_hash: bool,
3383        ) {
3384            Hash::hash(&0, hasher);
3385            DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
3386            Hash::hash(&1, hasher);
3387            DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
3388            Hash::hash(&2, hasher);
3389            DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
3390        }
3391    }
3392
3393    impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
3394        fn hash(
3395            &self,
3396            hasher: &mut StableHasher,
3397            error_format: ErrorOutputType,
3398            for_crate_hash: bool,
3399        ) {
3400            Hash::hash(&self.len(), hasher);
3401            for (index, elem) in self.iter().enumerate() {
3402                Hash::hash(&index, hasher);
3403                DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
3404            }
3405        }
3406    }
3407
3408    impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
3409        fn hash(
3410            &self,
3411            hasher: &mut StableHasher,
3412            error_format: ErrorOutputType,
3413            for_crate_hash: bool,
3414        ) {
3415            Hash::hash(&self.len(), hasher);
3416            for (key, value) in self.iter() {
3417                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3418                DepTrackingHash::hash(value, hasher, error_format, for_crate_hash);
3419            }
3420        }
3421    }
3422
3423    impl DepTrackingHash for OutputTypes {
3424        fn hash(
3425            &self,
3426            hasher: &mut StableHasher,
3427            error_format: ErrorOutputType,
3428            for_crate_hash: bool,
3429        ) {
3430            Hash::hash(&self.0.len(), hasher);
3431            for (key, val) in &self.0 {
3432                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
3433                if !for_crate_hash {
3434                    DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
3435                }
3436            }
3437        }
3438    }
3439
3440    // This is a stable hash because BTreeMap is a sorted container
3441    pub(crate) fn stable_hash(
3442        sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
3443        hasher: &mut StableHasher,
3444        error_format: ErrorOutputType,
3445        for_crate_hash: bool,
3446    ) {
3447        for (key, sub_hash) in sub_hashes {
3448            // Using Hash::hash() instead of DepTrackingHash::hash() is fine for
3449            // the keys, as they are just plain strings
3450            Hash::hash(&key.len(), hasher);
3451            Hash::hash(key, hasher);
3452            sub_hash.hash(hasher, error_format, for_crate_hash);
3453        }
3454    }
3455}
3456
3457/// Default behavior to use in out-of-memory situations.
3458#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
3459pub enum OomStrategy {
3460    /// Generate a panic that can be caught by `catch_unwind`.
3461    Panic,
3462
3463    /// Abort the process immediately.
3464    Abort,
3465}
3466
3467impl OomStrategy {
3468    pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic_v2";
3469
3470    pub fn should_panic(self) -> u8 {
3471        match self {
3472            OomStrategy::Panic => 1,
3473            OomStrategy::Abort => 0,
3474        }
3475    }
3476}
3477
3478/// How to run proc-macro code when building this crate
3479#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3480pub enum ProcMacroExecutionStrategy {
3481    /// Run the proc-macro code on the same thread as the server.
3482    SameThread,
3483
3484    /// Run the proc-macro code on a different thread.
3485    CrossThread,
3486}
3487
3488/// How to perform collapse macros debug info
3489/// if-ext - if macro from different crate (related to callsite code)
3490/// | cmd \ attr    | no  | (unspecified) | external | yes |
3491/// | no            | no  | no            | no       | no  |
3492/// | (unspecified) | no  | no            | if-ext   | yes |
3493/// | external      | no  | if-ext        | if-ext   | yes |
3494/// | yes           | yes | yes           | yes      | yes |
3495#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3496pub enum CollapseMacroDebuginfo {
3497    /// Don't collapse debuginfo for the macro
3498    No = 0,
3499    /// Unspecified value
3500    Unspecified = 1,
3501    /// Collapse debuginfo if the macro comes from a different crate
3502    External = 2,
3503    /// Collapse debuginfo for the macro
3504    Yes = 3,
3505}
3506
3507/// Which format to use for `-Z dump-mono-stats`
3508#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3509pub enum DumpMonoStatsFormat {
3510    /// Pretty-print a markdown table
3511    Markdown,
3512    /// Emit structured JSON
3513    Json,
3514}
3515
3516impl DumpMonoStatsFormat {
3517    pub fn extension(self) -> &'static str {
3518        match self {
3519            Self::Markdown => "md",
3520            Self::Json => "json",
3521        }
3522    }
3523}
3524
3525/// `-Z patchable-function-entry` representation - how many nops to put before and after function
3526/// entry.
3527#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3528pub struct PatchableFunctionEntry {
3529    /// Nops before the entry
3530    prefix: u8,
3531    /// Nops after the entry
3532    entry: u8,
3533}
3534
3535impl PatchableFunctionEntry {
3536    pub fn from_total_and_prefix_nops(
3537        total_nops: u8,
3538        prefix_nops: u8,
3539    ) -> Option<PatchableFunctionEntry> {
3540        if total_nops < prefix_nops {
3541            None
3542        } else {
3543            Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3544        }
3545    }
3546    pub fn prefix(&self) -> u8 {
3547        self.prefix
3548    }
3549    pub fn entry(&self) -> u8 {
3550        self.entry
3551    }
3552}
3553
3554/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
3555/// or future prototype.
3556#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3557pub enum Polonius {
3558    /// The default value: disabled.
3559    #[default]
3560    Off,
3561
3562    /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
3563    Legacy,
3564
3565    /// In-tree prototype, extending the NLL infrastructure.
3566    Next,
3567}
3568
3569impl Polonius {
3570    /// Returns whether the legacy version of polonius is enabled
3571    pub fn is_legacy_enabled(&self) -> bool {
3572        matches!(self, Polonius::Legacy)
3573    }
3574
3575    /// Returns whether the "next" version of polonius is enabled
3576    pub fn is_next_enabled(&self) -> bool {
3577        matches!(self, Polonius::Next)
3578    }
3579}
3580
3581#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3582pub enum InliningThreshold {
3583    Always,
3584    Sometimes(usize),
3585    Never,
3586}
3587
3588impl Default for InliningThreshold {
3589    fn default() -> Self {
3590        Self::Sometimes(100)
3591    }
3592}
3593
3594/// The different settings that the `-Zfunction-return` flag can have.
3595#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3596pub enum FunctionReturn {
3597    /// Keep the function return unmodified.
3598    #[default]
3599    Keep,
3600
3601    /// Replace returns with jumps to thunk, without emitting the thunk.
3602    ThunkExtern,
3603}
3604
3605/// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag.
3606/// By default, only enabled in the NLL MIR dumps, and disabled in all other passes.
3607#[derive(Clone, Copy, Default, PartialEq, Debug)]
3608pub enum MirIncludeSpans {
3609    Off,
3610    On,
3611    /// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as
3612    /// `Off` in all other cases.
3613    #[default]
3614    Nll,
3615}
3616
3617impl MirIncludeSpans {
3618    /// Unless opting into extra comments for all passes, they can be considered disabled.
3619    /// The cases where a distinction between on/off and a per-pass value can exist will be handled
3620    /// in the passes themselves: i.e. the `Nll` value is considered off for all intents and
3621    /// purposes, except for the NLL MIR dump pass.
3622    pub fn is_enabled(self) -> bool {
3623        self == MirIncludeSpans::On
3624    }
3625}