rustc_session/
config.rs

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