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