rustc_session/
config.rs

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