rustc_session/
config.rs

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