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