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