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